Merge "ImageReader/ImageWriter: unhide newInstance methods"
diff --git a/Android.bp b/Android.bp
index eb9cbbb..aac95cc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,6 +104,11 @@
         "core/java/android/app/backup/IRestoreObserver.aidl",
         "core/java/android/app/backup/IRestoreSession.aidl",
         "core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+        "core/java/android/app/contentsuggestions/IClassificationsCallback.aidl",
+        "core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl",
+        "core/java/android/app/contentsuggestions/ISelectionsCallback.aidl",
+        "core/java/android/app/prediction/IPredictionCallback.aidl",
+        "core/java/android/app/prediction/IPredictionManager.aidl",
         "core/java/android/app/role/IOnRoleHoldersChangedListener.aidl",
         "core/java/android/app/role/IRoleManager.aidl",
         "core/java/android/app/role/IRoleManagerCallback.aidl",
@@ -273,6 +278,7 @@
         "core/java/android/rolecontrollerservice/IRoleControllerService.aidl",
         ":keystore_aidl",
         "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
+        "core/java/android/service/appprediction/IPredictionService.aidl",
         "core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl",
         "core/java/android/service/autofill/augmented/IFillCallback.aidl",
         "core/java/android/service/autofill/IAutoFillService.aidl",
@@ -282,6 +288,7 @@
         "core/java/android/service/carrier/ICarrierService.aidl",
         "core/java/android/service/carrier/ICarrierMessagingCallback.aidl",
         "core/java/android/service/carrier/ICarrierMessagingService.aidl",
+        "core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl",
         "core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl",
         "core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl",
         "core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl",
@@ -298,6 +305,7 @@
         "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
         "core/java/android/service/gatekeeper/IGateKeeperService.aidl",
         "core/java/android/service/contentcapture/IContentCaptureService.aidl",
+        "core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl",
         "core/java/android/service/notification/INotificationListener.aidl",
         "core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
         "core/java/android/service/notification/IConditionListener.aidl",
@@ -350,6 +358,8 @@
         "core/java/android/service/textclassifier/ITextLanguageCallback.aidl",
         "core/java/android/service/textclassifier/ITextLinksCallback.aidl",
         "core/java/android/service/textclassifier/ITextSelectionCallback.aidl",
+        "core/java/android/service/attention/IAttentionService.aidl",
+        "core/java/android/service/attention/IAttentionCallback.aidl",
         "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
         "core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl",
         "core/java/android/view/accessibility/IAccessibilityManager.aidl",
@@ -469,6 +479,7 @@
         "media/java/android/media/IMediaRouterClient.aidl",
         "media/java/android/media/IMediaRouterService.aidl",
         "media/java/android/media/IMediaSession2.aidl",
+        "media/java/android/media/IMediaSession2Service.aidl",
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
         "media/java/android/media/IPlaybackConfigDispatcher.aidl",
@@ -495,6 +506,7 @@
         "media/java/android/media/session/IOnMediaKeyListener.aidl",
         "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
         "media/java/android/media/session/ISession.aidl",
+        "media/java/android/media/session/ISession2TokensListener.aidl",
         "media/java/android/media/session/ISessionCallback.aidl",
         "media/java/android/media/session/ISessionController.aidl",
         "media/java/android/media/session/ISessionControllerCallback.aidl",
@@ -593,7 +605,7 @@
         "telephony/java/com/android/internal/telephony/IPhoneSubInfo.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",
+        "telephony/java/com/android/internal/telephony/IOns.aidl",
         "telephony/java/com/android/internal/telephony/ITelephony.aidl",
         "telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
         "telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
@@ -731,6 +743,7 @@
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.health-V1.0-java-constants",
         "android.hardware.thermal-V1.0-java-constants",
+        "android.hardware.thermal-V1.0-java",
         "android.hardware.thermal-V1.1-java",
         "android.hardware.thermal-V2.0-java",
         "android.hardware.tv.input-V1.0-java-constants",
@@ -739,11 +752,13 @@
         "android.hardware.vibrator-V1.0-java",
         "android.hardware.vibrator-V1.1-java",
         "android.hardware.vibrator-V1.2-java",
+        "android.hardware.vibrator-V1.3-java",
         "android.hardware.wifi-V1.0-java-constants",
         "android.hardware.radio-V1.0-java",
         "android.hardware.radio-V1.3-java",
         "android.hardware.radio-V1.4-java",
         "android.hardware.usb.gadget-V1.0-java",
+        "networkstack-aidl-interfaces-java",
         "netd_aidl_interface-java",
         "devicepolicyprotosnano",
     ],
@@ -871,7 +886,21 @@
     name: "networkstack-aidl-interfaces",
     local_include_dir: "core/java",
     srcs: [
+        "core/java/android/net/INetworkMonitor.aidl",
+        "core/java/android/net/INetworkMonitorCallbacks.aidl",
+        "core/java/android/net/IIpMemoryStore.aidl",
         "core/java/android/net/INetworkStackConnector.aidl",
+        "core/java/android/net/INetworkStackStatusCallback.aidl",
+        "core/java/android/net/IpPrefixParcelable.aidl",
+        "core/java/android/net/LinkAddressParcelable.aidl",
+        "core/java/android/net/LinkPropertiesParcelable.aidl",
+        "core/java/android/net/PrivateDnsConfigParcel.aidl",
+        "core/java/android/net/ProxyInfoParcelable.aidl",
+        "core/java/android/net/RouteInfoParcelable.aidl",
+        "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
+        "core/java/android/net/dhcp/IDhcpServer.aidl",
+        "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
+        "core/java/android/net/ipmemorystore/**/*.aidl",
     ],
     api_dir: "aidl/networkstack",
 }
@@ -1047,6 +1076,7 @@
         "core/java/android/annotation/IntDef.java",
         "core/java/android/annotation/NonNull.java",
         "core/java/android/annotation/SystemApi.java",
+        "core/java/android/annotation/TestApi.java",
         "core/java/android/os/HwBinder.java",
         "core/java/android/os/HwBlob.java",
         "core/java/android/os/HwParcel.java",
@@ -1296,7 +1326,6 @@
         "ext",
         "framework",
         "voip-common",
-        "android.test.mock.impl",
     ],
     local_sourcepaths: frameworks_base_subdirs,
     installable: false,
diff --git a/Android.mk b/Android.mk
index e3cc275..9f7bf99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -80,6 +80,8 @@
 # ==== hiddenapi lists =======================================
 .KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
 $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
+    PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS)
+$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
     frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
     frameworks/base/config/hiddenapi-greylist.txt \
     frameworks/base/config/hiddenapi-greylist-max-p.txt \
@@ -87,7 +89,8 @@
     frameworks/base/config/hiddenapi-force-blacklist.txt \
     $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
     $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
-    $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
+    $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
+    $(SOONG_HIDDENAPI_FLAGS)
 	frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
 	    --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
 	    --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index d01e183..30c2c69 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -252,6 +252,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 77bacbe..3f87a1c 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -9,7 +9,7 @@
   src/android/os/ISomeService.aidl
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     androidx.annotation_annotation \
     apct-perftests-utils \
     guava
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 13c24d9..a564a4d 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -28,7 +28,7 @@
 
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.perftests.core"/>
 
 </manifest>
diff --git a/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java b/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java
index b9411fa..e455e6a 100644
--- a/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java
@@ -23,10 +23,10 @@
 import android.content.pm.PackageManager;
 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.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
index f8fd51d..b3f8359 100644
--- a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
@@ -21,9 +21,10 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -33,7 +34,6 @@
 // Due to b/71353150, you might get "java.lang.AssertionError: Binder ProxyMap has too many
 // entries", but it's flaky. Adding "Runtime.getRuntime().gc()" between each iteration solves
 // the problem, but it doesn't seem like it's currently needed.
-
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class PendingIntentPerfTest {
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
index 9cdeb48..c3e43ee 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
@@ -23,7 +23,8 @@
 import android.content.res.XmlResourceParser;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index bb0627e..1b07572 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -19,8 +19,9 @@
 import android.content.res.Resources;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java b/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java
index 897d0ae..c5ef80d 100644
--- a/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java
+++ b/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java
@@ -23,9 +23,10 @@
 import android.database.sqlite.SQLiteDatabase;
 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 androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
diff --git a/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java b/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java
index 7c5316d..830302e 100644
--- a/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java
+++ b/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java
@@ -16,17 +16,20 @@
 
 package android.database;
 
+import static org.junit.Assert.assertEquals;
+
 import android.app.Activity;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.Preconditions;
 
 import org.junit.After;
@@ -40,8 +43,6 @@
 import java.util.List;
 import java.util.Map;
 
-import static org.junit.Assert.assertEquals;
-
 /**
  * Performance tests for measuring amount of data written during typical DB operations
  *
diff --git a/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java b/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
index e2b75c3..973e996 100644
--- a/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
+++ b/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
@@ -16,14 +16,18 @@
 
 package android.database;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
 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 androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -33,9 +37,6 @@
 
 import java.util.Random;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 /**
  * Performance tests for typical CRUD operations and loading rows into the Cursor
  *
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 c742df3..9f09305 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -24,7 +24,8 @@
 import android.graphics.RenderNode;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Rule;
 import org.junit.Test;
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 f9c3758..10a5128 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
@@ -19,7 +19,8 @@
 import android.graphics.Outline;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
index 26b8309..3a80020 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
@@ -20,17 +20,18 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
 
-import java.util.Arrays;
-import java.util.Collection;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized.Parameters;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
 
 @LargeTest
 @RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java
index b9ee613..fc8c673 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java
@@ -19,9 +19,10 @@
 import android.graphics.Canvas;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
 import android.text.TextPaint;
 
+import androidx.test.filters.LargeTest;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
index 7a49b4f..c6de9ec 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
@@ -20,7 +20,8 @@
 import android.graphics.RectF;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 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 62dd124..e7fe235 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -20,7 +20,8 @@
 import android.graphics.RenderNode;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index 11ee599..d6e8ab2 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -21,9 +21,14 @@
 import android.graphics.Typeface;
 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 androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -31,10 +36,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TypefaceCreatePerfTest {
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
index 5533782..3b2b8a9 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
@@ -16,6 +16,8 @@
 
 package android.graphics.perftests;
 
+import static junit.framework.Assert.assertTrue;
+
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -25,21 +27,17 @@
 import android.perftests.utils.BitmapUtils;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.perftests.core.R;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.IOException;
-
-import static junit.framework.Assert.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VectorDrawablePerfTest {
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 99e4ba1..12e49e3 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -18,8 +18,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal.CallSession;
@@ -31,7 +32,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-
 /**
  * Performance tests for {@link BinderCallsStats}
  */
diff --git a/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java b/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java
index 4961b4f..0d7b7ca 100644
--- a/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java
@@ -18,8 +18,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -28,7 +29,6 @@
 import java.nio.file.Files;
 import java.nio.file.Paths;
 
-
 /**
  * Performance tests collecting CPU data different mechanisms.
  */
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
index 9034034..11c7599 100644
--- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -20,8 +20,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.KernelCpuThreadReader;
 
@@ -29,7 +30,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-
 /**
  * Performance tests collecting per-thread CPU data.
  */
diff --git a/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
index 0f880b7..162167d 100644
--- a/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
@@ -19,8 +19,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.CachedDeviceState;
 import com.android.internal.os.LooperStats;
@@ -31,7 +32,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-
 /**
  * Performance tests for {@link LooperStats}.
  */
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
index 145fbcd..3aa6749 100644
--- a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
@@ -16,20 +16,16 @@
 
 package android.os;
 
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 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 org.junit.Assert;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
index a67aeca..af6d6b0 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
@@ -18,7 +18,8 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
index 6e4c9c5..4db9262 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
@@ -16,10 +16,15 @@
 
 package android.os;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -27,10 +32,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ParcelPerfTest {
diff --git a/apct-tests/perftests/core/src/android/os/PssPerfTest.java b/apct-tests/perftests/core/src/android/os/PssPerfTest.java
index 400115d..2cc294f 100644
--- a/apct-tests/perftests/core/src/android/os/PssPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/PssPerfTest.java
@@ -18,8 +18,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java b/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java
index 099134f..dd479ac 100644
--- a/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java
+++ b/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java
@@ -20,9 +20,10 @@
 import android.content.SharedPreferences;
 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 androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/os/StrictModeTest.java b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
index d973c20..60678e9 100644
--- a/apct-tests/perftests/core/src/android/os/StrictModeTest.java
+++ b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
@@ -23,17 +23,21 @@
 import android.net.Uri;
 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 androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.google.common.util.concurrent.SettableFuture;
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StrictModeTest {
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 8e5cfaa..0d64c39 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -20,7 +20,8 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.ShellHelper;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.AfterClass;
 import org.junit.Assert;
diff --git a/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
index 95a7144..4f0d108 100644
--- a/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
+++ b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
@@ -18,8 +18,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import dalvik.annotation.optimization.FastNative;
 
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
index 9245c1b..c8121c5 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -22,9 +22,10 @@
 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 androidx.test.filters.LargeTest;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
index 194a88c..ad1327c 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
@@ -18,9 +18,10 @@
 import android.graphics.Canvas;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
 import android.text.NonEditableTextGenerator.TextType;
 
+import androidx.test.filters.LargeTest;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
index ad9fb5f..bb6b691 100644
--- a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
+++ b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
@@ -19,8 +19,9 @@
 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 androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
index b4c7f54..5be99d9 100644
--- a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
@@ -24,11 +24,12 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
 import android.text.style.ReplacementSpan;
 import android.util.ArraySet;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
index a7972f5..bbe75b7 100644
--- a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -21,7 +21,8 @@
 import android.graphics.RenderNode;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
index 00a6267..4ae2b93 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
@@ -18,9 +18,10 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
index 33b1a47..3be9114 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
@@ -18,8 +18,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
index b40dd6b..6c92229 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -22,9 +22,10 @@
 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 androidx.test.filters.LargeTest;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java
index 2768a7d..47e04ef 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java
@@ -19,8 +19,8 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
index 60c6d89..0b79834 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
@@ -19,17 +19,16 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Random;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index f60cbee..bd7522d 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -21,8 +21,9 @@
 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 androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
index 25cc707..10bfa42 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -23,11 +23,12 @@
 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.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
index a482c4a..a7a81f2 100644
--- a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
+++ b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
@@ -18,13 +18,14 @@
 import android.content.Context;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLanguage;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
index 0c1f289..b24bf42 100644
--- a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
@@ -18,8 +18,9 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java b/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java
index 07cd33f..26cec95 100644
--- a/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java
+++ b/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java
@@ -18,11 +18,11 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
index 990be24..a1f8608 100644
--- a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
@@ -17,13 +17,13 @@
 package android.view;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
 import android.widget.FrameLayout;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import com.android.perftests.core.R;
 
 import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index dc4d4bd..b34001d 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -24,14 +24,15 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
 import android.view.View.MeasureSpec;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
index d219d3a..b3ea62a 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
@@ -19,14 +19,15 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
 import android.text.Selection;
 import android.view.KeyEvent;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
index b6cf7d3..aa47d5b 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
@@ -19,14 +19,15 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
 import android.text.Selection;
 import android.view.KeyEvent;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
index ce0c357..e50016c 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
@@ -16,10 +16,16 @@
 
 package android.widget;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Locale;
-import java.util.Random;
+import android.app.Activity;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.StubActivity;
+import android.view.KeyEvent;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -27,22 +33,9 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.RenderNodeAnimator;
-import android.view.ViewGroup;
-import android.view.View.MeasureSpec;
-
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Random;
 
 @LargeTest
 @RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
index d570ef3..644095b 100644
--- a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
@@ -16,17 +16,26 @@
 
 package android.widget;
 
+import static android.perftests.utils.LayoutUtils.gatherViewTree;
+import static android.perftests.utils.LayoutUtils.requestLayoutForAllNodes;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.os.Looper;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
 import com.android.perftests.core.R;
 
 import org.junit.Rule;
@@ -38,13 +47,6 @@
 import java.util.Collection;
 import java.util.List;
 
-import static android.perftests.utils.LayoutUtils.gatherViewTree;
-import static android.perftests.utils.LayoutUtils.requestLayoutForAllNodes;
-import static android.view.View.MeasureSpec.AT_MOST;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-import static org.junit.Assert.assertTrue;
-
 @LargeTest
 @RunWith(Parameterized.class)
 public class LayoutPerfTest {
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
index c310166..bed173b 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
@@ -16,33 +16,27 @@
 
 package android.widget;
 
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.os.Looper;
-import android.os.Bundle;
-import android.perftests.utils.PerfStatusReporter;
-import android.util.Log;
-import android.view.View;
-
 import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.perftests.core.R;
 
-import java.util.Locale;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
 import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
-import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import java.util.Collection;
 
 @LargeTest
 @RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java
index 4b6da6b..1f00838 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java
@@ -19,23 +19,21 @@
 import android.content.Context;
 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.view.LayoutInflater;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import com.android.perftests.core.R;
 
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
 import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
-import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import java.util.Collection;
 
 @LargeTest
 @RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
index a14dd25..88acbba 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
@@ -25,24 +25,20 @@
 import android.graphics.Typeface;
 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.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.TextAppearanceSpan;
-import android.view.LayoutInflater;
 
-import com.android.perftests.core.R;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
-import java.util.Random;
-import java.util.Locale;
-
-import org.junit.Test;
 import org.junit.Rule;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
+import java.util.Locale;
+import java.util.Random;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
index bd91112..0bc9ee4 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
@@ -24,9 +24,6 @@
 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.BoringLayout;
 import android.text.Layout;
 import android.text.PrecomputedText;
@@ -34,6 +31,10 @@
 import android.text.TextPerfUtils;
 import android.view.View.MeasureSpec;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
index e95676b..00bd8db 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
@@ -19,8 +19,9 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/api/current.txt b/api/current.txt
index 3caed22..e7c9b24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5245,8 +5245,8 @@
     method public android.app.Notification clone();
     method public int describeContents();
     method public boolean getAllowSystemGeneratedContextualActions();
-    method public android.app.PendingIntent getAppOverlayIntent();
     method public int getBadgeIconType();
+    method public android.app.Notification.BubbleMetadata getBubbleMetadata();
     method public java.lang.String getChannelId();
     method public java.lang.String getGroup();
     method public int getGroupAlertBehavior();
@@ -5385,11 +5385,11 @@
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
     method public int getSemanticAction();
+    method public boolean isContextual();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
     field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
     field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
-    field public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11; // 0xb
     field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
     field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
     field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
@@ -5414,6 +5414,7 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
     method public android.os.Bundle getExtras();
     method public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean);
+    method public android.app.Notification.Action.Builder setContextual(boolean);
     method public android.app.Notification.Action.Builder setSemanticAction(int);
   }
 
@@ -5458,6 +5459,25 @@
     method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
   }
 
+  public static final class Notification.BubbleMetadata implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDesiredHeight();
+    method public android.graphics.drawable.Icon getIcon();
+    method public android.app.PendingIntent getIntent();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR;
+  }
+
+  public static class Notification.BubbleMetadata.Builder {
+    ctor public Notification.BubbleMetadata.Builder();
+    method public android.app.Notification.BubbleMetadata build();
+    method public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int);
+    method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon);
+    method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public android.app.Notification.BubbleMetadata.Builder setTitle(java.lang.CharSequence);
+  }
+
   public static class Notification.Builder {
     ctor public Notification.Builder(android.content.Context, java.lang.String);
     ctor public deprecated Notification.Builder(android.content.Context);
@@ -5477,9 +5497,9 @@
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
-    method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setBadgeIconType(int);
+    method public android.app.Notification.Builder setBubbleMetadata(android.app.Notification.BubbleMetadata);
     method public android.app.Notification.Builder setCategory(java.lang.String);
     method public android.app.Notification.Builder setChannelId(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
@@ -5694,8 +5714,8 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+    method public boolean canBubble();
     method public boolean canBypassDnd();
-    method public boolean canOverlayApps();
     method public boolean canShowBadge();
     method public int describeContents();
     method public void enableLights(boolean);
@@ -5711,7 +5731,7 @@
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
-    method public void setAllowAppOverlay(boolean);
+    method public void setAllowBubbles(boolean);
     method public void setBypassDnd(boolean);
     method public void setDescription(java.lang.String);
     method public void setGroup(java.lang.String);
@@ -5745,7 +5765,7 @@
 
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
-    method public boolean areAppOverlaysAllowed();
+    method public boolean areBubblesAllowed();
     method public boolean areNotificationsEnabled();
     method public boolean canNotifyAsPackage(java.lang.String);
     method public void cancel(int);
@@ -6764,9 +6784,11 @@
     method public void wipeData(int);
     method public void wipeData(int, java.lang.CharSequence);
     field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+    field public static final java.lang.String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE";
     field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
     field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
+    field public static final java.lang.String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED";
     field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
@@ -6809,12 +6831,15 @@
     field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
     field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
     field public static final deprecated java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
+    field public static final java.lang.String EXTRA_PROVISIONING_IMEI = "android.app.extra.PROVISIONING_IMEI";
     field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
     field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
     field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
     field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
     field public static final java.lang.String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
     field public static final java.lang.String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
+    field public static final java.lang.String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
+    field public static final java.lang.String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
     field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
     field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
     field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
@@ -6891,6 +6916,9 @@
     field public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2; // 0x2
     field public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1; // 0x1
     field public static final int PRIVATE_DNS_SET_SUCCESS = 0; // 0x0
+    field public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; // 0x1
+    field public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; // 0x2
+    field public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 3; // 0x3
     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
@@ -7378,7 +7406,9 @@
     method public boolean isRoleHeld(java.lang.String);
     field public static final java.lang.String ROLE_BROWSER = "android.app.role.BROWSER";
     field public static final java.lang.String ROLE_DIALER = "android.app.role.DIALER";
+    field public static final java.lang.String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
     field public static final java.lang.String ROLE_GALLERY = "android.app.role.GALLERY";
+    field public static final java.lang.String ROLE_HOME = "android.app.role.HOME";
     field public static final java.lang.String ROLE_MUSIC = "android.app.role.MUSIC";
     field public static final java.lang.String ROLE_SMS = "android.app.role.SMS";
   }
@@ -7660,6 +7690,7 @@
     field public static final int ACTIVITY_RESUMED = 1; // 0x1
     field public static final int ACTIVITY_STOPPED = 23; // 0x17
     field public static final int CONFIGURATION_CHANGE = 5; // 0x5
+    field public static final int DEVICE_SHUTDOWN = 26; // 0x1a
     field public static final int FOREGROUND_SERVICE_START = 19; // 0x13
     field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14
     field public static final int KEYGUARD_HIDDEN = 18; // 0x12
@@ -8531,42 +8562,44 @@
     field public static final java.lang.String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid";
   }
 
-  public final class BluetoothHealth implements android.bluetooth.BluetoothProfile {
-    method public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
-    method public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
+  public final deprecated class BluetoothHealth implements android.bluetooth.BluetoothProfile {
+    ctor public BluetoothHealth();
+    method public deprecated boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+    method public deprecated boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
     method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method public int getConnectionState(android.bluetooth.BluetoothDevice);
     method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
-    method public android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
-    method public boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
-    method public boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
-    field public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
-    field public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
-    field public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
-    field public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
-    field public static final int CHANNEL_TYPE_RELIABLE = 10; // 0xa
-    field public static final int CHANNEL_TYPE_STREAMING = 11; // 0xb
-    field public static final int SINK_ROLE = 2; // 0x2
-    field public static final int SOURCE_ROLE = 1; // 0x1
-    field public static final int STATE_CHANNEL_CONNECTED = 2; // 0x2
-    field public static final int STATE_CHANNEL_CONNECTING = 1; // 0x1
-    field public static final int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
-    field public static final int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
+    method public deprecated android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+    method public deprecated boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
+    method public deprecated boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
+    field public static final deprecated int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
+    field public static final deprecated int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
+    field public static final deprecated int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
+    field public static final deprecated int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
+    field public static final deprecated int CHANNEL_TYPE_RELIABLE = 10; // 0xa
+    field public static final deprecated int CHANNEL_TYPE_STREAMING = 11; // 0xb
+    field public static final deprecated int SINK_ROLE = 2; // 0x2
+    field public static final deprecated int SOURCE_ROLE = 1; // 0x1
+    field public static final deprecated int STATE_CHANNEL_CONNECTED = 2; // 0x2
+    field public static final deprecated int STATE_CHANNEL_CONNECTING = 1; // 0x1
+    field public static final deprecated int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
+    field public static final deprecated int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
   }
 
-  public final class BluetoothHealthAppConfiguration implements android.os.Parcelable {
+  public final deprecated class BluetoothHealthAppConfiguration implements android.os.Parcelable {
+    ctor public BluetoothHealthAppConfiguration();
     method public int describeContents();
-    method public int getDataType();
-    method public java.lang.String getName();
-    method public int getRole();
+    method public deprecated int getDataType();
+    method public deprecated java.lang.String getName();
+    method public deprecated int getRole();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHealthAppConfiguration> CREATOR;
+    field public static final deprecated android.os.Parcelable.Creator<android.bluetooth.BluetoothHealthAppConfiguration> CREATOR;
   }
 
-  public abstract class BluetoothHealthCallback {
+  public abstract deprecated class BluetoothHealthCallback {
     ctor public BluetoothHealthCallback();
-    method public void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
-    method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
+    method public deprecated void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
+    method public deprecated void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
   }
 
   public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
@@ -8663,7 +8696,7 @@
     field public static final int GATT = 7; // 0x7
     field public static final int GATT_SERVER = 8; // 0x8
     field public static final int HEADSET = 1; // 0x1
-    field public static final int HEALTH = 3; // 0x3
+    field public static final deprecated int HEALTH = 3; // 0x3
     field public static final int HID_DEVICE = 19; // 0x13
     field public static final int SAP = 10; // 0xa
     field public static final int STATE_CONNECTED = 2; // 0x2
@@ -10199,6 +10232,7 @@
     field public static final java.lang.String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
     field public static final java.lang.String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
     field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE";
+    field public static final java.lang.String ACTION_PERMISSION_USAGE_DETAILS = "android.intent.action.PERMISSION_USAGE_DETAILS";
     field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK";
     field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
     field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
@@ -10323,6 +10357,7 @@
     field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_PERMISSION_USAGE_PERMISSIONS = "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
     field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
     field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -10340,6 +10375,7 @@
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
     field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
     field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
+    field public static final java.lang.String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
     field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
     field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
@@ -11184,6 +11220,7 @@
     method public void registerCallback(android.content.pm.LauncherApps.Callback);
     method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
+    method public boolean shouldHideFromSuggestions(java.lang.String, android.os.UserHandle);
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
@@ -11914,6 +11951,9 @@
     method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
     method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]);
     method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
+    method public android.content.pm.ShortcutInfo.Builder setLongLived();
+    method public android.content.pm.ShortcutInfo.Builder setPerson(android.app.Person);
+    method public android.content.pm.ShortcutInfo.Builder setPersons(android.app.Person[]);
     method public android.content.pm.ShortcutInfo.Builder setRank(int);
     method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
   }
@@ -12254,6 +12294,7 @@
     method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
     method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
+    field public static final int ID_NULL = 0; // 0x0
   }
 
   public static class Resources.NotFoundException extends java.lang.RuntimeException {
@@ -12297,6 +12338,7 @@
     method public java.lang.String getPositionDescription();
     method public int getResourceId(int, int);
     method public android.content.res.Resources getResources();
+    method public int getSourceStyleResourceId(int, int);
     method public java.lang.String getString(int);
     method public java.lang.CharSequence getText(int);
     method public java.lang.CharSequence[] getTextArray(int);
@@ -13729,6 +13771,7 @@
     method public void drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawTextRun(java.lang.CharSequence, int, int, int, int, float, float, boolean, android.graphics.Paint);
+    method public void drawTextRun(android.graphics.text.MeasuredText, int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint);
     method public void enableZ();
     method public boolean getClipBounds(android.graphics.Rect);
@@ -16429,9 +16472,9 @@
 
   public class BiometricManager {
     method public int canAuthenticate();
-    field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
+    field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
+    field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
-    field public static final int BIOMETRIC_ERROR_UNAVAILABLE = 1; // 0x1
     field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
   }
 
@@ -16474,6 +16517,7 @@
     method public android.hardware.biometrics.BiometricPrompt build();
     method public android.hardware.biometrics.BiometricPrompt.Builder setDescription(java.lang.CharSequence);
     method public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener);
+    method public android.hardware.biometrics.BiometricPrompt.Builder setRequireConfirmation(boolean);
     method public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(java.lang.CharSequence);
     method public android.hardware.biometrics.BiometricPrompt.Builder setTitle(java.lang.CharSequence);
   }
@@ -22655,6 +22699,7 @@
     method public deprecated double getCarrierPhase();
     method public deprecated double getCarrierPhaseUncertainty();
     method public double getCn0DbHz();
+    method public int getCodeType();
     method public int getConstellationType();
     method public int getMultipathIndicator();
     method public double getPseudorangeRateMetersPerSecond();
@@ -22670,6 +22715,7 @@
     method public boolean hasCarrierFrequencyHz();
     method public deprecated boolean hasCarrierPhase();
     method public deprecated boolean hasCarrierPhaseUncertainty();
+    method public boolean hasCodeType();
     method public boolean hasSnrInDb();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
@@ -22678,6 +22724,21 @@
     field public static final int ADR_STATE_RESET = 2; // 0x2
     field public static final int ADR_STATE_UNKNOWN = 0; // 0x0
     field public static final int ADR_STATE_VALID = 1; // 0x1
+    field public static final int CODE_TYPE_A = 0; // 0x0
+    field public static final int CODE_TYPE_B = 1; // 0x1
+    field public static final int CODE_TYPE_C = 2; // 0x2
+    field public static final int CODE_TYPE_CODELESS = 13; // 0xd
+    field public static final int CODE_TYPE_I = 3; // 0x3
+    field public static final int CODE_TYPE_L = 4; // 0x4
+    field public static final int CODE_TYPE_M = 5; // 0x5
+    field public static final int CODE_TYPE_P = 6; // 0x6
+    field public static final int CODE_TYPE_Q = 7; // 0x7
+    field public static final int CODE_TYPE_S = 8; // 0x8
+    field public static final int CODE_TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final int CODE_TYPE_W = 9; // 0x9
+    field public static final int CODE_TYPE_X = 10; // 0xa
+    field public static final int CODE_TYPE_Y = 11; // 0xb
+    field public static final int CODE_TYPE_Z = 12; // 0xc
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurement> CREATOR;
     field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
     field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2
@@ -24092,6 +24153,7 @@
     method public int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
     method protected void finalize();
     method public void flush();
+    method public java.lang.String getCanonicalName();
     method public android.media.MediaCodecInfo getCodecInfo();
     method public java.nio.ByteBuffer getInputBuffer(int);
     method public deprecated java.nio.ByteBuffer[] getInputBuffers();
@@ -24170,8 +24232,11 @@
   public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
     ctor public MediaCodec.CryptoException(int, java.lang.String);
     method public int getErrorCode();
+    field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
     field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
+    field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
     field public static final int ERROR_KEY_EXPIRED = 2; // 0x2
+    field public static final int ERROR_LOST_STATE = 9; // 0x9
     field public static final int ERROR_NO_KEY = 1; // 0x1
     field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
     field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
@@ -24215,10 +24280,15 @@
   }
 
   public final class MediaCodecInfo {
+    method public java.lang.String getCanonicalName();
     method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
     method public java.lang.String getName();
     method public java.lang.String[] getSupportedTypes();
+    method public boolean isAlias();
     method public boolean isEncoder();
+    method public boolean isHardwareAccelerated();
+    method public boolean isSoftwareOnly();
+    method public boolean isVendor();
   }
 
   public static final class MediaCodecInfo.AudioCapabilities {
@@ -24294,7 +24364,10 @@
     field public static final deprecated int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
     field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
     field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
+    field public static final java.lang.String FEATURE_DynamicTimestamp = "dynamic-timestamp";
+    field public static final java.lang.String FEATURE_FrameParsing = "frame-parsing";
     field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh";
+    field public static final java.lang.String FEATURE_MultipleFrames = "multiple-frames";
     field public static final java.lang.String FEATURE_PartialFrame = "partial-frame";
     field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback";
     field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback";
@@ -24316,6 +24389,33 @@
     field public static final int AACObjectSSR = 3; // 0x3
     field public static final int AACObjectScalable = 6; // 0x6
     field public static final int AACObjectXHE = 42; // 0x2a
+    field public static final int AV1Level2 = 1; // 0x1
+    field public static final int AV1Level21 = 2; // 0x2
+    field public static final int AV1Level22 = 4; // 0x4
+    field public static final int AV1Level23 = 8; // 0x8
+    field public static final int AV1Level3 = 16; // 0x10
+    field public static final int AV1Level31 = 32; // 0x20
+    field public static final int AV1Level32 = 64; // 0x40
+    field public static final int AV1Level33 = 128; // 0x80
+    field public static final int AV1Level4 = 256; // 0x100
+    field public static final int AV1Level41 = 512; // 0x200
+    field public static final int AV1Level42 = 1024; // 0x400
+    field public static final int AV1Level43 = 2048; // 0x800
+    field public static final int AV1Level5 = 4096; // 0x1000
+    field public static final int AV1Level51 = 8192; // 0x2000
+    field public static final int AV1Level52 = 16384; // 0x4000
+    field public static final int AV1Level53 = 32768; // 0x8000
+    field public static final int AV1Level6 = 65536; // 0x10000
+    field public static final int AV1Level61 = 131072; // 0x20000
+    field public static final int AV1Level62 = 262144; // 0x40000
+    field public static final int AV1Level63 = 524288; // 0x80000
+    field public static final int AV1Level7 = 1048576; // 0x100000
+    field public static final int AV1Level71 = 2097152; // 0x200000
+    field public static final int AV1Level72 = 4194304; // 0x400000
+    field public static final int AV1Level73 = 8388608; // 0x800000
+    field public static final int AV1Profile0 = 1; // 0x1
+    field public static final int AV1Profile1 = 2; // 0x2
+    field public static final int AV1Profile2 = 4; // 0x4
     field public static final int AVCLevel1 = 1; // 0x1
     field public static final int AVCLevel11 = 4; // 0x4
     field public static final int AVCLevel12 = 8; // 0x8
@@ -24333,6 +24433,9 @@
     field public static final int AVCLevel5 = 16384; // 0x4000
     field public static final int AVCLevel51 = 32768; // 0x8000
     field public static final int AVCLevel52 = 65536; // 0x10000
+    field public static final int AVCLevel6 = 131072; // 0x20000
+    field public static final int AVCLevel61 = 262144; // 0x40000
+    field public static final int AVCLevel62 = 524288; // 0x80000
     field public static final int AVCProfileBaseline = 1; // 0x1
     field public static final int AVCProfileConstrainedBaseline = 65536; // 0x10000
     field public static final int AVCProfileConstrainedHigh = 524288; // 0x80000
@@ -24495,12 +24598,53 @@
     method public android.util.Range<java.lang.Double> getSupportedFrameRatesFor(int, int);
     method public android.util.Range<java.lang.Integer> getSupportedHeights();
     method public android.util.Range<java.lang.Integer> getSupportedHeightsFor(int);
+    method public java.util.List<android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint> getSupportedPerformancePoints();
     method public android.util.Range<java.lang.Integer> getSupportedWidths();
     method public android.util.Range<java.lang.Integer> getSupportedWidthsFor(int);
     method public int getWidthAlignment();
     method public boolean isSizeSupported(int, int);
   }
 
+  public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint {
+    method public boolean covers(android.media.MediaFormat);
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_100;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_120;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_200;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_24;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_240;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_25;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_30;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_50;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint FHD_60;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_100;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_120;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_200;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_24;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_240;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_25;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_30;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_50;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint HD_60;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_24;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_25;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_30;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_48;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_50;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint SD_60;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_100;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_120;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_200;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_24;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_240;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_25;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_30;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_50;
+    field public static final android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint UHD_60;
+    field public final int frameRate;
+    field public final int height;
+    field public final int width;
+  }
+
   public final class MediaCodecList {
     ctor public MediaCodecList(int);
     method public java.lang.String findDecoderForFormat(android.media.MediaFormat);
@@ -24512,6 +24656,24 @@
     field public static final int REGULAR_CODECS = 0; // 0x0
   }
 
+  public class MediaController2 implements java.lang.AutoCloseable {
+    ctor public MediaController2(android.content.Context, android.media.Session2Token);
+    ctor public MediaController2(android.content.Context, android.media.Session2Token, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
+    method public void cancelSessionCommand(java.lang.Object);
+    method public void close();
+    method public boolean isPlaybackActive();
+    method public java.lang.Object sendSessionCommand(android.media.Session2Command, android.os.Bundle);
+  }
+
+  public static abstract class MediaController2.ControllerCallback {
+    ctor public MediaController2.ControllerCallback();
+    method public void onCommandResult(android.media.MediaController2, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result);
+    method public void onConnected(android.media.MediaController2, android.media.Session2CommandGroup);
+    method public void onDisconnected(android.media.MediaController2);
+    method public void onPlaybackActiveChanged(android.media.MediaController2, boolean);
+    method public android.media.Session2Command.Result onSessionCommand(android.media.MediaController2, android.media.Session2Command, android.os.Bundle);
+  }
+
   public final class MediaCrypto {
     ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
     method protected void finalize();
@@ -24619,6 +24781,7 @@
     method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
     method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler);
     method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
+    method public void setOnSessionLostStateListener(android.media.MediaDrm.OnSessionLostStateListener, android.os.Handler);
     method public void setPropertyByteArray(java.lang.String, byte[]);
     method public void setPropertyString(java.lang.String, java.lang.String);
     field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
@@ -24737,6 +24900,10 @@
     method public abstract void onKeyStatusChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean);
   }
 
+  public static abstract interface MediaDrm.OnSessionLostStateListener {
+    method public abstract void onSessionLostState(android.media.MediaDrm, byte[]);
+  }
+
   public static final class MediaDrm.ProvisionRequest {
     method public byte[] getData();
     method public java.lang.String getDefaultUrl();
@@ -24745,6 +24912,12 @@
   public static abstract class MediaDrm.SecurityLevel implements java.lang.annotation.Annotation {
   }
 
+  public static final class MediaDrm.SessionException extends java.lang.RuntimeException {
+    ctor public MediaDrm.SessionException(int, java.lang.String);
+    method public int getErrorCode();
+    field public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
+  }
+
   public class MediaDrmException extends java.lang.Exception {
     ctor public MediaDrmException(java.lang.String);
   }
@@ -24926,6 +25099,7 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
     field public static final java.lang.String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
+    field public static final java.lang.String MIMETYPE_VIDEO_AV1 = "video/av01";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
     field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
@@ -24944,6 +25118,25 @@
     field public static final int TYPE_STRING = 4; // 0x4
   }
 
+  public final class MediaItem2 implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getEndPosition();
+    method public android.media.MediaMetadata getMetadata();
+    method public long getStartPosition();
+    method public void setMetadata(android.media.MediaMetadata);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.MediaItem2> CREATOR;
+    field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static class MediaItem2.Builder {
+    ctor public MediaItem2.Builder();
+    method public android.media.MediaItem2 build();
+    method public android.media.MediaItem2.Builder setEndPosition(long);
+    method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata);
+    method public android.media.MediaItem2.Builder setStartPosition(long);
+  }
+
   public final class MediaMetadata implements android.os.Parcelable {
     method public boolean containsKey(java.lang.String);
     method public int describeContents();
@@ -25342,12 +25535,15 @@
     method public java.lang.Object clearNextDataSources();
     method public void clearPendingCommands();
     method public void close();
+    method public java.lang.Object deselectTrack(int);
     method public java.lang.Object deselectTrack(android.media.DataSourceDesc, int);
     method public android.media.AudioAttributes getAudioAttributes();
     method public int getAudioSessionId();
+    method public long getBufferedPosition();
     method public long getBufferedPosition(android.media.DataSourceDesc);
     method public android.media.DataSourceDesc getCurrentDataSource();
     method public long getCurrentPosition();
+    method public long getDuration();
     method public long getDuration(android.media.DataSourceDesc);
     method public float getMaxPlayerVolume();
     method public android.os.PersistableBundle getMetrics();
@@ -25355,10 +25551,12 @@
     method public float getPlayerVolume();
     method public android.media.AudioDeviceInfo getPreferredDevice();
     method public android.media.AudioDeviceInfo getRoutedDevice();
+    method public int getSelectedTrack(int);
     method public int getSelectedTrack(android.media.DataSourceDesc, int);
     method public int getState();
     method public android.media.SyncParams getSyncParams();
     method public android.media.MediaTimestamp getTimestamp();
+    method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
     method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(android.media.DataSourceDesc);
     method public android.media.VideoSize getVideoSize();
     method public boolean isLooping();
@@ -25372,12 +25570,14 @@
     method public void reset();
     method public java.lang.Object seekTo(long);
     method public java.lang.Object seekTo(long, int);
+    method public java.lang.Object selectTrack(int);
     method public java.lang.Object selectTrack(android.media.DataSourceDesc, int);
     method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
     method public java.lang.Object setAudioSessionId(int);
     method public java.lang.Object setAuxEffectSendLevel(float);
     method public java.lang.Object setDataSource(android.media.DataSourceDesc);
     method public java.lang.Object setDisplay(android.view.SurfaceHolder);
+    method public void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
     method public java.lang.Object setNextDataSource(android.media.DataSourceDesc);
     method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>);
     method public java.lang.Object setPlaybackParams(android.media.PlaybackParams);
@@ -25456,6 +25656,33 @@
     field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public static class MediaPlayer2.DrmEventCallback {
+    ctor public MediaPlayer2.DrmEventCallback();
+    method public void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm);
+    method public android.media.MediaPlayer2.DrmPreparationInfo onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo);
+    method public byte[] onDrmKeyRequest(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm.KeyRequest);
+    method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int, byte[]);
+  }
+
+  public static final class MediaPlayer2.DrmInfo {
+    method public java.util.Map<java.util.UUID, byte[]> getPssh();
+    method public java.util.List<java.util.UUID> getSupportedSchemes();
+  }
+
+  public static final class MediaPlayer2.DrmPreparationInfo {
+  }
+
+  public static final class MediaPlayer2.DrmPreparationInfo.Builder {
+    ctor public MediaPlayer2.DrmPreparationInfo.Builder();
+    method public android.media.MediaPlayer2.DrmPreparationInfo build();
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setInitData(byte[]);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeySetId(byte[]);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeyType(int);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setMimeType(java.lang.String);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setOptionalParameters(java.util.Map<java.lang.String, java.lang.String>);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setUuid(java.util.UUID);
+  }
+
   public static class MediaPlayer2.EventCallback {
     ctor public MediaPlayer2.EventCallback();
     method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
@@ -25483,6 +25710,10 @@
     field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
   }
 
+  public static final class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException {
+    ctor public MediaPlayer2.NoDrmSchemeException(java.lang.String);
+  }
+
   public static class MediaPlayer2.TrackInfo {
     method public android.media.MediaFormat getFormat();
     method public java.lang.String getLanguage();
@@ -25574,6 +25805,7 @@
     field public static final int VOICE_CALL = 4; // 0x4
     field public static final int VOICE_COMMUNICATION = 7; // 0x7
     field public static final int VOICE_DOWNLINK = 3; // 0x3
+    field public static final int VOICE_PERFORMANCE = 10; // 0xa
     field public static final int VOICE_RECOGNITION = 6; // 0x6
     field public static final int VOICE_UPLINK = 2; // 0x2
   }
@@ -25775,6 +26007,39 @@
     method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
   }
 
+  public class MediaSession2 implements java.lang.AutoCloseable {
+    method public void broadcastSessionCommand(android.media.Session2Command, android.os.Bundle);
+    method public void cancelSessionCommand(android.media.MediaSession2.ControllerInfo, java.lang.Object);
+    method public void close();
+    method public java.lang.String getSessionId();
+    method public android.media.Session2Token getSessionToken();
+    method public boolean isPlaybackActive();
+    method public java.lang.Object sendSessionCommand(android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle);
+    method public void setPlaybackActive(boolean);
+  }
+
+  public static final class MediaSession2.Builder {
+    ctor public MediaSession2.Builder(android.content.Context);
+    method public android.media.MediaSession2 build();
+    method public android.media.MediaSession2.Builder setId(java.lang.String);
+    method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
+    method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
+  }
+
+  public static final class MediaSession2.ControllerInfo {
+    method public java.lang.String getPackageName();
+    method public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo();
+    method public int getUid();
+  }
+
+  public static abstract class MediaSession2.SessionCallback {
+    ctor public MediaSession2.SessionCallback();
+    method public void onCommandResult(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result);
+    method public android.media.Session2CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
+    method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
+    method public android.media.Session2Command.Result onSessionCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle);
+  }
+
   public final class MediaSync {
     ctor public MediaSync();
     method public android.view.Surface createInputSurface();
@@ -26048,6 +26313,59 @@
     field public static final int URI_COLUMN_INDEX = 2; // 0x2
   }
 
+  public final class Session2Command implements android.os.Parcelable {
+    ctor public Session2Command(int);
+    ctor public Session2Command(java.lang.String, android.os.Bundle);
+    method public int describeContents();
+    method public int getCommandCode();
+    method public java.lang.String getCustomCommand();
+    method public android.os.Bundle getExtras();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.media.Session2Command> CREATOR;
+    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public static final class Session2Command.Result {
+    ctor public Session2Command.Result(int, android.os.Bundle);
+    method public int getResultCode();
+    method public android.os.Bundle getResultData();
+  }
+
+  public final class Session2CommandGroup implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.Set<android.media.Session2Command> getCommands();
+    method public boolean hasCommand(android.media.Session2Command);
+    method public boolean hasCommand(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.Session2CommandGroup> CREATOR;
+  }
+
+  public static final class Session2CommandGroup.Builder {
+    ctor public Session2CommandGroup.Builder();
+    ctor public Session2CommandGroup.Builder(android.media.Session2CommandGroup);
+    method public android.media.Session2CommandGroup.Builder addCommand(android.media.Session2Command);
+    method public android.media.Session2CommandGroup.Builder addCommand(int);
+    method public android.media.Session2CommandGroup build();
+    method public android.media.Session2CommandGroup.Builder removeCommand(android.media.Session2Command);
+    method public android.media.Session2CommandGroup.Builder removeCommand(int);
+  }
+
+  public final class Session2Token implements android.os.Parcelable {
+    ctor public Session2Token(android.content.Context, android.content.ComponentName);
+    method public int describeContents();
+    method public java.lang.String getPackageName();
+    method public java.lang.String getServiceName();
+    method public int getType();
+    method public int getUid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR;
+    field public static final int TYPE_SESSION = 0; // 0x0
+    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+  }
+
   public class SoundPool {
     ctor public deprecated SoundPool(int, int, int);
     method public final void autoPause();
@@ -27093,6 +27411,21 @@
 
 package android.media.session {
 
+  public final class ControllerCallbackLink implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.IBinder getBinder();
+    method public void notifyEvent(java.lang.String, android.os.Bundle);
+    method public void notifyExtrasChanged(android.os.Bundle);
+    method public void notifyMetadataChanged(android.media.MediaMetadata);
+    method public void notifyPlaybackStateChanged(android.media.session.PlaybackState);
+    method public void notifyQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
+    method public void notifyQueueTitleChanged(java.lang.CharSequence);
+    method public void notifySessionDestroyed();
+    method public void notifyVolumeInfoChanged(android.media.session.MediaController.PlaybackInfo);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.session.ControllerCallbackLink> CREATOR;
+  }
+
   public final class MediaController {
     ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
     method public void adjustVolume(int, int);
@@ -27128,12 +27461,15 @@
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
   }
 
-  public static final class MediaController.PlaybackInfo {
+  public static final class MediaController.PlaybackInfo implements android.os.Parcelable {
+    method public int describeContents();
     method public android.media.AudioAttributes getAudioAttributes();
     method public int getCurrentVolume();
     method public int getMaxVolume();
     method public int getPlaybackType();
     method public int getVolumeControl();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.session.MediaController.PlaybackInfo> CREATOR;
     field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
     field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
   }
@@ -27324,6 +27660,36 @@
     method public android.media.session.PlaybackState.CustomAction.Builder setExtras(android.os.Bundle);
   }
 
+  public final class SessionCallbackLink implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.IBinder getBinder();
+    method public void notifyAdjustVolume(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
+    method public void notifyCommand(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void notifyCustomAction(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+    method public void notifyFastForward(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void notifyMediaButton(java.lang.String, int, int, android.content.Intent, int, android.os.ResultReceiver);
+    method public void notifyMediaButtonFromController(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.content.Intent);
+    method public void notifyNext(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void notifyPause(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void notifyPlay(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void notifyPlayFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+    method public void notifyPlayFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+    method public void notifyPlayFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
+    method public void notifyPrepare(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void notifyPrepareFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+    method public void notifyPrepareFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+    method public void notifyPrepareFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
+    method public void notifyPrevious(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void notifyRate(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.media.Rating);
+    method public void notifyRewind(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void notifySeekTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
+    method public void notifySetVolumeTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
+    method public void notifySkipToTrack(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
+    method public void notifyStop(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.session.SessionCallbackLink> CREATOR;
+  }
+
 }
 
 package android.media.tv {
@@ -29384,7 +29750,7 @@
     field public java.lang.String SSID;
     field public java.util.BitSet allowedAuthAlgorithms;
     field public java.util.BitSet allowedGroupCiphers;
-    field public java.util.BitSet allowedGroupMgmtCiphers;
+    field public java.util.BitSet allowedGroupManagementCiphers;
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
@@ -29531,8 +29897,10 @@
     method public java.lang.String getMacAddress();
     method public int getNetworkId();
     method public int getRssi();
+    method public int getRxLinkSpeedMbps();
     method public java.lang.String getSSID();
     method public android.net.wifi.SupplicantState getSupplicantState();
+    method public int getTxLinkSpeedMbps();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String FREQUENCY_UNITS = "MHz";
     field public static final java.lang.String LINK_SPEED_UNITS = "Mbps";
@@ -29555,7 +29923,7 @@
     method public android.net.wifi.WifiInfo getConnectionInfo();
     method public android.net.DhcpInfo getDhcpInfo();
     method public int getMaxNumberOfNetworkSuggestionsPerApp();
-    method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
+    method public deprecated java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
     method public java.util.List<android.net.wifi.ScanResult> getScanResults();
     method public int getWifiState();
     method public boolean is5GHzBandSupported();
@@ -29575,7 +29943,7 @@
     method public deprecated boolean reconnect();
     method public deprecated boolean removeNetwork(int);
     method public int removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
-    method public void removePasspointConfiguration(java.lang.String);
+    method public deprecated void removePasspointConfiguration(java.lang.String);
     method public deprecated boolean saveConfiguration();
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
@@ -29651,9 +30019,6 @@
     method public void setReferenceCounted(boolean);
   }
 
-  public static abstract class WifiManager.NetworkSuggestionsStatusCode implements java.lang.annotation.Annotation {
-  }
-
   public class WifiManager.WifiLock {
     method public void acquire();
     method public boolean isHeld();
@@ -29826,12 +30191,16 @@
     method public android.net.NetworkSpecifier build();
     method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setDiscoverySession(android.net.wifi.aware.DiscoverySession);
     method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPeerHandle(android.net.wifi.aware.PeerHandle);
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPort(int);
     method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPskPassphrase(java.lang.String);
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setTransportProtocol(int);
   }
 
   public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
     method public int describeContents();
     method public java.net.Inet6Address getPeerIpv6Addr();
+    method public int getPort();
+    method public int getTransportProtocol();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkInfo> CREATOR;
   }
@@ -29975,7 +30344,8 @@
     method public android.net.wifi.p2p.WifiP2pConfig build();
     method public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean);
     method public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(android.net.MacAddress);
-    method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOwnerBand(int);
+    method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingBand(int);
+    method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingFrequency(int);
     method public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(java.lang.String);
     method public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(java.lang.String);
   }
@@ -30018,6 +30388,7 @@
     ctor public WifiP2pGroup(android.net.wifi.p2p.WifiP2pGroup);
     method public int describeContents();
     method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getClientList();
+    method public int getFrequency();
     method public java.lang.String getInterface();
     method public java.lang.String getNetworkName();
     method public android.net.wifi.p2p.WifiP2pDevice getOwner();
@@ -33677,6 +34048,7 @@
     method public static final void flushPendingCommands();
     method public static final int getCallingPid();
     method public static final int getCallingUid();
+    method public static final int getCallingUidOrThrow();
     method public static final android.os.UserHandle getCallingUserHandle();
     method public java.lang.String getInterfaceDescriptor();
     method public boolean isBinderAlive();
@@ -34341,6 +34713,8 @@
     method public java.util.ArrayList<java.lang.String> createStringArrayList();
     method public <T> T[] createTypedArray(android.os.Parcelable.Creator<T>);
     method public <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>);
+    method public <T extends android.os.Parcelable> android.util.ArrayMap<java.lang.String, T> createTypedArrayMap(android.os.Parcelable.Creator<T>);
+    method public <T extends android.os.Parcelable> android.util.SparseArray<T> createTypedSparseArray(android.os.Parcelable.Creator<T>);
     method public int dataAvail();
     method public int dataCapacity();
     method public int dataPosition();
@@ -34382,7 +34756,7 @@
     method public java.io.Serializable readSerializable();
     method public android.util.Size readSize();
     method public android.util.SizeF readSizeF();
-    method public android.util.SparseArray readSparseArray(java.lang.ClassLoader);
+    method public <T> android.util.SparseArray<T> readSparseArray(java.lang.ClassLoader);
     method public android.util.SparseBooleanArray readSparseBooleanArray();
     method public java.lang.String readString();
     method public void readStringArray(java.lang.String[]);
@@ -34428,7 +34802,7 @@
     method public void writeSerializable(java.io.Serializable);
     method public void writeSize(android.util.Size);
     method public void writeSizeF(android.util.SizeF);
-    method public void writeSparseArray(android.util.SparseArray<java.lang.Object>);
+    method public <T> void writeSparseArray(android.util.SparseArray<T>);
     method public void writeSparseBooleanArray(android.util.SparseBooleanArray);
     method public void writeString(java.lang.String);
     method public void writeStringArray(java.lang.String[]);
@@ -34436,8 +34810,10 @@
     method public void writeStrongBinder(android.os.IBinder);
     method public void writeStrongInterface(android.os.IInterface);
     method public <T extends android.os.Parcelable> void writeTypedArray(T[], int);
+    method public <T extends android.os.Parcelable> void writeTypedArrayMap(android.util.ArrayMap<java.lang.String, T>, int);
     method public <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
     method public <T extends android.os.Parcelable> void writeTypedObject(T, int);
+    method public <T extends android.os.Parcelable> void writeTypedSparseArray(android.util.SparseArray<T>, int);
     method public void writeValue(java.lang.Object);
     field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
   }
@@ -37866,7 +38242,11 @@
     method public static java.lang.String getVolumeName(android.net.Uri);
     method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri);
     method public static android.net.Uri setIncludePending(android.net.Uri);
+    method public static android.net.Uri setIncludeTrashed(android.net.Uri);
     method public static android.net.Uri setRequireOriginal(android.net.Uri);
+    method public static void trash(android.content.Context, android.net.Uri);
+    method public static void trash(android.content.Context, android.net.Uri, long);
+    method public static void untrash(android.content.Context, android.net.Uri);
     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";
     field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -38045,6 +38425,7 @@
   }
 
   public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns {
+    field public static final java.lang.String DESCRIPTION = "description";
     field public static final java.lang.String DOWNLOAD_URI = "download_uri";
     field public static final java.lang.String REFERER_URI = "referer_uri";
   }
@@ -38089,6 +38470,7 @@
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String ORIENTATION = "orientation";
     field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
+    field public static final java.lang.String SECONDARY_BUCKET_ID = "secondary_bucket_id";
   }
 
   public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
@@ -38133,11 +38515,13 @@
   public static abstract interface MediaStore.MediaColumns implements android.provider.BaseColumns {
     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_EXPIRES = "date_expires";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String DISPLAY_NAME = "_display_name";
     field public static final java.lang.String HASH = "_hash";
     field public static final java.lang.String HEIGHT = "height";
     field public static final java.lang.String IS_PENDING = "is_pending";
+    field public static final java.lang.String IS_TRASHED = "is_trashed";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String OWNER_PACKAGE_NAME = "owner_package_name";
     field public static final java.lang.String SIZE = "_size";
@@ -38213,6 +38597,7 @@
     field public static final deprecated java.lang.String LONGITUDE = "longitude";
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String RESOLUTION = "resolution";
+    field public static final java.lang.String SECONDARY_BUCKET_ID = "secondary_bucket_id";
     field public static final java.lang.String TAGS = "tags";
   }
 
@@ -41454,6 +41839,7 @@
     field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
     field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
     field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
+    field public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 128; // 0x80
     field public static final int SHOW_SOURCE_NOTIFICATION = 64; // 0x40
     field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20
     field public static final int SHOW_WITH_ASSIST = 1; // 0x1
@@ -42620,6 +43006,7 @@
     method public static java.lang.String capabilitiesToString(int);
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public int getCallCapabilities();
+    method public int getCallDirection();
     method public android.telecom.CallIdentification getCallIdentification();
     method public int getCallProperties();
     method public java.lang.String getCallerDisplayName();
@@ -42656,6 +43043,9 @@
     field public static final int CAPABILITY_SUPPORT_DEFLECT = 16777216; // 0x1000000
     field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
     field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int DIRECTION_INCOMING = 0; // 0x0
+    field public static final int DIRECTION_OUTGOING = 1; // 0x1
+    field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff
     field public static final int PROPERTY_CONFERENCE = 1; // 0x1
     field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
     field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -42731,10 +43121,10 @@
     ctor public CallRedirectionService();
     method public final void cancelCall();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
     method public final boolean onUnbind(android.content.Intent);
     method public final void placeCallUnmodified();
-    method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
   }
 
@@ -42918,10 +43308,12 @@
     field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
     field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
     field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+    field public static final java.lang.String EVENT_RTT_AUDIO_INDICATION_CHANGED = "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
     field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
     field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
+    field public static final java.lang.String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final java.lang.String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
@@ -44197,6 +44589,7 @@
     method public int describeContents();
     method public int getCdmaDbm();
     method public int getCdmaEcio();
+    method public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths();
     method public int getEvdoDbm();
     method public int getEvdoEcio();
     method public int getEvdoSnr();
@@ -44576,6 +44969,7 @@
     field public static final int DATA_CONNECTING = 1; // 0x1
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
+    field public static final int DATA_UNKNOWN = -1; // 0xffffffff
     field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
     field public static final java.lang.String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
     field public static final java.lang.String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
@@ -44638,6 +45032,9 @@
   public static abstract class TelephonyManager.CellInfoCallback {
     ctor public TelephonyManager.CellInfoCallback();
     method public abstract void onCellInfo(java.util.List<android.telephony.CellInfo>);
+    method public void onError(int, java.lang.Throwable);
+    field public static final int ERROR_MODEM_ERROR = 2; // 0x2
+    field public static final int ERROR_TIMEOUT = 1; // 0x1
   }
 
   public static abstract class TelephonyManager.UssdResponseCallback {
@@ -44805,16 +45202,21 @@
     method public int compareTo(android.telephony.emergency.EmergencyNumber);
     method public int describeContents();
     method public java.lang.String getCountryIso();
+    method public int getEmergencyCallRouting();
     method public int getEmergencyNumberSourceBitmask();
     method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
     method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
     method public int getEmergencyServiceCategoryBitmask();
+    method public java.util.List<java.lang.String> getEmergencyUrns();
     method public java.lang.String getMnc();
     method public java.lang.String getNumber();
     method public boolean isFromSources(int);
     method public boolean isInEmergencyServiceCategories(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+    field public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1; // 0x1
+    field public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2; // 0x2
+    field public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0; // 0x0
     field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10
     field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
     field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
@@ -44852,6 +45254,7 @@
   }
 
   public class EuiccManager {
+    method public android.telephony.euicc.EuiccManager createForCardId(int);
     method public void deleteSubscription(int, android.app.PendingIntent);
     method public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent);
     method public java.lang.String getEid();
@@ -44859,6 +45262,7 @@
     method public boolean isEnabled();
     method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException;
     method public void switchToSubscription(int, android.app.PendingIntent);
+    method public void updateSubscriptionNickname(int, java.lang.String, android.app.PendingIntent);
     field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
     field public static final java.lang.String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; // 0x2
@@ -46853,7 +47257,7 @@
   public class Linkify {
     ctor public Linkify();
     method public static final boolean addLinks(android.text.Spannable, int);
-    method public static final boolean addLinks(android.text.Spannable, int, android.text.util.Linkify.UrlSpanFactory);
+    method public static final boolean addLinks(android.text.Spannable, int, java.util.function.Function<java.lang.String, android.text.style.URLSpan>);
     method public static final boolean addLinks(android.widget.TextView, int);
     method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String);
     method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
@@ -46861,7 +47265,7 @@
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
-    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, android.text.util.Linkify.UrlSpanFactory);
+    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, java.util.function.Function<java.lang.String, android.text.style.URLSpan>);
     field public static final int ALL = 15; // 0xf
     field public static final int EMAIL_ADDRESSES = 2; // 0x2
     field public static final deprecated int MAP_ADDRESSES = 8; // 0x8
@@ -46880,11 +47284,6 @@
     method public abstract java.lang.String transformUrl(java.util.regex.Matcher, java.lang.String);
   }
 
-  public static class Linkify.UrlSpanFactory {
-    ctor public Linkify.UrlSpanFactory();
-    method public android.text.style.URLSpan create(java.lang.String);
-  }
-
   public class Rfc822Token {
     ctor public Rfc822Token(java.lang.String, java.lang.String, java.lang.String);
     method public java.lang.String getAddress();
@@ -47957,6 +48356,7 @@
     field public int data;
     field public int density;
     field public int resourceId;
+    field public int sourceStyleResourceId;
     field public java.lang.CharSequence string;
     field public int type;
   }
@@ -48143,6 +48543,7 @@
     field public static final android.os.Parcelable.Creator<android.view.Display.HdrCapabilities> CREATOR;
     field public static final int HDR_TYPE_DOLBY_VISION = 1; // 0x1
     field public static final int HDR_TYPE_HDR10 = 2; // 0x2
+    field public static final int HDR_TYPE_HDR10_PLUS = 4; // 0x4
     field public static final int HDR_TYPE_HLG = 3; // 0x3
     field public static final float INVALID_LUMINANCE = -1.0f;
   }
@@ -49071,6 +49472,7 @@
     method public float getAxisValue(int);
     method public float getAxisValue(int, int);
     method public int getButtonState();
+    method public int getClassification();
     method public int getDeviceId();
     method public long getDownTime();
     method public int getEdgeFlags();
@@ -49222,6 +49624,9 @@
     field public static final int BUTTON_STYLUS_PRIMARY = 32; // 0x20
     field public static final int BUTTON_STYLUS_SECONDARY = 64; // 0x40
     field public static final int BUTTON_TERTIARY = 4; // 0x4
+    field public static final int CLASSIFICATION_AMBIGUOUS_GESTURE = 1; // 0x1
+    field public static final int CLASSIFICATION_DEEP_PRESS = 2; // 0x2
+    field public static final int CLASSIFICATION_NONE = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.view.MotionEvent> CREATOR;
     field public static final int EDGE_BOTTOM = 2; // 0x2
     field public static final int EDGE_LEFT = 4; // 0x4
@@ -52272,10 +52677,12 @@
     method public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(android.view.contentcapture.ContentCaptureContext);
     method public final void destroy();
     method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
+    method public android.view.autofill.AutofillId newAutofillId(android.view.autofill.AutofillId, int);
+    method public final android.view.ViewStructure newVirtualViewStructure(android.view.autofill.AutofillId, int);
     method public final void notifyViewAppeared(android.view.ViewStructure);
     method public final void notifyViewDisappeared(android.view.autofill.AutofillId);
     method public final void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
-    field public static final int FLAG_USER_INPUT = 1; // 0x1
+    method public final void notifyViewsDisappeared(android.view.autofill.AutofillId, int[]);
   }
 
   public final class ContentCaptureSessionId implements android.os.Parcelable {
@@ -52712,19 +53119,8 @@
     ctor public InspectionCompanion.UninitializedPropertyMapException();
   }
 
-  public final class IntEnumMapping {
-    method public java.lang.String nameOf(int);
-  }
-
-  public static final class IntEnumMapping.Builder {
-    ctor public IntEnumMapping.Builder();
-    method public android.view.inspector.IntEnumMapping.Builder addValue(java.lang.String, int);
-    method public android.view.inspector.IntEnumMapping build();
-    method public void clear();
-  }
-
   public final class IntFlagMapping {
-    method public java.lang.String[] namesOf(int);
+    method public java.util.Set<java.lang.String> get(int);
   }
 
   public static final class IntFlagMapping.Builder {
@@ -52732,7 +53128,6 @@
     method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int);
     method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int, int);
     method public android.view.inspector.IntFlagMapping build();
-    method public void clear();
   }
 
   public abstract interface PropertyMapper {
@@ -52744,7 +53139,7 @@
     method public abstract int mapFloat(java.lang.String, int);
     method public abstract int mapGravity(java.lang.String, int);
     method public abstract int mapInt(java.lang.String, int);
-    method public abstract int mapIntEnum(java.lang.String, int, android.view.inspector.IntEnumMapping);
+    method public abstract int mapIntEnum(java.lang.String, int, android.util.SparseArray<java.lang.String>);
     method public abstract int mapIntFlag(java.lang.String, int, android.view.inspector.IntFlagMapping);
     method public abstract int mapLong(java.lang.String, int);
     method public abstract int mapObject(java.lang.String, int);
@@ -52782,15 +53177,15 @@
 
 package android.view.textclassifier {
 
-  public final class ConversationActions implements android.os.Parcelable {
-    ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>, java.lang.String);
+  public final class ConversationAction implements android.os.Parcelable {
     method public int describeContents();
-    method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
-    method public java.lang.String getId();
+    method public android.app.RemoteAction getAction();
+    method public float getConfidenceScore();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getTextReply();
+    method public java.lang.String getType();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
-    field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
-    field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationAction> CREATOR;
     field public static final java.lang.String TYPE_CALL_PHONE = "call_phone";
     field public static final java.lang.String TYPE_CREATE_REMINDER = "create_reminder";
     field public static final java.lang.String TYPE_OPEN_URL = "open_url";
@@ -52803,24 +53198,22 @@
     field public static final java.lang.String TYPE_VIEW_MAP = "view_map";
   }
 
-  public static final class ConversationActions.ConversationAction implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.app.RemoteAction getAction();
-    method public float getConfidenceScore();
-    method public android.os.Bundle getExtras();
-    method public java.lang.CharSequence getTextReply();
-    method public java.lang.String getType();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.ConversationAction> CREATOR;
+  public static final class ConversationAction.Builder {
+    ctor public ConversationAction.Builder(java.lang.String);
+    method public android.view.textclassifier.ConversationAction build();
+    method public android.view.textclassifier.ConversationAction.Builder setAction(android.app.RemoteAction);
+    method public android.view.textclassifier.ConversationAction.Builder setConfidenceScore(float);
+    method public android.view.textclassifier.ConversationAction.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.ConversationAction.Builder setTextReply(java.lang.CharSequence);
   }
 
-  public static final class ConversationActions.ConversationAction.Builder {
-    ctor public ConversationActions.ConversationAction.Builder(java.lang.String);
-    method public android.view.textclassifier.ConversationActions.ConversationAction build();
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setAction(android.app.RemoteAction);
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setConfidenceScore(float);
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setExtras(android.os.Bundle);
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setTextReply(java.lang.CharSequence);
+  public final class ConversationActions implements android.os.Parcelable {
+    ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationAction>, java.lang.String);
+    method public int describeContents();
+    method public java.util.List<android.view.textclassifier.ConversationAction> getConversationActions();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
   }
 
   public static final class ConversationActions.Message implements android.os.Parcelable {
@@ -52850,9 +53243,11 @@
     method public java.lang.String getConversationId();
     method public java.util.List<java.lang.String> getHints();
     method public int getMaxSuggestions();
-    method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
+    method public android.view.textclassifier.TextClassifier.EntityConfig getTypeConfig();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Request> CREATOR;
+    field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
+    field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
   }
 
   public static final class ConversationActions.Request.Builder {
@@ -52861,23 +53256,7 @@
     method public android.view.textclassifier.ConversationActions.Request.Builder setConversationId(java.lang.String);
     method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
     method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
-    method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
-  }
-
-  public static final class ConversationActions.TypeConfig implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.util.Collection<java.lang.String> resolveTypes(java.util.Collection<java.lang.String>);
-    method public boolean shouldIncludeTypesFromTextClassifier();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.TypeConfig> CREATOR;
-  }
-
-  public static final class ConversationActions.TypeConfig.Builder {
-    ctor public ConversationActions.TypeConfig.Builder();
-    method public android.view.textclassifier.ConversationActions.TypeConfig build();
-    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder includeTypesFromTextClassifier(boolean);
-    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
-    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.TextClassifier.EntityConfig);
   }
 
   public final class SelectionEvent implements android.os.Parcelable {
@@ -53051,16 +53430,26 @@
   }
 
   public static final class TextClassifier.EntityConfig implements android.os.Parcelable {
-    method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>);
-    method public static android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>);
-    method public static android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>);
+    method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>);
+    method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>);
+    method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>);
     method public int describeContents();
     method public java.util.Collection<java.lang.String> getHints();
     method public java.util.Collection<java.lang.String> resolveEntityListModifications(java.util.Collection<java.lang.String>);
+    method public boolean shouldIncludeTypesFromTextClassifier();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
   }
 
+  public static final class TextClassifier.EntityConfig.Builder {
+    ctor public TextClassifier.EntityConfig.Builder();
+    method public android.view.textclassifier.TextClassifier.EntityConfig build();
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder includeTypesFromTextClassifier(boolean);
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setHints(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+  }
+
   public final class TextClassifierEvent implements android.os.Parcelable {
     method public int describeContents();
     method public int[] getActionIndices();
@@ -53084,6 +53473,7 @@
     field public static final int CATEGORY_SELECTION = 1; // 0x1
     field public static final int CATEGORY_UNDEFINED = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent> CREATOR;
+    field public static final int TYPE_ACTIONS_GENERATED = 20; // 0x14
     field public static final int TYPE_ACTIONS_SHOWN = 6; // 0x6
     field public static final int TYPE_AUTO_SELECTION = 5; // 0x5
     field public static final int TYPE_COPY_ACTION = 9; // 0x9
@@ -53956,6 +54346,8 @@
     method public static java.lang.ClassLoader getWebViewClassLoader();
     method public android.webkit.WebViewClient getWebViewClient();
     method public android.os.Looper getWebViewLooper();
+    method public android.webkit.WebViewRenderer getWebViewRenderer();
+    method public android.webkit.WebViewRendererClient getWebViewRendererClient();
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
@@ -54005,6 +54397,8 @@
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
     method public void setWebViewClient(android.webkit.WebViewClient);
+    method public void setWebViewRendererClient(java.util.concurrent.Executor, android.webkit.WebViewRendererClient);
+    method public void setWebViewRendererClient(android.webkit.WebViewRendererClient);
     method public deprecated boolean shouldDelayChildPressedState();
     method public deprecated boolean showFindDialog(java.lang.String, boolean);
     method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
@@ -54120,6 +54514,16 @@
     method public android.webkit.WebView getWebView();
   }
 
+  public abstract class WebViewRenderer {
+    method public abstract boolean terminate();
+  }
+
+  public abstract class WebViewRendererClient {
+    ctor public WebViewRendererClient();
+    method public abstract void onRendererResponsive(android.webkit.WebView, android.webkit.WebViewRenderer);
+    method public abstract void onRendererUnresponsive(android.webkit.WebView, android.webkit.WebViewRenderer);
+  }
+
 }
 
 package android.widget {
@@ -55398,7 +55802,7 @@
     method public int getSourceWidth();
     method public int getWidth();
     method public float getZoom();
-    method public boolean isForcePositionWithinWindowSystemInsetsBounds();
+    method public boolean isClippingEnabled();
     method public void setZoom(float);
     method public void show(float, float);
     method public void show(float, float, float, float);
@@ -55411,10 +55815,10 @@
   public static class Magnifier.Builder {
     ctor public Magnifier.Builder(android.view.View);
     method public android.widget.Magnifier build();
+    method public android.widget.Magnifier.Builder setClippingEnabled(boolean);
     method public android.widget.Magnifier.Builder setCornerRadius(float);
     method public android.widget.Magnifier.Builder setDefaultSourceToMagnifierOffset(int, int);
     method public android.widget.Magnifier.Builder setElevation(float);
-    method public android.widget.Magnifier.Builder setForcePositionWithinWindowSystemInsetsBounds(boolean);
     method public android.widget.Magnifier.Builder setOverlay(android.graphics.drawable.Drawable);
     method public android.widget.Magnifier.Builder setSize(int, int);
     method public android.widget.Magnifier.Builder setSourceBounds(int, int, int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 10d483d..e9743f7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -18,6 +18,7 @@
     field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
     field public static final java.lang.String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final java.lang.String BACKUP = "android.permission.BACKUP";
+    field public static final java.lang.String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
     field public static final java.lang.String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
     field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
     field public static final java.lang.String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
@@ -86,13 +87,14 @@
     field public static final java.lang.String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
     field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
+    field public static final java.lang.String MANAGE_APP_PREDICTIONS = "android.permission.MANAGE_APP_PREDICTIONS";
     field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
     field public static final java.lang.String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL";
     field public static final java.lang.String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final java.lang.String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
+    field public static final java.lang.String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
     field public static final java.lang.String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
-    field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final java.lang.String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -127,6 +129,7 @@
     field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
     field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
+    field public static final java.lang.String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
     field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
     field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
     field public static final java.lang.String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
@@ -149,6 +152,7 @@
     field public static final java.lang.String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
     field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
     field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
+    field public static final java.lang.String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
     field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
     field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -182,6 +186,7 @@
     field public static final java.lang.String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
     field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
     field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
+    field public static final java.lang.String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
     field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
     field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
     field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
@@ -193,10 +198,12 @@
   }
 
   public static final class R.array {
+    field public static final int config_defaultRoleHolders = 17235974; // 0x1070006
     field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
   }
 
   public static final class R.attr {
+    field public static final int inheritShowWhenLocked = 16844194; // 0x10105a2
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
@@ -256,6 +263,7 @@
     method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions);
     method public deprecated boolean isBackgroundVisibleBehind();
     method public deprecated void onBackgroundVisibleBehindChanged(boolean);
+    method public void setInheritShowWhenLocked(boolean);
   }
 
   public static abstract interface Activity.TranslucentConversionListener {
@@ -284,10 +292,11 @@
   }
 
   public class AppOpsManager {
-    method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
-    method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
+    method public void getHistoricalOps(int, java.lang.String, java.lang.String[], long, long, java.util.concurrent.Executor, java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static java.lang.String[] getOpStrs();
-    method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
+    method public deprecated java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
+    method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, java.lang.String...);
+    method public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(java.lang.String[]);
     method public static int opToDefaultMode(java.lang.String);
     method public static java.lang.String opToPermission(java.lang.String);
     method public void setMode(java.lang.String, int, java.lang.String, int);
@@ -343,7 +352,7 @@
     field public static final int UID_STATE_TOP = 1; // 0x1
   }
 
-  public static final class AppOpsManager.HistoricalOpEntry implements android.os.Parcelable {
+  public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
     method public int describeContents();
     method public long getAccessCount(int);
     method public long getAccessDuration(int);
@@ -353,23 +362,43 @@
     method public long getForegroundAccessCount();
     method public long getForegroundAccessDuration();
     method public long getForegroundRejectCount();
-    method public java.lang.String getOp();
+    method public java.lang.String getOpName();
     method public long getRejectCount(int);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOpEntry> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR;
+  }
+
+  public static final class AppOpsManager.HistoricalOps implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getBeginTimeMillis();
+    method public long getEndTimeMillis();
+    method public int getUidCount();
+    method public android.app.AppOpsManager.HistoricalUidOps getUidOps(int);
+    method public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
   }
 
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
-    method public android.app.AppOpsManager.HistoricalOpEntry getEntry(java.lang.String);
-    method public android.app.AppOpsManager.HistoricalOpEntry getEntryAt(int);
-    method public int getEntryCount();
+    method public android.app.AppOpsManager.HistoricalOp getOp(java.lang.String);
+    method public android.app.AppOpsManager.HistoricalOp getOpAt(int);
+    method public int getOpCount();
     method public java.lang.String getPackageName();
-    method public int getUid();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalPackageOps> CREATOR;
   }
 
+  public static final class AppOpsManager.HistoricalUidOps implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getPackageCount();
+    method public android.app.AppOpsManager.HistoricalPackageOps getPackageOps(java.lang.String);
+    method public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(int);
+    method public int getUid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalUidOps> CREATOR;
+  }
+
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
     method public int getDuration();
@@ -537,6 +566,13 @@
     method public void onVrStateChanged(boolean);
   }
 
+  public final class WallpaperColors implements android.os.Parcelable {
+    ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
+    method public int getColorHints();
+    field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
+    field public static final int HINT_SUPPORTS_DARK_THEME = 2; // 0x2
+  }
+
   public final class WallpaperInfo implements android.os.Parcelable {
     method public boolean supportsAmbientMode();
   }
@@ -841,6 +877,73 @@
 
 }
 
+package android.app.contentsuggestions {
+
+  public final class ClassificationsRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public java.util.List<android.app.contentsuggestions.ContentSelection> getSelections();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.ClassificationsRequest> CREATOR;
+  }
+
+  public static final class ClassificationsRequest.Builder {
+    ctor public ClassificationsRequest.Builder(java.util.List<android.app.contentsuggestions.ContentSelection>);
+    method public android.app.contentsuggestions.ClassificationsRequest build();
+    method public android.app.contentsuggestions.ClassificationsRequest.Builder setExtras(android.os.Bundle);
+  }
+
+  public final class ContentClassification implements android.os.Parcelable {
+    ctor public ContentClassification(java.lang.String, android.os.Bundle);
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.ContentClassification> CREATOR;
+  }
+
+  public final class ContentSelection implements android.os.Parcelable {
+    ctor public ContentSelection(java.lang.String, android.os.Bundle);
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.ContentSelection> CREATOR;
+  }
+
+  public final class ContentSuggestionsManager {
+    method public void classifyContentSelections(android.app.contentsuggestions.ClassificationsRequest, java.util.concurrent.Executor, android.app.contentsuggestions.ContentSuggestionsManager.ClassificationsCallback);
+    method public void notifyInteraction(java.lang.String, android.os.Bundle);
+    method public void provideContextImage(int, android.os.Bundle);
+    method public void suggestContentSelections(android.app.contentsuggestions.SelectionsRequest, java.util.concurrent.Executor, android.app.contentsuggestions.ContentSuggestionsManager.SelectionsCallback);
+  }
+
+  public static abstract interface ContentSuggestionsManager.ClassificationsCallback {
+    method public abstract void onContentClassificationsAvailable(int, java.util.List<android.app.contentsuggestions.ContentClassification>);
+  }
+
+  public static abstract interface ContentSuggestionsManager.SelectionsCallback {
+    method public abstract void onContentSelectionsAvailable(int, java.util.List<android.app.contentsuggestions.ContentSelection>);
+  }
+
+  public final class SelectionsRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.Point getInterestPoint();
+    method public int getTaskId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.SelectionsRequest> CREATOR;
+  }
+
+  public static final class SelectionsRequest.Builder {
+    ctor public SelectionsRequest.Builder(int);
+    method public android.app.contentsuggestions.SelectionsRequest build();
+    method public android.app.contentsuggestions.SelectionsRequest.Builder setExtras(android.os.Bundle);
+    method public android.app.contentsuggestions.SelectionsRequest.Builder setInterestPoint(android.graphics.Point);
+  }
+
+}
+
 package android.app.job {
 
   public abstract class JobScheduler {
@@ -849,6 +952,87 @@
 
 }
 
+package android.app.prediction {
+
+  public final class AppPredictionContext implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getPackageName();
+    method public int getPredictedTargetCount();
+    method public java.lang.String getUiSurface();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.prediction.AppPredictionContext> CREATOR;
+  }
+
+  public static final class AppPredictionContext.Builder {
+    method public android.app.prediction.AppPredictionContext build();
+    method public android.app.prediction.AppPredictionContext.Builder setExtras(android.os.Bundle);
+    method public android.app.prediction.AppPredictionContext.Builder setPredictedTargetCount(int);
+    method public android.app.prediction.AppPredictionContext.Builder setUiSurface(java.lang.String);
+  }
+
+  public final class AppPredictionManager {
+    method public android.app.prediction.AppPredictor createAppPredictionSession(android.app.prediction.AppPredictionContext);
+  }
+
+  public final class AppPredictionSessionId 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.app.prediction.AppPredictionSessionId> CREATOR;
+  }
+
+  public final class AppPredictor {
+    method public void destroy();
+    method public void notifyAppTargetEvent(android.app.prediction.AppTargetEvent);
+    method public void notifyLocationShown(java.lang.String, java.util.List<android.app.prediction.AppTargetId>);
+    method public void registerPredictionUpdates(java.util.concurrent.Executor, android.app.prediction.AppPredictor.Callback);
+    method public void requestPredictionUpdate();
+    method public void sortTargets(java.util.List<android.app.prediction.AppTarget>, java.util.concurrent.Executor, java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>);
+    method public void unregisterPredictionUpdates(android.app.prediction.AppPredictor.Callback);
+  }
+
+  public static abstract interface AppPredictor.Callback {
+    method public abstract void onTargetsAvailable(java.util.List<android.app.prediction.AppTarget>);
+  }
+
+  public final class AppTarget implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getClassName();
+    method public android.app.prediction.AppTargetId getId();
+    method public java.lang.String getPackageName();
+    method public int getRank();
+    method public android.content.pm.ShortcutInfo getShortcutInfo();
+    method public android.os.UserHandle getUser();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.prediction.AppTarget> CREATOR;
+  }
+
+  public final class AppTargetEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAction();
+    method public java.lang.String getLaunchLocation();
+    method public android.app.prediction.AppTarget getTarget();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ACTION_DISMISS = 2; // 0x2
+    field public static final int ACTION_LAUNCH = 1; // 0x1
+    field public static final int ACTION_PIN = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR;
+  }
+
+  public static final class AppTargetEvent.Builder {
+    ctor public AppTargetEvent.Builder(android.app.prediction.AppTarget, int);
+    method public android.app.prediction.AppTargetEvent build();
+    method public android.app.prediction.AppTargetEvent.Builder setLaunchLocation(java.lang.String);
+  }
+
+  public final class AppTargetId 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.app.prediction.AppTargetId> CREATOR;
+  }
+
+}
+
 package android.app.role {
 
   public abstract interface OnRoleHoldersChangedListener {
@@ -867,7 +1051,6 @@
     method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
     method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
     method public void setRoleNamesFromController(java.util.List<java.lang.String>);
-    field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
   }
 
   public abstract interface RoleManagerCallback {
@@ -927,6 +1110,9 @@
     method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
     method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
     method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent);
+    method public void reportUsageStart(android.app.Activity, java.lang.String);
+    method public void reportUsageStart(android.app.Activity, java.lang.String, long);
+    method public void reportUsageStop(android.app.Activity, java.lang.String);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
     method public void unregisterAppUsageObserver(int);
@@ -1032,7 +1218,9 @@
     method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.os.Bundle);
     method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public void startActivityAsUser(android.content.Intent, android.os.UserHandle);
+    field public static final java.lang.String APP_PREDICTION_SERVICE = "app_prediction";
     field public static final java.lang.String BACKUP_SERVICE = "backup";
+    field public static final java.lang.String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final java.lang.String CONTEXTHUB_SERVICE = "contexthub";
     field public static final java.lang.String EUICC_CARD_SERVICE = "euicc_card";
     field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
@@ -1069,8 +1257,10 @@
     field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
     field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
     field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
+    field public static final java.lang.String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
     field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
     field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
+    field public static final java.lang.String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
     field public static final java.lang.String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
     field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
     field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
@@ -1098,10 +1288,12 @@
     field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
     field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
     field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
+    field public static final java.lang.String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
     field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
     field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON";
     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_ROLE_NAME = "android.intent.extra.ROLE_NAME";
     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_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
     field public static final java.lang.String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
@@ -1114,16 +1306,37 @@
 
 }
 
+package android.content.om {
+
+  public final class OverlayInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isEnabled();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.om.OverlayInfo> CREATOR;
+    field public final java.lang.String category;
+    field public final java.lang.String packageName;
+    field public final java.lang.String targetPackageName;
+    field public final int userId;
+  }
+
+  public class OverlayManager {
+    method public java.util.List<android.content.om.OverlayInfo> getOverlayInfosForTarget(java.lang.String, int);
+    method public boolean setEnabledExclusiveInCategory(java.lang.String, int);
+  }
+
+}
+
 package android.content.pm {
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    method public boolean isEncryptionAware();
     method public boolean isInstantApp();
     field public java.lang.String credentialProtectedDataDir;
     field public int targetSandboxVersion;
   }
 
   public class CrossProfileApps {
-    method public void startAnyActivity(android.content.ComponentName, android.os.UserHandle);
+    method public void startActivity(android.content.ComponentName, android.os.UserHandle);
   }
 
   public final class InstantAppInfo implements android.os.Parcelable {
@@ -1253,6 +1466,7 @@
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void sendDeviceCustomizationReadyBroadcast();
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
+    method public java.lang.String[] setDistractingPackageRestrictions(java.lang.String[], int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
     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);
@@ -1320,6 +1534,9 @@
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_INSTANT = 8388608; // 0x800000
+    field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
+    field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
+    field public static final int RESTRICTION_NONE = 0; // 0x0
   }
 
   public static abstract class PackageManager.DexModuleRegisterCallback {
@@ -1327,6 +1544,9 @@
     method public abstract void onDexModuleRegistered(java.lang.String, boolean, java.lang.String);
   }
 
+  public static abstract class PackageManager.DistractionRestriction implements java.lang.annotation.Annotation {
+  }
+
   public static abstract interface PackageManager.OnPermissionsChangedListener {
     method public abstract void onPermissionsChanged(int);
   }
@@ -1343,6 +1563,7 @@
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int FLAG_REMOVED = 2; // 0x2
+    field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
@@ -1351,6 +1572,18 @@
     field public int requestRes;
   }
 
+  public class ShortcutManager {
+    method public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(android.content.IntentFilter);
+  }
+
+  public static final class ShortcutManager.ShareShortcutInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.pm.ShortcutInfo getShortcutInfo();
+    method public android.content.ComponentName getTargetComponent();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutManager.ShareShortcutInfo> CREATOR;
+  }
+
   public final class SuspendDialogInfo implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -1537,6 +1770,11 @@
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
   }
 
+  public final class ColorDisplayManager {
+    method public boolean setAppSaturationLevel(java.lang.String, int);
+    method public boolean setSaturationLevel(int);
+  }
+
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
     method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -1545,7 +1783,7 @@
     method public android.util.Pair<float[], float[]> getMinimumBrightnessCurve();
     method public android.graphics.Point getStableDisplaySize();
     method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
-    method public void setSaturationLevel(float);
+    method public deprecated void setSaturationLevel(float);
   }
 
 }
@@ -2663,6 +2901,7 @@
     method public int getConstellationType();
     method public float getExcessPathLengthMeters();
     method public float getExcessPathLengthUncertaintyMeters();
+    method public float getProbSatIsLos();
     method public android.location.GnssReflectingPlane getReflectingPlane();
     method public int getSatId();
     method public int getSingleSatCorrectionFlags();
@@ -2670,13 +2909,12 @@
     method public boolean hasExcessPathLengthUncertainty();
     method public boolean hasReflectingPlane();
     method public boolean hasSatelliteLineOfSight();
-    method public boolean isSatelliteLineOfSight();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR;
     field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2
     field public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 4; // 0x4
+    field public static final int HAS_PROB_SAT_IS_LOS_MASK = 1; // 0x1
     field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8
-    field public static final int HAS_SAT_IS_LOS_MASK = 1; // 0x1
   }
 
   public static class GnssSingleSatCorrection.Builder {
@@ -2686,9 +2924,9 @@
     method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int);
     method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float);
     method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float);
+    method public android.location.GnssSingleSatCorrection.Builder setProbSatIsLos(float);
     method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane);
     method public android.location.GnssSingleSatCorrection.Builder setSatId(int);
-    method public android.location.GnssSingleSatCorrection.Builder setSatIsLos(boolean);
     method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int);
   }
 
@@ -2943,7 +3181,7 @@
     method public void setLocationControllerExtraPackage(java.lang.String);
     method public void setLocationControllerExtraPackageEnabled(boolean);
     method public void setLocationEnabledForUser(boolean, android.os.UserHandle);
-    method public boolean setProviderEnabledForUser(java.lang.String, boolean, android.os.UserHandle);
+    method public deprecated boolean setProviderEnabledForUser(java.lang.String, boolean, android.os.UserHandle);
     method public boolean unregisterGnssBatchedLocationCallback(android.location.BatchedLocationCallback);
   }
 
@@ -3081,6 +3319,7 @@
   }
 
   public final class MediaRecorder.AudioSource {
+    field public static final int ECHO_REFERENCE = 1997; // 0x7cd
     field public static final int HOTWORD = 1999; // 0x7cf
     field public static final int RADIO_TUNER = 1998; // 0x7ce
   }
@@ -3203,6 +3442,22 @@
 
 package android.media.session {
 
+  public final class ControllerCallbackLink implements android.os.Parcelable {
+    ctor public ControllerCallbackLink(android.media.session.ControllerCallbackLink.CallbackStub);
+  }
+
+  public static abstract class ControllerCallbackLink.CallbackStub {
+    ctor public ControllerCallbackLink.CallbackStub();
+    method public void onEvent(java.lang.String, android.os.Bundle);
+    method public void onExtrasChanged(android.os.Bundle);
+    method public void onMetadataChanged(android.media.MediaMetadata);
+    method public void onPlaybackStateChanged(android.media.session.PlaybackState);
+    method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
+    method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onSessionDestroyed();
+    method public void onVolumeInfoChanged(android.media.session.MediaController.PlaybackInfo);
+  }
+
   public final class MediaSessionManager {
     method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
     method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
@@ -3672,24 +3927,24 @@
 
 package android.net.wifi {
 
-  public abstract class DppStatusCallback {
-    ctor public DppStatusCallback();
+  public abstract class EasyConnectStatusCallback {
+    ctor public EasyConnectStatusCallback();
     method public abstract void onConfiguratorSuccess(int);
     method public abstract void onEnrolleeSuccess(int);
     method public abstract void onFailure(int);
     method public abstract void onProgress(int);
-    field public static final int DPP_EVENT_FAILURE = -7; // 0xfffffff9
-    field public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
-    field public static final int DPP_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
-    field public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
-    field public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
-    field public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
-    field public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
-    field public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
-    field public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
-    field public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
-    field public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
-    field public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
+    field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
+    field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
+    field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
+    field public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; // 0xfffffff9
+    field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
+    field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
+    field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
+    field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
+    field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
+    field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
+    field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
+    field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
   }
 
   public deprecated class RttManager {
@@ -3903,13 +4158,19 @@
     field public static final int WPA2_PSK = 4; // 0x4
   }
 
+  public class WifiInfo implements android.os.Parcelable {
+    method public boolean isOsuAp();
+  }
+
   public class WifiManager {
     method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
     method public void connect(int, android.net.wifi.WifiManager.ActionListener);
     method public void disable(int, android.net.wifi.WifiManager.ActionListener);
     method public void disableEphemeralNetwork(java.lang.String);
     method public void forget(int, android.net.wifi.WifiManager.ActionListener);
-    method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
+    method public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration, java.util.Map<java.lang.Integer, java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
+    method public java.util.Map<android.net.wifi.hotspot2.OsuProvider, java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
+    method public java.util.Map<android.net.wifi.hotspot2.OsuProvider, android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
     method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method public int getWifiApState();
@@ -3921,10 +4182,11 @@
     method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
     method public void setDeviceMobilityState(int);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
-    method public void startDppAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.DppStatusCallback);
-    method public void startDppAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.DppStatusCallback);
+    method public void startEasyConnectAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.EasyConnectStatusCallback);
+    method public void startEasyConnectAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.EasyConnectStatusCallback);
     method public boolean startScan(android.os.WorkSource);
-    method public void stopDppSession();
+    method public void startSubscriptionProvisioning(android.net.wifi.hotspot2.OsuProvider, android.net.wifi.hotspot2.ProvisioningCallback, android.os.Handler);
+    method public void stopEasyConnectSession();
     method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     field public static final int CHANGE_REASON_ADDED = 0; // 0x0
     field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
@@ -3934,8 +4196,8 @@
     field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
     field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
     field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
-    field public static final int DPP_NETWORK_ROLE_AP = 1; // 0x1
-    field public static final int DPP_NETWORK_ROLE_STA = 0; // 0x0
+    field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
+    field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
     field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
     field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -3943,6 +4205,8 @@
     field public static final java.lang.String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
     field public static final java.lang.String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
     field public static final java.lang.String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
+    field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0
+    field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1
     field public static final java.lang.String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED";
     field public static final int WIFI_AP_STATE_DISABLED = 11; // 0xb
     field public static final int WIFI_AP_STATE_DISABLING = 10; // 0xa
@@ -4119,6 +4383,59 @@
 
 }
 
+package android.net.wifi.hotspot2 {
+
+  public final class OsuProvider implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getFriendlyName();
+    method public android.net.Uri getServerUri();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.OsuProvider> CREATOR;
+  }
+
+  public abstract class ProvisioningCallback {
+    ctor public ProvisioningCallback();
+    method public abstract void onProvisioningComplete();
+    method public abstract void onProvisioningFailure(int);
+    method public abstract void onProvisioningStatus(int);
+    field public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22; // 0x16
+    field public static final int OSU_FAILURE_AP_CONNECTION = 1; // 0x1
+    field public static final int OSU_FAILURE_INVALID_SERVER_URL = 8; // 0x8
+    field public static final int OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE = 17; // 0x11
+    field public static final int OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE = 21; // 0x15
+    field public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14; // 0xe
+    field public static final int OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE = 19; // 0x13
+    field public static final int OSU_FAILURE_NO_PPS_MO = 16; // 0x10
+    field public static final int OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE = 18; // 0x12
+    field public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23; // 0x17
+    field public static final int OSU_FAILURE_PROVISIONING_ABORTED = 6; // 0x6
+    field public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7; // 0x7
+    field public static final int OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES = 20; // 0x14
+    field public static final int OSU_FAILURE_SERVER_CONNECTION = 3; // 0x3
+    field public static final int OSU_FAILURE_SERVER_URL_INVALID = 2; // 0x2
+    field public static final int OSU_FAILURE_SERVER_VALIDATION = 4; // 0x4
+    field public static final int OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION = 5; // 0x5
+    field public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11; // 0xb
+    field public static final int OSU_FAILURE_START_REDIRECT_LISTENER = 12; // 0xc
+    field public static final int OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER = 13; // 0xd
+    field public static final int OSU_FAILURE_UNEXPECTED_COMMAND_TYPE = 9; // 0x9
+    field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS = 15; // 0xf
+    field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE = 10; // 0xa
+    field public static final int OSU_STATUS_AP_CONNECTED = 2; // 0x2
+    field public static final int OSU_STATUS_AP_CONNECTING = 1; // 0x1
+    field public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6; // 0x6
+    field public static final int OSU_STATUS_REDIRECT_RESPONSE_RECEIVED = 8; // 0x8
+    field public static final int OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS = 11; // 0xb
+    field public static final int OSU_STATUS_SECOND_SOAP_EXCHANGE = 9; // 0x9
+    field public static final int OSU_STATUS_SERVER_CONNECTED = 5; // 0x5
+    field public static final int OSU_STATUS_SERVER_CONNECTING = 3; // 0x3
+    field public static final int OSU_STATUS_SERVER_VALIDATED = 4; // 0x4
+    field public static final int OSU_STATUS_THIRD_SOAP_EXCHANGE = 10; // 0xa
+    field public static final int OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE = 7; // 0x7
+  }
+
+}
+
 package android.net.wifi.rtt {
 
   public static final class RangingRequest.Builder {
@@ -4210,6 +4527,10 @@
     method public abstract java.lang.Object onTransactStarted(android.os.IBinder, int);
   }
 
+  public static class Build.VERSION {
+    field public static final java.lang.String PREVIEW_SDK_FINGERPRINT;
+  }
+
   public final class ConfigUpdate {
     field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB";
     field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
@@ -4506,6 +4827,7 @@
     field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
     field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
     field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
+    field public static final int PAYLOAD_TIMESTAMP_ERROR = 51; // 0x33
     field public static final int POST_INSTALL_RUNNER_ERROR = 5; // 0x5
     field public static final int SUCCESS = 0; // 0x0
     field public static final int UPDATED_BUT_NOT_ACTIVE = 52; // 0x34
@@ -4602,6 +4924,7 @@
     method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException;
     method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
     method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException;
+    method public static boolean hasIsolatedStorage();
     field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
   }
 
@@ -4609,13 +4932,27 @@
 
 package android.permission {
 
+  public final class PermissionControllerManager {
+    method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+    field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
+    field public static final int REASON_MALWARE = 1; // 0x1
+  }
+
+  public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback {
+    ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback();
+    method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>);
+  }
+
   public abstract class PermissionControllerService extends android.app.Service {
     ctor public PermissionControllerService();
     method public final void attachBaseContext(android.content.Context);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
     method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+    method public abstract void onGetRuntimePermissionsBackup(android.os.UserHandle, java.io.OutputStream);
+    method public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onPermissionUsageResult(boolean, long);
     method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
+    method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
   }
 
@@ -4639,6 +4976,15 @@
     field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
   }
 
+  public final class RuntimePermissionUsageInfo implements android.os.Parcelable {
+    ctor public RuntimePermissionUsageInfo(java.lang.CharSequence, int);
+    method public int describeContents();
+    method public int getAppAccessCount();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionUsageInfo> CREATOR;
+  }
+
 }
 
 package android.permissionpresenterservice {
@@ -4760,6 +5106,10 @@
     method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener);
     method public static void resetToDefaults(int, java.lang.String);
     method public static boolean setProperty(java.lang.String, java.lang.String, java.lang.String, boolean);
+    field public static final java.lang.String NAMESPACE_AUTOFILL = "autofill";
+    field public static final java.lang.String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+    field public static final java.lang.String NAMESPACE_GAME_DRIVER = "game_driver";
+    field public static final java.lang.String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
   }
 
   public static abstract interface DeviceConfig.OnPropertyChangedListener {
@@ -5074,7 +5424,8 @@
 
   public class RecoveryController {
     method public android.security.keystore.recovery.RecoverySession createRecoverySession();
-    method public java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+    method public deprecated java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+    method public java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
     method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
     method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
@@ -5082,7 +5433,8 @@
     method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public java.util.Map<java.lang.String, java.security.cert.X509Certificate> getRootCertificates();
-    method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+    method public deprecated java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+    method public java.security.Key importKey(java.lang.String, byte[], byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
     method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
     method public static boolean isRecoverableKeyStoreEnabled(android.content.Context);
     method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -5109,6 +5461,7 @@
     method public int describeContents();
     method public java.lang.String getAlias();
     method public byte[] getEncryptedKeyMaterial();
+    method public byte[] getMetadata();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.WrappedApplicationKey> CREATOR;
   }
@@ -5118,6 +5471,47 @@
     method public android.security.keystore.recovery.WrappedApplicationKey build();
     method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAlias(java.lang.String);
     method public android.security.keystore.recovery.WrappedApplicationKey.Builder setEncryptedKeyMaterial(byte[]);
+    method public android.security.keystore.recovery.WrappedApplicationKey.Builder setMetadata(byte[]);
+  }
+
+}
+
+package android.service.appprediction {
+
+  public abstract class AppPredictionService extends android.app.Service {
+    ctor public AppPredictionService();
+    method public abstract void onAppTargetEvent(android.app.prediction.AppPredictionSessionId, android.app.prediction.AppTargetEvent);
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onCreatePredictionSession(android.app.prediction.AppPredictionContext, android.app.prediction.AppPredictionSessionId);
+    method public void onDestroyPredictionSession(android.app.prediction.AppPredictionSessionId);
+    method public abstract void onLocationShown(android.app.prediction.AppPredictionSessionId, java.lang.String, java.util.List<android.app.prediction.AppTargetId>);
+    method public abstract void onRequestPredictionUpdate(android.app.prediction.AppPredictionSessionId);
+    method public abstract void onSortAppTargets(android.app.prediction.AppPredictionSessionId, java.util.List<android.app.prediction.AppTarget>, android.os.CancellationSignal, java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>);
+    method public void onStartPredictionUpdates();
+    method public void onStopPredictionUpdates();
+    method public final void updatePredictions(android.app.prediction.AppPredictionSessionId, java.util.List<android.app.prediction.AppTarget>);
+  }
+
+}
+
+package android.service.attention {
+
+  public abstract class AttentionService extends android.app.Service {
+    ctor public AttentionService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onCancelAttentionCheck(int);
+    method public abstract void onCheckAttention(int, android.service.attention.AttentionService.AttentionCallback);
+    field public static final int ATTENTION_FAILURE_PREEMPTED = 2; // 0x2
+    field public static final int ATTENTION_FAILURE_TIMED_OUT = 3; // 0x3
+    field public static final int ATTENTION_FAILURE_UNKNOWN = 4; // 0x4
+    field public static final int ATTENTION_SUCCESS_ABSENT = 0; // 0x0
+    field public static final int ATTENTION_SUCCESS_PRESENT = 1; // 0x1
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.attention.AttentionService";
+  }
+
+  public static final class AttentionService.AttentionCallback {
+    method public void onFailure(int, int);
+    method public void onSuccess(int, int, long);
   }
 
 }
@@ -5141,6 +5535,8 @@
 
   public abstract class AugmentedAutofillService extends android.app.Service {
     ctor public AugmentedAutofillService();
+    method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method protected void dump(java.io.PrintWriter, java.lang.String[]);
     method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
   }
@@ -5230,6 +5626,7 @@
     method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
     method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
+    method public void onUserDataRemovalRequest(android.view.contentcapture.UserDataRemovalRequest);
     method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
     method public final void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
     method public final void setPackageContentCaptureEnabled(java.lang.String, boolean);
@@ -5247,6 +5644,19 @@
 
 }
 
+package android.service.contentsuggestions {
+
+  public abstract class ContentSuggestionsService extends android.app.Service {
+    ctor public ContentSuggestionsService();
+    method public abstract void classifyContentSelections(android.app.contentsuggestions.ClassificationsRequest, android.app.contentsuggestions.ContentSuggestionsManager.ClassificationsCallback);
+    method public abstract void notifyInteraction(java.lang.String, android.os.Bundle);
+    method public abstract void processContextImage(int, android.graphics.Bitmap, android.os.Bundle);
+    method public abstract void suggestContentSelections(android.app.contentsuggestions.SelectionsRequest, android.app.contentsuggestions.ContentSuggestionsManager.SelectionsCallback);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.contentsuggestions.ContentSuggestionsService";
+  }
+
+}
+
 package android.service.euicc {
 
   public final class DownloadSubscriptionResult implements android.os.Parcelable {
@@ -5336,6 +5746,7 @@
     field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
     field public static final java.lang.String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
     field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
+    field public static final java.lang.String EXTRA_RESOLUTION_CARD_ID = "android.service.euicc.extra.RESOLUTION_CARD_ID";
     field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
     field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
     field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
@@ -5409,9 +5820,9 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
-    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
+    method public void onActionInvoked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public void onNotificationDirectReply(java.lang.String);
+    method public void onNotificationDirectReplied(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
@@ -5926,6 +6337,40 @@
     field public static final int WWAN = 1; // 0x1
   }
 
+  public class CallAttributes implements android.os.Parcelable {
+    ctor public CallAttributes(android.telephony.PreciseCallState, int, android.telephony.CallQuality);
+    method public int describeContents();
+    method public android.telephony.CallQuality getCallQuality();
+    method public int getNetworkType();
+    method public android.telephony.PreciseCallState getPreciseCallState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
+  }
+
+  public final class CallQuality implements android.os.Parcelable {
+    ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+    method public int describeContents();
+    method public int getAverageRelativeJitter();
+    method public int getAverageRoundTripTime();
+    method public int getCallDuration();
+    method public int getCodecType();
+    method public int getDownlinkCallQualityLevel();
+    method public int getMaxRelativeJitter();
+    method public int getNumRtpPacketsNotReceived();
+    method public int getNumRtpPacketsReceived();
+    method public int getNumRtpPacketsTransmitted();
+    method public int getNumRtpPacketsTransmittedLost();
+    method public int getUplinkCallQualityLevel();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CALL_QUALITY_BAD = 4; // 0x4
+    field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
+    field public static final int CALL_QUALITY_FAIR = 2; // 0x2
+    field public static final int CALL_QUALITY_GOOD = 1; // 0x1
+    field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5
+    field public static final int CALL_QUALITY_POOR = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
+  }
+
   public class CarrierConfigManager {
     method public static android.os.PersistableBundle getDefaultConfig();
     method public void overrideConfig(int, android.os.PersistableBundle);
@@ -5933,6 +6378,111 @@
     field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
   }
 
+  public final class CarrierRestrictionRules implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers();
+    method public int getDefaultCarrierRestriction();
+    method public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
+    method public int getMultiSimPolicy();
+    method public boolean isAllCarriersAllowed();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1
+    field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.telephony.CarrierRestrictionRules> CREATOR;
+    field public static final int MULTISIM_POLICY_NONE = 0; // 0x0
+    field public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1; // 0x1
+  }
+
+  public static class CarrierRestrictionRules.Builder {
+    method public android.telephony.CarrierRestrictionRules build();
+    method public android.telephony.CarrierRestrictionRules.Builder setAllCarriersAllowed();
+    method public android.telephony.CarrierRestrictionRules.Builder setAllowedCarriers(java.util.List<android.service.carrier.CarrierIdentifier>);
+    method public android.telephony.CarrierRestrictionRules.Builder setDefaultCarrierRestriction(int);
+    method public android.telephony.CarrierRestrictionRules.Builder setExcludedCarriers(java.util.List<android.service.carrier.CarrierIdentifier>);
+    method public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
+  }
+
+  public final class DataFailCause {
+    field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e
+    field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f
+    field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41
+    field public static final int APN_TYPE_CONFLICT = 112; // 0x70
+    field public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 122; // 0x7a
+    field public static final int COMPANION_IFACE_IN_USE = 118; // 0x76
+    field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+    field public static final int EMERGENCY_IFACE_ONLY = 116; // 0x74
+    field public static final int EMM_ACCESS_BARRED = 115; // 0x73
+    field public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 121; // 0x79
+    field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+    field public static final int ESM_INFO_NOT_RECEIVED = 53; // 0x35
+    field public static final int FEATURE_NOT_SUPP = 40; // 0x28
+    field public static final int FILTER_SEMANTIC_ERROR = 44; // 0x2c
+    field public static final int FILTER_SYTAX_ERROR = 45; // 0x2d
+    field public static final int GPRS_REGISTRATION_FAIL = -2; // 0xfffffffe
+    field public static final int IFACE_AND_POL_FAMILY_MISMATCH = 120; // 0x78
+    field public static final int IFACE_MISMATCH = 117; // 0x75
+    field public static final int INSUFFICIENT_RESOURCES = 26; // 0x1a
+    field public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 114; // 0x72
+    field public static final int INVALID_MANDATORY_INFO = 96; // 0x60
+    field public static final int INVALID_PCSCF_ADDR = 113; // 0x71
+    field public static final int INVALID_TRANSACTION_ID = 81; // 0x51
+    field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77
+    field public static final int LLC_SNDCP = 25; // 0x19
+    field public static final int LOST_CONNECTION = 65540; // 0x10004
+    field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f
+    field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61
+    field public static final int MISSING_UNKNOWN_APN = 27; // 0x1b
+    field public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 101; // 0x65
+    field public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 98; // 0x62
+    field public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 55; // 0x37
+    field public static final int NAS_SIGNALLING = 14; // 0xe
+    field public static final int NETWORK_FAILURE = 38; // 0x26
+    field public static final int NONE = 0; // 0x0
+    field public static final int NSAPI_IN_USE = 35; // 0x23
+    field public static final int OEM_DCFAILCAUSE_1 = 4097; // 0x1001
+    field public static final int OEM_DCFAILCAUSE_10 = 4106; // 0x100a
+    field public static final int OEM_DCFAILCAUSE_11 = 4107; // 0x100b
+    field public static final int OEM_DCFAILCAUSE_12 = 4108; // 0x100c
+    field public static final int OEM_DCFAILCAUSE_13 = 4109; // 0x100d
+    field public static final int OEM_DCFAILCAUSE_14 = 4110; // 0x100e
+    field public static final int OEM_DCFAILCAUSE_15 = 4111; // 0x100f
+    field public static final int OEM_DCFAILCAUSE_2 = 4098; // 0x1002
+    field public static final int OEM_DCFAILCAUSE_3 = 4099; // 0x1003
+    field public static final int OEM_DCFAILCAUSE_4 = 4100; // 0x1004
+    field public static final int OEM_DCFAILCAUSE_5 = 4101; // 0x1005
+    field public static final int OEM_DCFAILCAUSE_6 = 4102; // 0x1006
+    field public static final int OEM_DCFAILCAUSE_7 = 4103; // 0x1007
+    field public static final int OEM_DCFAILCAUSE_8 = 4104; // 0x1008
+    field public static final int OEM_DCFAILCAUSE_9 = 4105; // 0x1009
+    field public static final int ONLY_IPV4_ALLOWED = 50; // 0x32
+    field public static final int ONLY_IPV6_ALLOWED = 51; // 0x33
+    field public static final int ONLY_SINGLE_BEARER_ALLOWED = 52; // 0x34
+    field public static final int OPERATOR_BARRED = 8; // 0x8
+    field public static final int PDN_CONN_DOES_NOT_EXIST = 54; // 0x36
+    field public static final int PDP_WITHOUT_ACTIVE_TFT = 46; // 0x2e
+    field public static final int PREF_RADIO_TECH_CHANGED = -4; // 0xfffffffc
+    field public static final int PROTOCOL_ERRORS = 111; // 0x6f
+    field public static final int QOS_NOT_ACCEPTED = 37; // 0x25
+    field public static final int RADIO_NOT_AVAILABLE = 65537; // 0x10001
+    field public static final int RADIO_POWER_OFF = -5; // 0xfffffffb
+    field public static final int REGISTRATION_FAIL = -1; // 0xffffffff
+    field public static final int REGULAR_DEACTIVATION = 36; // 0x24
+    field public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 33; // 0x21
+    field public static final int SERVICE_OPTION_NOT_SUPPORTED = 32; // 0x20
+    field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
+    field public static final int SIGNAL_LOST = -3; // 0xfffffffd
+    field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
+    field public static final int TFT_SEMANTIC_ERROR = 41; // 0x29
+    field public static final int TFT_SYTAX_ERROR = 42; // 0x2a
+    field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27
+    field public static final int UNKNOWN = 65536; // 0x10000
+    field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63
+    field public static final int UNKNOWN_PDP_ADDRESS_TYPE = 28; // 0x1c
+    field public static final int UNKNOWN_PDP_CONTEXT = 43; // 0x2b
+    field public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 66; // 0x42
+    field public static final int USER_AUTHENTICATION = 29; // 0x1d
+  }
+
   public class DisconnectCause {
     field public static final int ALREADY_DIALING = 72; // 0x48
     field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
@@ -6010,6 +6560,18 @@
     field public static final int WIFI_LOST = 59; // 0x3b
   }
 
+  public final class LteVopsSupportInfo implements android.os.Parcelable {
+    ctor public LteVopsSupportInfo(int, int);
+    method public int describeContents();
+    method public int getEmcBearerSupport();
+    method public int getVopsSupport();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.LteVopsSupportInfo> CREATOR;
+    field public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
+    field public static final int LTE_STATUS_NOT_SUPPORTED = 3; // 0x3
+    field public static final int LTE_STATUS_SUPPORTED = 2; // 0x2
+  }
+
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
   }
@@ -6098,13 +6660,17 @@
   }
 
   public class PhoneStateListener {
+    method public void onCallAttributesChanged(android.telephony.CallAttributes);
     method public void onCallDisconnectCauseChanged(int, int);
     method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
+    method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
+    field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
     field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
+    field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
     field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
     field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
@@ -6129,6 +6695,16 @@
     field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
   }
 
+  public final class PreciseDataConnectionState implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getDataConnectionApn();
+    method public int getDataConnectionApnTypeBitMask();
+    method public int getDataConnectionFailCause();
+    method public int getDataConnectionState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
+  }
+
   public class PreciseDisconnectCause {
     field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
     field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
@@ -6264,6 +6840,7 @@
   public class SubscriptionInfo implements android.os.Parcelable {
     method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
     method public int getCardId();
+    method public int getProfileClass();
   }
 
   public class SubscriptionManager {
@@ -6273,6 +6850,11 @@
     method public void setDefaultDataSubId(int);
     method public void setDefaultSmsSubId(int);
     field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
+    field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
+    field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
+    field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
+    field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
+    field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff
     field public static final android.net.Uri VT_ENABLED_CONTENT_URI;
     field public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
     field public static final android.net.Uri WFC_MODE_CONTENT_URI;
@@ -6313,12 +6895,14 @@
     method public void dial(java.lang.String);
     method public boolean disableDataConnectivity();
     method public boolean enableDataConnectivity();
+    method public boolean enableModemForSlot(int, boolean);
     method public void enableVideoCalling(boolean);
     method public java.lang.String getAidForAppType(int);
-    method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
+    method public deprecated java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     method public int getCardIdForDefaultEuicc();
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
+    method public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
     method public java.lang.String getCdmaMdn();
     method public java.lang.String getCdmaMdn(int);
     method public java.lang.String getCdmaMin();
@@ -6331,6 +6915,7 @@
     method public deprecated boolean getDataEnabled(int);
     method public boolean getEmergencyCallbackMode();
     method public java.lang.String getIsimDomain();
+    method public java.lang.String getIsimIst();
     method public int getPreferredNetworkTypeBitmap();
     method public int getRadioPowerState();
     method public int getSimApplicationState();
@@ -6356,8 +6941,9 @@
     method public void requestCellInfoUpdate(android.os.WorkSource, java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback);
     method public void requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback);
     method public boolean resetRadioConfig();
-    method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+    method public deprecated int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method public void setCarrierDataEnabled(boolean);
+    method public int setCarrierRestrictionRules(android.telephony.CarrierRestrictionRules);
     method public void setDataActivationState(int);
     method public deprecated void setDataEnabled(int, boolean);
     method public void setDataRoamingEnabled(boolean);
@@ -6409,6 +6995,9 @@
     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 SET_CARRIER_RESTRICTION_ERROR = 2; // 0x2
+    field public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1; // 0x1
+    field public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0; // 0x0
     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
@@ -6731,12 +7320,15 @@
     method public android.os.Bundle getCallExtras();
     method public int getCallType();
     method public static int getCallTypeFromVideoState(int);
+    method public int getEmergencyCallRouting();
     method public int getEmergencyServiceCategories();
+    method public java.util.List<java.lang.String> getEmergencyUrns();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
     method public int getRestrictCause();
     method public int getServiceType();
     method public static int getVideoStateFromCallType(int);
     method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+    method public boolean isEmergencyCallTesting();
     method public boolean isVideoCall();
     method public boolean isVideoPaused();
     method public static int presentationToOir(int);
@@ -6744,7 +7336,10 @@
     method public void setCallExtraBoolean(java.lang.String, boolean);
     method public void setCallExtraInt(java.lang.String, int);
     method public void setCallRestrictCause(int);
+    method public void setEmergencyCallRouting(int);
+    method public void setEmergencyCallTesting(boolean);
     method public void setEmergencyServiceCategories(int);
+    method public void setEmergencyUrns(java.util.List<java.lang.String>);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -6817,6 +7412,7 @@
     method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
+    method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile);
     method public void callSessionRttMessageReceived(java.lang.String);
     method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionRttModifyResponseReceived(int);
@@ -6873,6 +7469,7 @@
   public class ImsMmTelManager {
     method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
     method public int getVoWiFiModeSetting();
+    method public int getVoWiFiRoamingModeSetting();
     method public boolean isAdvancedCallingSettingEnabled();
     method public boolean isAvailable(int, int);
     method public boolean isCapable(int, int);
@@ -7223,10 +7820,12 @@
     method public int describeContents();
     method public int getAudioDirection();
     method public int getAudioQuality();
+    method public boolean getRttAudioSpeech();
     method public int getRttMode();
     method public int getVideoDirection();
     method public int getVideoQuality();
     method public boolean isRttCall();
+    method public void setRttAudioSpeech(boolean);
     method public void setRttMode(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
@@ -7739,8 +8338,8 @@
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
     method public long getEventTime();
-    method public int getFlags();
     method public android.view.autofill.AutofillId getId();
+    method public java.util.List<android.view.autofill.AutofillId> getIds();
     method public java.lang.CharSequence getText();
     method public int getType();
     method public android.view.contentcapture.ViewNode getViewNode();
@@ -8044,6 +8643,8 @@
     method public abstract int getVisibleTitleHeight();
     method public abstract android.webkit.WebChromeClient getWebChromeClient();
     method public abstract android.webkit.WebViewClient getWebViewClient();
+    method public abstract android.webkit.WebViewRenderer getWebViewRenderer();
+    method public abstract android.webkit.WebViewRendererClient getWebViewRendererClient();
     method public abstract android.view.View getZoomControls();
     method public abstract void goBack();
     method public abstract void goBackOrForward(int);
@@ -8093,6 +8694,7 @@
     method public abstract void setVerticalScrollbarOverlay(boolean);
     method public abstract void setWebChromeClient(android.webkit.WebChromeClient);
     method public abstract void setWebViewClient(android.webkit.WebViewClient);
+    method public abstract void setWebViewRendererClient(java.util.concurrent.Executor, android.webkit.WebViewRendererClient);
     method public abstract boolean showFindDialog(java.lang.String, boolean);
     method public abstract void stopLoading();
     method public abstract boolean zoomBy(float);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 4beb699..5a7ec8b 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,11 @@
+package android {
+
+  public static final class Manifest.permission {
+    field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
+  }
+
+}
+
 package android.app {
 
   public class Notification implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1401cbb..0853182 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28,6 +28,7 @@
 
   public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     method public void onMovedToDisplay(int, android.content.res.Configuration);
+    method public void setInheritShowWhenLocked(boolean);
   }
 
   public class ActivityManager {
@@ -89,18 +90,26 @@
   }
 
   public class AppOpsManager {
-    method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
-    method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
+    method public void addHistoricalOps(android.app.AppOpsManager.HistoricalOps);
+    method public void clearHistory();
+    method public void getHistoricalOps(int, java.lang.String, java.lang.String[], long, long, java.util.concurrent.Executor, java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
+    method public void getHistoricalOpsFromDiskRaw(int, java.lang.String, java.lang.String[], long, long, java.util.concurrent.Executor, java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static int getNumOps();
     method public static java.lang.String[] getOpStrs();
     method public boolean isOperationActive(int, int, java.lang.String);
+    method public void offsetHistory(long);
     method public static java.lang.String opToPermission(int);
     method public static int permissionToOpCode(java.lang.String);
+    method public void resetHistoryParameters();
+    method public void setHistoryParameters(int, long, int);
     method public void setMode(int, int, java.lang.String, int);
     method public void setUidMode(java.lang.String, int, int);
     method public void startWatchingActive(int[], android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void stopWatchingActive(android.app.AppOpsManager.OnOpActiveChangedListener);
     method public static int strOpToOp(java.lang.String);
+    field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
+    field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
+    field public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2; // 0x2
     field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
     field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
@@ -144,11 +153,18 @@
     field public static final java.lang.String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
     field public static final java.lang.String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
+    field public static final int OP_COARSE_LOCATION = 0; // 0x0
     field public static final int OP_RECORD_AUDIO = 27; // 0x1b
     field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
+    field public static final int UID_STATE_BACKGROUND = 4; // 0x4
+    field public static final int UID_STATE_CACHED = 5; // 0x5
+    field public static final int UID_STATE_FOREGROUND = 3; // 0x3
+    field public static final int UID_STATE_FOREGROUND_SERVICE = 2; // 0x2
+    field public static final int UID_STATE_PERSISTENT = 0; // 0x0
+    field public static final int UID_STATE_TOP = 1; // 0x1
   }
 
-  public static final class AppOpsManager.HistoricalOpEntry implements android.os.Parcelable {
+  public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
     method public int describeContents();
     method public long getAccessCount(int);
     method public long getAccessDuration(int);
@@ -158,27 +174,57 @@
     method public long getForegroundAccessCount();
     method public long getForegroundAccessDuration();
     method public long getForegroundRejectCount();
-    method public java.lang.String getOp();
+    method public java.lang.String getOpName();
     method public long getRejectCount(int);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOpEntry> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR;
+  }
+
+  public static final class AppOpsManager.HistoricalOps implements android.os.Parcelable {
+    ctor public AppOpsManager.HistoricalOps(long, long);
+    method public int describeContents();
+    method public long getBeginTimeMillis();
+    method public long getEndTimeMillis();
+    method public int getUidCount();
+    method public android.app.AppOpsManager.HistoricalUidOps getUidOps(int);
+    method public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(int);
+    method public void increaseAccessCount(int, int, java.lang.String, int, long);
+    method public void increaseAccessDuration(int, int, java.lang.String, int, long);
+    method public void increaseRejectCount(int, int, java.lang.String, int, long);
+    method public void offsetBeginAndEndTime(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
   }
 
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
-    method public android.app.AppOpsManager.HistoricalOpEntry getEntry(java.lang.String);
-    method public android.app.AppOpsManager.HistoricalOpEntry getEntryAt(int);
-    method public int getEntryCount();
+    method public android.app.AppOpsManager.HistoricalOp getOp(java.lang.String);
+    method public android.app.AppOpsManager.HistoricalOp getOpAt(int);
+    method public int getOpCount();
     method public java.lang.String getPackageName();
-    method public int getUid();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalPackageOps> CREATOR;
   }
 
+  public static final class AppOpsManager.HistoricalUidOps implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getPackageCount();
+    method public android.app.AppOpsManager.HistoricalPackageOps getPackageOps(java.lang.String);
+    method public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(int);
+    method public int getUid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalUidOps> CREATOR;
+  }
+
   public static abstract interface AppOpsManager.OnOpActiveChangedListener {
     method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean);
   }
 
+  public final class NotificationChannel implements android.os.Parcelable {
+    method public boolean isImportanceLockedByOEM();
+    method public void setImportanceLockedByOEM(boolean);
+  }
+
   public final class NotificationChannelGroup implements android.os.Parcelable {
     method public int getUserLockedFields();
     method public void lockFields(int);
@@ -374,7 +420,9 @@
   }
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
+    field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
@@ -394,6 +442,7 @@
 package android.content.res {
 
   public final class Configuration implements java.lang.Comparable android.os.Parcelable {
+    field public int assetsSeq;
     field public final android.app.WindowConfiguration windowConfiguration;
   }
 
@@ -453,10 +502,21 @@
 
 package android.graphics {
 
+  public final class Bitmap implements android.os.Parcelable {
+    method public void eraseColor(long);
+  }
+
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
   }
 
+  public class Paint {
+    method public long getColorLong();
+    method public long getShadowLayerColorLong();
+    method public void setColor(long);
+    method public void setShadowLayer(float, float, float, long);
+  }
+
 }
 
 package android.graphics.drawable {
@@ -522,6 +582,8 @@
 
   public final class BrightnessConfiguration implements android.os.Parcelable {
     method public int describeContents();
+    method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
+    method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
     method public android.util.Pair<float[], float[]> getCurve();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -529,10 +591,22 @@
 
   public static class BrightnessConfiguration.Builder {
     ctor public BrightnessConfiguration.Builder(float[], float[]);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
     method public android.hardware.display.BrightnessConfiguration build();
+    method public int getMaxCorrectionsByCategory();
+    method public int getMaxCorrectionsByPackageName();
     method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
   }
 
+  public final class BrightnessCorrection implements android.os.Parcelable {
+    method public float apply(float);
+    method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
+  }
+
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
     method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -576,6 +650,7 @@
     method public void resetCarrierFrequencyHz();
     method public deprecated void resetCarrierPhase();
     method public deprecated void resetCarrierPhaseUncertainty();
+    method public void resetCodeType();
     method public void resetSnrInDb();
     method public void set(android.location.GnssMeasurement);
     method public void setAccumulatedDeltaRangeMeters(double);
@@ -587,6 +662,7 @@
     method public deprecated void setCarrierPhase(double);
     method public deprecated void setCarrierPhaseUncertainty(double);
     method public void setCn0DbHz(double);
+    method public void setCodeType(int);
     method public void setConstellationType(int);
     method public void setMultipathIndicator(int);
     method public void setPseudorangeRateMetersPerSecond(double);
@@ -743,6 +819,134 @@
     method public static java.io.File getStorageDirectory();
   }
 
+  public abstract class HwBinder implements android.os.IHwBinder {
+    ctor public HwBinder();
+    method public static final void configureRpcThreadpool(long, boolean);
+    method public static void enableInstrumentation();
+    method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException;
+    method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException;
+    method public static final void joinRpcThreadpool();
+    method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+    method public final void registerService(java.lang.String) throws android.os.RemoteException;
+    method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+  }
+
+  public class HwBlob {
+    ctor public HwBlob(int);
+    method public final void copyToBoolArray(long, boolean[], int);
+    method public final void copyToDoubleArray(long, double[], int);
+    method public final void copyToFloatArray(long, float[], int);
+    method public final void copyToInt16Array(long, short[], int);
+    method public final void copyToInt32Array(long, int[], int);
+    method public final void copyToInt64Array(long, long[], int);
+    method public final void copyToInt8Array(long, byte[], int);
+    method public final boolean getBool(long);
+    method public final double getDouble(long);
+    method public final float getFloat(long);
+    method public final short getInt16(long);
+    method public final int getInt32(long);
+    method public final long getInt64(long);
+    method public final byte getInt8(long);
+    method public final java.lang.String getString(long);
+    method public final long handle();
+    method public final void putBlob(long, android.os.HwBlob);
+    method public final void putBool(long, boolean);
+    method public final void putBoolArray(long, boolean[]);
+    method public final void putDouble(long, double);
+    method public final void putDoubleArray(long, double[]);
+    method public final void putFloat(long, float);
+    method public final void putFloatArray(long, float[]);
+    method public final void putInt16(long, short);
+    method public final void putInt16Array(long, short[]);
+    method public final void putInt32(long, int);
+    method public final void putInt32Array(long, int[]);
+    method public final void putInt64(long, long);
+    method public final void putInt64Array(long, long[]);
+    method public final void putInt8(long, byte);
+    method public final void putInt8Array(long, byte[]);
+    method public final void putNativeHandle(long, android.os.NativeHandle);
+    method public final void putString(long, java.lang.String);
+    method public static java.lang.Boolean[] wrapArray(boolean[]);
+    method public static java.lang.Long[] wrapArray(long[]);
+    method public static java.lang.Byte[] wrapArray(byte[]);
+    method public static java.lang.Short[] wrapArray(short[]);
+    method public static java.lang.Integer[] wrapArray(int[]);
+    method public static java.lang.Float[] wrapArray(float[]);
+    method public static java.lang.Double[] wrapArray(double[]);
+  }
+
+  public class HwParcel {
+    ctor public HwParcel();
+    method public final void enforceInterface(java.lang.String);
+    method public final boolean readBool();
+    method public final java.util.ArrayList<java.lang.Boolean> readBoolVector();
+    method public final android.os.HwBlob readBuffer(long);
+    method public final double readDouble();
+    method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
+    method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
+    method public final android.os.NativeHandle readEmbeddedNativeHandle(long, long);
+    method public final float readFloat();
+    method public final java.util.ArrayList<java.lang.Float> readFloatVector();
+    method public final short readInt16();
+    method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
+    method public final int readInt32();
+    method public final java.util.ArrayList<java.lang.Integer> readInt32Vector();
+    method public final long readInt64();
+    method public final java.util.ArrayList<java.lang.Long> readInt64Vector();
+    method public final byte readInt8();
+    method public final java.util.ArrayList<java.lang.Byte> readInt8Vector();
+    method public final android.os.NativeHandle readNativeHandle();
+    method public final java.util.ArrayList<android.os.NativeHandle> readNativeHandleVector();
+    method public final java.lang.String readString();
+    method public final java.util.ArrayList<java.lang.String> readStringVector();
+    method public final android.os.IHwBinder readStrongBinder();
+    method public final void release();
+    method public final void releaseTemporaryStorage();
+    method public final void send();
+    method public final void verifySuccess();
+    method public final void writeBool(boolean);
+    method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>);
+    method public final void writeBuffer(android.os.HwBlob);
+    method public final void writeDouble(double);
+    method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
+    method public final void writeFloat(float);
+    method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
+    method public final void writeInt16(short);
+    method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
+    method public final void writeInt32(int);
+    method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>);
+    method public final void writeInt64(long);
+    method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>);
+    method public final void writeInt8(byte);
+    method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>);
+    method public final void writeInterfaceToken(java.lang.String);
+    method public final void writeNativeHandle(android.os.NativeHandle);
+    method public final void writeNativeHandleVector(java.util.ArrayList<android.os.NativeHandle>);
+    method public final void writeStatus(int);
+    method public final void writeString(java.lang.String);
+    method public final void writeStringVector(java.util.ArrayList<java.lang.String>);
+    method public final void writeStrongBinder(android.os.IHwBinder);
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+  }
+
+  public static abstract class HwParcel.Status implements java.lang.annotation.Annotation {
+  }
+
+  public abstract interface IHwBinder {
+    method public abstract boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
+    method public abstract android.os.IHwInterface queryLocalInterface(java.lang.String);
+    method public abstract void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+    method public abstract boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient);
+  }
+
+  public static abstract interface IHwBinder.DeathRecipient {
+    method public abstract void serviceDied(long);
+  }
+
+  public abstract interface IHwInterface {
+    method public abstract android.os.IHwBinder asBinder();
+  }
+
   public class IncidentManager {
     method public void reportIncident(android.os.IncidentReportArgs);
   }
@@ -768,6 +972,18 @@
     method public void removeSyncBarrier(int);
   }
 
+  public final class NativeHandle implements java.io.Closeable {
+    ctor public NativeHandle();
+    ctor public NativeHandle(java.io.FileDescriptor, boolean);
+    ctor public NativeHandle(java.io.FileDescriptor[], int[], boolean);
+    method public void close() throws java.io.IOException;
+    method public android.os.NativeHandle dup() throws java.io.IOException;
+    method public java.io.FileDescriptor getFileDescriptor();
+    method public java.io.FileDescriptor[] getFileDescriptors();
+    method public int[] getInts();
+    method public boolean hasSingleFileDescriptor();
+  }
+
   public final class PowerManager {
     method public int getPowerSaveMode();
     method public boolean setDynamicPowerSavings(boolean, int);
@@ -778,6 +994,11 @@
 
   public class Process {
     method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
+    field public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; // 0x15f90
+    field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
+    field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7
+    field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f
+    field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64
   }
 
   public final class RemoteCallback implements android.os.Parcelable {
@@ -899,6 +1120,24 @@
     field public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR;
   }
 
+  public class VintfObject {
+    method public static java.lang.String[] getHalNamesAndVersions();
+    method public static java.lang.String getSepolicyVersion();
+    method public static java.lang.Long getTargetFrameworkCompatibilityMatrixVersion();
+    method public static java.util.Map<java.lang.String, java.lang.String[]> getVndkSnapshots();
+    method public static java.lang.String[] report();
+  }
+
+  public class VintfRuntimeInfo {
+    method public static java.lang.String getCpuInfo();
+    method public static java.lang.String getHardwareId();
+    method public static java.lang.String getKernelVersion();
+    method public static java.lang.String getNodeName();
+    method public static java.lang.String getOsName();
+    method public static java.lang.String getOsRelease();
+    method public static java.lang.String getOsVersion();
+  }
+
   public class WorkSource implements android.os.Parcelable {
     ctor public WorkSource(int);
     method public boolean add(int);
@@ -985,6 +1224,21 @@
 
 }
 
+package android.permission {
+
+  public final class PermissionControllerManager {
+    method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+    field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
+    field public static final int REASON_MALWARE = 1; // 0x1
+  }
+
+  public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback {
+    ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback();
+    method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>);
+  }
+
+}
+
 package android.print {
 
   public final class PrintJobInfo implements android.os.Parcelable {
@@ -1190,6 +1444,71 @@
 
 }
 
+package android.service.autofill.augmented {
+
+  public abstract class AugmentedAutofillService extends android.app.Service {
+    ctor public AugmentedAutofillService();
+    method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method protected void dump(java.io.PrintWriter, java.lang.String[]);
+    method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
+  }
+
+  public final class FillCallback {
+    method public void onSuccess(android.service.autofill.augmented.FillResponse);
+  }
+
+  public final class FillController {
+    method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+  }
+
+  public final class FillRequest {
+    method public android.content.ComponentName getActivityComponent();
+    method public android.view.autofill.AutofillId getFocusedId();
+    method public android.view.autofill.AutofillValue getFocusedValue();
+    method public android.service.autofill.augmented.PresentationParams getPresentationParams();
+    method public int getTaskId();
+  }
+
+  public final class FillResponse 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.service.autofill.augmented.FillResponse> CREATOR;
+  }
+
+  public static final class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.autofill.augmented.FillResponse build();
+    method public android.service.autofill.augmented.FillResponse.Builder setFillWindow(android.service.autofill.augmented.FillWindow);
+    method public android.service.autofill.augmented.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+  }
+
+  public final class FillWindow implements java.lang.AutoCloseable {
+    ctor public FillWindow();
+    method public void destroy();
+    method public boolean update(android.service.autofill.augmented.PresentationParams.Area, android.view.View, long);
+    field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+  }
+
+  public abstract class PresentationParams {
+    method public int getFlags();
+    method public android.service.autofill.augmented.PresentationParams.Area getFullArea();
+    method public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
+    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+    field public static final int FLAG_HOST_IME = 16; // 0x10
+    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+  }
+
+  public static abstract class PresentationParams.Area {
+    method public android.graphics.Rect getBounds();
+    method public android.service.autofill.augmented.PresentationParams.Area getSubArea(android.graphics.Rect);
+  }
+
+}
+
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
@@ -1219,9 +1538,9 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
-    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
+    method public void onActionInvoked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public void onNotificationDirectReply(java.lang.String);
+    method public void onNotificationDirectReplied(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
@@ -1791,6 +2110,10 @@
     ctor public AutofillId(android.view.autofill.AutofillId, int);
   }
 
+  public final class AutofillManager {
+    field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0
+  }
+
 }
 
 package android.view.inputmethod {
@@ -1801,6 +2124,33 @@
 
 }
 
+package android.view.inspector {
+
+  public abstract class InspectableNodeName implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class InspectableProperty implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class InspectableProperty.EnumMap implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class InspectableProperty.FlagMap implements java.lang.annotation.Annotation {
+  }
+
+  public static final class InspectableProperty.ValueType extends java.lang.Enum {
+    method public static android.view.inspector.InspectableProperty.ValueType valueOf(java.lang.String);
+    method public static final android.view.inspector.InspectableProperty.ValueType[] values();
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType COLOR;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType GRAVITY;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType INFERRED;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_ENUM;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_FLAG;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType NONE;
+  }
+
+}
+
 package android.widget {
 
   public abstract class AbsListView extends android.widget.AdapterView implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index abe18ba..803f83c 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -20,6 +20,7 @@
         "misc-*",
         "modernize-*",
         "readability-*",
+        "-modernize-avoid-c-arrays",
     ],
     tidy_flags: [
         "-system-headers",
@@ -38,6 +39,7 @@
         "libidmap2/CommandLineOptions.cpp",
         "libidmap2/FileUtils.cpp",
         "libidmap2/Idmap.cpp",
+        "libidmap2/Policies.cpp",
         "libidmap2/PrettyPrintVisitor.cpp",
         "libidmap2/RawPrintVisitor.cpp",
         "libidmap2/ResourceUtils.cpp",
@@ -87,6 +89,7 @@
         "tests/Idmap2BinaryTests.cpp",
         "tests/IdmapTests.cpp",
         "tests/Main.cpp",
+        "tests/PoliciesTests.cpp",
         "tests/PrettyPrintVisitorTests.cpp",
         "tests/RawPrintVisitorTests.cpp",
         "tests/ResourceUtilsTests.cpp",
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index b075673..c455ac0 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -27,17 +27,25 @@
 #include "idmap2/CommandLineOptions.h"
 #include "idmap2/FileUtils.h"
 #include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
 
 using android::ApkAssets;
 using android::idmap2::BinaryStreamVisitor;
 using android::idmap2::CommandLineOptions;
 using android::idmap2::Idmap;
+using android::idmap2::PoliciesToBitmask;
+using android::idmap2::PolicyBitmask;
+using android::idmap2::PolicyFlags;
+using android::idmap2::Result;
 using android::idmap2::utils::kIdmapFilePermissionMask;
 
 bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
   std::string target_apk_path;
   std::string overlay_apk_path;
   std::string idmap_path;
+  std::vector<std::string> policies;
+  bool ignore_overlayable;
 
   const CommandLineOptions opts =
       CommandLineOptions("idmap2 create")
@@ -47,12 +55,28 @@
           .MandatoryOption("--overlay-apk-path",
                            "input: path to apk which contains the new resource values",
                            &overlay_apk_path)
-          .MandatoryOption("--idmap-path", "output: path to where to write idmap file",
-                           &idmap_path);
+          .MandatoryOption("--idmap-path", "output: path to where to write idmap file", &idmap_path)
+          .OptionalOption("--policy",
+                          "input: an overlayable policy this overlay fulfills "
+                          "(if none or supplied, the overlay policy will default to \"public\")",
+                          &policies)
+          .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
+                        &ignore_overlayable);
   if (!opts.Parse(args, out_error)) {
     return false;
   }
 
+  PolicyBitmask fulfilled_policies = 0;
+  if (auto result = PoliciesToBitmask(policies, out_error)) {
+    fulfilled_policies |= *result;
+  } else {
+    return false;
+  }
+
+  if (fulfilled_policies == 0) {
+    fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+  }
+
   const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
   if (!target_apk) {
     out_error << "error: failed to load apk " << target_apk_path << std::endl;
@@ -66,7 +90,8 @@
   }
 
   const std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, out_error);
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           fulfilled_policies, !ignore_overlayable, out_error);
   if (!idmap) {
     return false;
   }
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 4918747..a269ee9 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -21,8 +21,11 @@
 #include <set>
 #include <sstream>
 #include <string>
+#include <utility>
 #include <vector>
 
+#include "android-base/properties.h"
+
 #include "idmap2/CommandLineOptions.h"
 #include "idmap2/FileUtils.h"
 #include "idmap2/Idmap.h"
@@ -34,11 +37,34 @@
 using android::idmap2::CommandLineOptions;
 using android::idmap2::Idmap;
 using android::idmap2::MemoryChunk;
+using android::idmap2::PoliciesToBitmask;
+using android::idmap2::PolicyBitmask;
+using android::idmap2::PolicyFlags;
+using android::idmap2::Result;
 using android::idmap2::Xml;
 using android::idmap2::ZipFile;
 using android::idmap2::utils::FindFiles;
 
 namespace {
+
+struct InputOverlay {
+  bool operator<(InputOverlay const& rhs) const {
+    return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path);
+  }
+
+  std::string apk_path;               // NOLINT(misc-non-private-member-variables-in-classes)
+  std::string idmap_path;             // NOLINT(misc-non-private-member-variables-in-classes)
+  int priority;                       // NOLINT(misc-non-private-member-variables-in-classes)
+  std::vector<std::string> policies;  // NOLINT(misc-non-private-member-variables-in-classes)
+  bool ignore_overlayable;            // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
+bool VendorIsQOrLater() {
+  // STOPSHIP(b/119390857): Check api version once Q sdk version is finalized
+  std::string version = android::base::GetProperty("ro.vndk.version", "Q");
+  return version == "Q" || version == "q";
+}
+
 std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs,
                                                        bool recursive, std::ostream& out_error) {
   const auto predicate = [](unsigned char type, const std::string& path) -> bool {
@@ -58,6 +84,23 @@
   }
   return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
 }
+
+PolicyBitmask PolicyForPath(const std::string& apk_path) {
+  static const std::vector<std::pair<std::string, PolicyBitmask>> values = {
+      {"/product/", PolicyFlags::POLICY_PRODUCT_PARTITION},
+      {"/system/", PolicyFlags::POLICY_SYSTEM_PARTITION},
+      {"/vendor/", PolicyFlags::POLICY_VENDOR_PARTITION},
+  };
+
+  for (auto const& pair : values) {
+    if (apk_path.compare(0, pair.first.size(), pair.first) == 0) {
+      return pair.second | PolicyFlags::POLICY_PUBLIC;
+    }
+  }
+
+  return PolicyFlags::POLICY_PUBLIC;
+}
+
 }  // namespace
 
 bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
@@ -65,6 +108,7 @@
   std::string target_package_name;
   std::string target_apk_path;
   std::string output_directory;
+  std::vector<std::string> override_policies;
   bool recursive = false;
 
   const CommandLineOptions opts =
@@ -77,7 +121,12 @@
           .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
           .MandatoryOption("--output-directory",
                            "directory in which to write artifacts (idmap files and overlays.list)",
-                           &output_directory);
+                           &output_directory)
+          .OptionalOption(
+              "--override-policy",
+              "input: an overlayable policy this overlay fulfills "
+              "(if none or supplied, the overlays will not have their policies overriden",
+              &override_policies);
   if (!opts.Parse(args, out_error)) {
     return false;
   }
@@ -87,7 +136,7 @@
     return false;
   }
 
-  std::vector<std::string> interesting_apks;
+  std::vector<InputOverlay> interesting_apks;
   for (const std::string& path : *apk_paths) {
     std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
     if (!zip) {
@@ -132,27 +181,63 @@
       continue;
     }
 
+    PolicyBitmask fulfilled_policies;
+    if (!override_policies.empty()) {
+      if (Result<PolicyBitmask> result = PoliciesToBitmask(override_policies, out_error)) {
+        fulfilled_policies = *result;
+      } else {
+        return false;
+      }
+    } else {
+      fulfilled_policies = PolicyForPath(path);
+    }
+
+    bool ignore_overlayable = false;
+    if ((fulfilled_policies & PolicyFlags::POLICY_VENDOR_PARTITION) != 0 && !VendorIsQOrLater()) {
+      // If the overlay is on a pre-Q vendor partition, do not enforce overlayable
+      // restrictions on this overlay because the pre-Q platform has no understanding of
+      // overlayable.
+      ignore_overlayable = true;
+    }
+
+    std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);
+
+    // Sort the static overlays in ascending priority order
+    InputOverlay input{path, idmap_path, priority, override_policies, ignore_overlayable};
     interesting_apks.insert(
-        std::lower_bound(interesting_apks.begin(), interesting_apks.end(), path), path);
+        std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
   }
 
   std::stringstream stream;
-  for (const auto& apk : interesting_apks) {
-    const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, apk);
+  for (const auto& overlay : interesting_apks) {
+    // Create the idmap for the overlay if it currently does not exist or if it is not up to date.
     std::stringstream dev_null;
-    if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), dev_null) &&
-        !Create(std::vector<std::string>({
-                    "--target-apk-path",
-                    target_apk_path,
-                    "--overlay-apk-path",
-                    apk,
-                    "--idmap-path",
-                    idmap_path,
-                }),
-                out_error)) {
-      return false;
+
+    std::vector<std::string> verify_args = {"--idmap-path", overlay.idmap_path};
+    for (const std::string& policy : overlay.policies) {
+      verify_args.emplace_back("--policy");
+      verify_args.emplace_back(policy);
     }
-    stream << idmap_path << std::endl;
+
+    if (!Verify(std::vector<std::string>(verify_args), dev_null)) {
+      std::vector<std::string> create_args = {"--target-apk-path",  target_apk_path,
+                                              "--overlay-apk-path", overlay.apk_path,
+                                              "--idmap-path",       overlay.idmap_path};
+      if (overlay.ignore_overlayable) {
+        create_args.emplace_back("--ignore-overlayable");
+      }
+
+      for (const std::string& policy : overlay.policies) {
+        verify_args.emplace_back("--policy");
+        verify_args.emplace_back(policy);
+      }
+
+      if (!Create(create_args, out_error)) {
+        return false;
+      }
+    }
+
+    stream << overlay.idmap_path << std::endl;
   }
 
   std::cout << stream.str();
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index d2e46e1..a3c7527 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -26,12 +26,15 @@
 #include <string>
 
 #include "android-base/macros.h"
+#include "android-base/stringprintf.h"
 #include "utils/String8.h"
 #include "utils/Trace.h"
 
 #include "idmap2/BinaryStreamVisitor.h"
 #include "idmap2/FileUtils.h"
 #include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
 
 #include "idmap2d/Idmap2Service.h"
 
@@ -39,6 +42,8 @@
 using android::idmap2::BinaryStreamVisitor;
 using android::idmap2::Idmap;
 using android::idmap2::IdmapHeader;
+using android::idmap2::PolicyBitmask;
+using android::idmap2::Result;
 using android::idmap2::utils::kIdmapFilePermissionMask;
 
 namespace {
@@ -54,6 +59,10 @@
   return Status::fromExceptionCode(Status::EX_NONE, msg.c_str());
 }
 
+PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
+  return static_cast<PolicyBitmask>(arg);
+}
+
 }  // namespace
 
 namespace android::os {
@@ -78,6 +87,8 @@
 }
 
 Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
+                                  int32_t fulfilled_policies ATTRIBUTE_UNUSED,
+                                  bool enforce_overlayable ATTRIBUTE_UNUSED,
                                   int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
   assert(_aidl_return);
   const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
@@ -86,11 +97,15 @@
   fin.close();
   std::stringstream dev_null;
   *_aidl_return = header && header->IsUpToDate(dev_null);
+
+  // TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed
+
   return ok();
 }
 
 Status Idmap2Service::createIdmap(const std::string& target_apk_path,
-                                  const std::string& overlay_apk_path, int32_t user_id,
+                                  const std::string& overlay_apk_path, int32_t fulfilled_policies,
+                                  bool enforce_overlayable, int32_t user_id,
                                   std::unique_ptr<std::string>* _aidl_return) {
   assert(_aidl_return);
   std::stringstream trace;
@@ -101,6 +116,8 @@
 
   _aidl_return->reset(nullptr);
 
+  const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
+
   const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
   if (!target_apk) {
     return error("failed to load apk " + target_apk_path);
@@ -113,7 +130,8 @@
 
   std::stringstream err;
   const std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, err);
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           policy_bitmask, enforce_overlayable, err);
   if (!idmap) {
     return error(err.str());
   }
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index e0bc22e..1aab059 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -39,11 +39,12 @@
   binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
                              bool* _aidl_return);
 
-  binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id,
-                             bool* _aidl_return);
+  binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t fulfilled_policies,
+                             bool enforce_overlayable, int32_t user_id, bool* _aidl_return);
 
   binder::Status createIdmap(const std::string& target_apk_path,
-                             const std::string& overlay_apk_path, int32_t user_id,
+                             const std::string& overlay_apk_path, int32_t fulfilled_policies,
+                             bool enforce_overlayable, int32_t user_id,
                              std::unique_ptr<std::string>* _aidl_return);
 };
 
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index d475417..ea7274f 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -20,9 +20,18 @@
  * @hide
  */
 interface IIdmap2 {
+  const int POLICY_PUBLIC = 0x00000001;
+  const int POLICY_SYSTEM_PARTITION = 0x00000002;
+  const int POLICY_VENDOR_PARTITION = 0x00000004;
+  const int POLICY_PRODUCT_PARTITION = 0x00000008;
+
   @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
   boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
-  boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId);
+  boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies,
+                      boolean enforceOverlayable, int userId);
   @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
-                                          @utf8InCpp String overlayApkPath, int userId);
+                                          @utf8InCpp String overlayApkPath,
+                                          int fulfilledPolicies,
+                                          boolean enforceOverlayable,
+                                          int userId);
 }
diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h
index b93e716..6db6bf9 100644
--- a/cmds/idmap2/include/idmap2/CommandLineOptions.h
+++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h
@@ -44,6 +44,8 @@
                                       std::vector<std::string>* value);
   CommandLineOptions& OptionalOption(const std::string& name, const std::string& description,
                                      std::string* value);
+  CommandLineOptions& OptionalOption(const std::string& name, const std::string& description,
+                                     std::vector<std::string>* value);
   bool Parse(const std::vector<std::string>& argv, std::ostream& outError) const;
   void Usage(std::ostream& out) const;
 
@@ -56,6 +58,7 @@
       COUNT_OPTIONAL,
       COUNT_EXACTLY_ONCE,
       COUNT_ONCE_OR_MORE,
+      COUNT_OPTIONAL_ONCE_OR_MORE,
     } count;
     bool argument;
   };
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index b989e4c..1666dc8 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -57,6 +57,8 @@
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/StringPiece.h"
 
+#include "idmap2/Policies.h"
+
 namespace android::idmap2 {
 
 class Idmap;
@@ -233,11 +235,10 @@
   // file is used; change this in the next version of idmap to use a named
   // package instead; also update FromApkAssets to take additional parameters:
   // the target and overlay package names
-  static std::unique_ptr<const Idmap> FromApkAssets(const std::string& target_apk_path,
-                                                    const ApkAssets& target_apk_assets,
-                                                    const std::string& overlay_apk_path,
-                                                    const ApkAssets& overlay_apk_assets,
-                                                    std::ostream& out_error);
+  static std::unique_ptr<const Idmap> FromApkAssets(
+      const std::string& target_apk_path, const ApkAssets& target_apk_assets,
+      const std::string& overlay_apk_path, const ApkAssets& overlay_apk_assets,
+      const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, std::ostream& out_error);
 
   inline const std::unique_ptr<const IdmapHeader>& GetHeader() const {
     return header_;
diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/include/idmap2/Policies.h
new file mode 100644
index 0000000..eecee25
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Policies.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 <ostream>
+#include <string>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+
+#include "Result.h"
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
+#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
+
+namespace android::idmap2 {
+
+using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags;
+using PolicyBitmask = uint32_t;
+
+// Parses a the string representation of a set of policies into a bitmask. The format of the string
+// is the same as for the <policy> element.
+Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies,
+                                        std::ostream& err);
+
+}  // namespace android::idmap2
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index cabc8f3..a49a607 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -68,9 +68,18 @@
   return *this;
 }
 
+CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
+                                                       const std::string& description,
+                                                       std::vector<std::string>* value) {
+  assert(value != nullptr);
+  auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
+  options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
+  return *this;
+}
+
 bool CommandLineOptions::Parse(const std::vector<std::string>& argv, std::ostream& outError) const {
   const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
-    return opt.count != Option::COUNT_OPTIONAL;
+    return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
   });
   std::set<std::string> mandatory_opts;
   std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
@@ -122,7 +131,8 @@
   size_t maxLength = 0;
   out << "usage: " << name_;
   for (const Option& opt : options_) {
-    const bool mandatory = opt.count != Option::COUNT_OPTIONAL;
+    const bool mandatory =
+        opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
     out << " ";
     if (!mandatory) {
       out << "[";
@@ -134,9 +144,15 @@
       out << opt.name;
       maxLength = std::max(maxLength, opt.name.size());
     }
+
+    if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
+      out << " [..]";
+    }
+
     if (!mandatory) {
       out << "]";
     }
+
     if (opt.count == Option::COUNT_ONCE_OR_MORE) {
       out << " [" << opt.name << " arg [..]]";
     }
@@ -150,7 +166,8 @@
       out << opt.name;
     }
     out << "    " << opt.description;
-    if (opt.count == Option::COUNT_ONCE_OR_MORE) {
+    if (opt.count == Option::COUNT_ONCE_OR_MORE ||
+        opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
       out << " (can be provided multiple times)";
     }
     out << std::endl;
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 37d6af8..2890ae1 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -274,11 +274,23 @@
   return std::move(idmap);
 }
 
-std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_path,
-                                                  const ApkAssets& target_apk_assets,
-                                                  const std::string& overlay_apk_path,
-                                                  const ApkAssets& overlay_apk_assets,
-                                                  std::ostream& out_error) {
+bool CheckOverlayable(const LoadedPackage& target_package, PolicyBitmask fulfilled_polices,
+                      ResourceId resid) {
+  const OverlayableInfo* info = target_package.GetOverlayableInfo(resid);
+  if (info == nullptr) {
+    // If the resource does not have an overlayable definition, allow the resource to be overlaid.
+    // Once overlayable enforcement is turned on, this check will return false.
+    return true;
+  }
+
+  // Enforce policy restrictions if the resource is declared as overlayable.
+  return (info->policy_flags & fulfilled_polices) != 0;
+}
+
+std::unique_ptr<const Idmap> Idmap::FromApkAssets(
+    const std::string& target_apk_path, const ApkAssets& target_apk_assets,
+    const std::string& overlay_apk_path, const ApkAssets& overlay_apk_assets,
+    const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, std::ostream& out_error) {
   AssetManager2 target_asset_manager;
   if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
     out_error << "error: failed to create target asset manager" << std::endl;
@@ -380,6 +392,15 @@
     if (target_resid == 0) {
       continue;
     }
+
+    if (enforce_overlayable && !CheckOverlayable(*target_pkg, fulfilled_policies, target_resid)) {
+      // The resources must be defined as overlayable and the overlay must fulfill at least one
+      // policy enforced on the overlayable resource
+      LOG(WARNING) << "overlay \"" << overlay_apk_path << "\" is not allowed to overlay resource \""
+                   << full_name << "\"" << std::endl;
+      continue;
+    }
+
     matching_resources.Add(target_resid, overlay_resid);
   }
 
diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp
new file mode 100644
index 0000000..0f87ef0
--- /dev/null
+++ b/cmds/idmap2/libidmap2/Policies.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 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 <iterator>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
+
+namespace android::idmap2 {
+
+namespace {
+
+const std::map<android::StringPiece, PolicyFlags> kStringToFlag = {
+    {"public", PolicyFlags::POLICY_PUBLIC},
+    {"product", PolicyFlags::POLICY_PRODUCT_PARTITION},
+    {"system", PolicyFlags::POLICY_SYSTEM_PARTITION},
+    {"vendor", PolicyFlags::POLICY_VENDOR_PARTITION},
+};
+}  // namespace
+
+Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies,
+                                        std::ostream& err) {
+  PolicyBitmask bitmask = 0;
+  for (const std::string& policy : policies) {
+    const auto iter = kStringToFlag.find(policy);
+    if (iter != kStringToFlag.end()) {
+      bitmask |= iter->second;
+    } else {
+      err << "error: unknown policy \"" << policy << "\"";
+      return kResultError;
+    }
+  }
+
+  return Result<PolicyBitmask>(bitmask);
+}
+
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index ec2decf..b78e942 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -89,6 +89,7 @@
   }
 }
 
+// NOLINTNEXTLINE(cert-dcl50-cpp)
 void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
@@ -101,6 +102,7 @@
   offset_ += sizeof(uint16_t);
 }
 
+// NOLINTNEXTLINE(cert-dcl50-cpp)
 void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
@@ -113,6 +115,7 @@
   offset_ += sizeof(uint32_t);
 }
 
+// NOLINTNEXTLINE(cert-dcl50-cpp)
 void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh
index 560ccb6..ad9830b 100755
--- a/cmds/idmap2/static-checks.sh
+++ b/cmds/idmap2/static-checks.sh
@@ -70,7 +70,15 @@
 function _cpplint()
 {
     local cpplint="${ANDROID_BUILD_TOP}/tools/repohooks/tools/cpplint.py"
-    $cpplint --quiet $cpp_files
+    local output="$($cpplint --quiet $cpp_files 2>&1 >/dev/null | grep -v \
+        -e 'Found C system header after C++ system header.' \
+        -e 'Unknown NOLINT error category: misc-non-private-member-variables-in-classes' \
+    )"
+    if [[ "$output" ]]; then
+        echo "$output"
+        return 1
+    fi
+    return 0
 }
 
 function _parse_args()
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 2698ac0..35ec1ff 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -78,7 +78,8 @@
 
   std::stringstream error;
   std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
   ASSERT_THAT(idmap, NotNull());
 
   std::stringstream stream;
@@ -101,25 +102,52 @@
   header = loaded_idmap->GetEntryMapForType(0x02);
   ASSERT_THAT(header, NotNull());
 
-  success = LoadedIdmap::Lookup(header, 0x0002, &entry);
+  success = LoadedIdmap::Lookup(header, 0x0000, &entry);  // string/a
   ASSERT_FALSE(success);
 
-  success = LoadedIdmap::Lookup(header, 0x0003, &entry);
+  success = LoadedIdmap::Lookup(header, 0x0001, &entry);  // string/b
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0002, &entry);  // string/c
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0003, &entry);  // string/not_overlayable
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0004, &entry);  // string/policy_product
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0005, &entry);  // string/policy_public
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0006, &entry);  // string/policy_system
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0007, &entry);  // string/policy_system_vendor
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0008, &entry);  // string/str1
   ASSERT_TRUE(success);
   ASSERT_EQ(entry, 0x0000);
 
-  success = LoadedIdmap::Lookup(header, 0x0004, &entry);
+  success = LoadedIdmap::Lookup(header, 0x0009, &entry);  // string/str2
   ASSERT_FALSE(success);
 
-  success = LoadedIdmap::Lookup(header, 0x0005, &entry);
+  success = LoadedIdmap::Lookup(header, 0x000a, &entry);  // string/str3
   ASSERT_TRUE(success);
   ASSERT_EQ(entry, 0x0001);
 
-  success = LoadedIdmap::Lookup(header, 0x0006, &entry);
+  success = LoadedIdmap::Lookup(header, 0x000b, &entry);  // string/str4
   ASSERT_TRUE(success);
   ASSERT_EQ(entry, 0x0002);
 
-  success = LoadedIdmap::Lookup(header, 0x0007, &entry);
+  success = LoadedIdmap::Lookup(header, 0x000c, &entry);  // string/x
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x000d, &entry);  // string/y
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x000e, &entry);  // string/z
   ASSERT_FALSE(success);
 }
 
diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
index c27d27a..39f18d3 100644
--- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp
+++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
@@ -121,6 +121,56 @@
   ASSERT_FALSE(success);
 }
 
+TEST(CommandLineOptionsTests, OptionalOptionList) {
+  std::vector<std::string> foo;
+  std::vector<std::string> bar;
+  CommandLineOptions opts = CommandLineOptions("test")
+                                .OptionalOption("--foo", "", &foo)
+                                .OptionalOption("--bar", "", &bar);
+  std::ostream fakeStdErr(nullptr);
+  bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(foo.size(), 1U);
+  ASSERT_EQ(foo[0], "FOO");
+  ASSERT_EQ(bar.size(), 1U);
+  ASSERT_EQ(bar[0], "BAR");
+
+  foo.clear();
+  bar.clear();
+  success = opts.Parse({"--foo", "BAZ"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(foo.size(), 1U);
+  ASSERT_EQ(foo[0], "BAZ");
+  ASSERT_EQ(bar.size(), 0U);
+
+  foo.clear();
+  bar.clear();
+  success =
+      opts.Parse({"--foo", "BAZ", "--foo", "BIZ", "--bar", "FIZ", "--bar", "FUZZ"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(foo.size(), 2U);
+  ASSERT_EQ(foo[0], "BAZ");
+  ASSERT_EQ(foo[1], "BIZ");
+  ASSERT_EQ(bar.size(), 2U);
+  ASSERT_EQ(bar[0], "FIZ");
+  ASSERT_EQ(bar[1], "FUZZ");
+
+  foo.clear();
+  bar.clear();
+  success = opts.Parse({"--foo"}, fakeStdErr);
+  ASSERT_FALSE(success);
+
+  foo.clear();
+  bar.clear();
+  success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr);
+  ASSERT_FALSE(success);
+
+  foo.clear();
+  bar.clear();
+  success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr);
+  ASSERT_FALSE(success);
+}
+
 TEST(CommandLineOptionsTests, CornerCases) {
   std::string foo;
   std::string bar;
@@ -172,6 +222,7 @@
   bool arg5 = false;
   bool arg6 = false;
   std::vector<std::string> arg7;
+  std::vector<std::string> arg8;
   CommandLineOptions opts = CommandLineOptions("test")
                                 .MandatoryOption("--aa", "description-aa", &arg1)
                                 .OptionalFlag("--bb", "description-bb", &arg5)
@@ -179,12 +230,13 @@
                                 .OptionalOption("--dd", "description-dd", &arg3)
                                 .MandatoryOption("--ee", "description-ee", &arg4)
                                 .OptionalFlag("--ff", "description-ff", &arg6)
-                                .MandatoryOption("--gg", "description-gg", &arg7);
+                                .MandatoryOption("--gg", "description-gg", &arg7)
+                                .OptionalOption("--hh", "description-hh", &arg8);
   std::stringstream stream;
   opts.Usage(stream);
   const std::string s = stream.str();
   ASSERT_NE(s.find("usage: test --aa arg [--bb] [--cc arg] [--dd arg] --ee arg [--ff] --gg arg "
-                   "[--gg arg [..]]"),
+                   "[--gg arg [..]] [--hh arg [..]]"),
             std::string::npos);
   ASSERT_NE(s.find("--aa arg    description-aa"), std::string::npos);
   ASSERT_NE(s.find("--ff        description-ff"), std::string::npos);
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 4bf832a..d9d9a7f 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -37,10 +37,10 @@
                             [](unsigned char type ATTRIBUTE_UNUSED,
                                const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
   ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 4U);
-  ASSERT_EQ(
-      std::set<std::string>(v->begin(), v->end()),
-      std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"}));
+  ASSERT_EQ(v->size(), 6U);
+  ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
+            std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target",
+                                   root + "/system-overlay", root + "/system-overlay-invalid"}));
 }
 
 TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
@@ -49,11 +49,13 @@
     return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
   });
   ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 4U);
+  ASSERT_EQ(v->size(), 6U);
   ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
             std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
                                    root + "/overlay/overlay-static-1.apk",
-                                   root + "/overlay/overlay-static-2.apk"}));
+                                   root + "/overlay/overlay-static-2.apk",
+                                   root + "/system-overlay/system-overlay.apk",
+                                   root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
 }
 
 TEST(FileUtilsTests, ReadFile) {
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 22f48e9..0c8f164 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -38,6 +38,7 @@
 #include "gtest/gtest.h"
 
 #include "androidfw/PosixUtils.h"
+
 #include "idmap2/FileUtils.h"
 #include "idmap2/Idmap.h"
 
@@ -114,8 +115,9 @@
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
   ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f020003 -> 0x7f020000 string/str1"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f020005 -> 0x7f020001 string/str3"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f020008 -> 0x7f020000 string/str1"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020001 string/str3"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020002 string/str4"), std::string::npos);
   ASSERT_EQ(result->stdout.find("00000210:     007f  target package id"), std::string::npos);
 
   // clang-format off
@@ -157,7 +159,8 @@
                                "--recursive",
                                "--target-package-name", "test.target",
                                "--target-apk-path", GetTargetApkPath(),
-                               "--output-directory", GetTempDirPath()});
+                               "--output-directory", GetTempDirPath(),
+                               "--override-policy", "public"});
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -190,7 +193,8 @@
                           "--input-directory", GetTestDataPath() + "/overlay",
                           "--target-package-name", "test.target",
                           "--target-apk-path", GetTargetApkPath(),
-                          "--output-directory", GetTempDirPath()});
+                          "--output-directory", GetTempDirPath(),
+                          "--override-policy", "public"});
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -207,7 +211,8 @@
                           "--recursive",
                           "--target-package-name", "test.target",
                           "--target-apk-path", GetTargetApkPath(),
-                          "--output-directory", GetTempDirPath()});
+                          "--output-directory", GetTempDirPath(),
+                          "--override-policy", "public"});
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -222,7 +227,8 @@
                           "--input-directory", GetTempDirPath(),
                           "--target-package-name", "test.target",
                           "--target-apk-path", GetTargetApkPath(),
-                          "--output-directory", GetTempDirPath()});
+                          "--output-directory", GetTempDirPath(),
+                          "--override-policy", "public"});
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -245,7 +251,7 @@
                           "lookup",
                           "--idmap-path", GetIdmapPath(),
                           "--config", "",
-                          "--resid", "0x7f020003"});  // string/str1
+                          "--resid", "0x7f020008"});  // string/str1
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -310,6 +316,18 @@
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_NE(result->status, EXIT_SUCCESS);
+
+  // unknown policy
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "create",
+                          "--target-apk-path", GetTargetApkPath(),
+                          "--overlay-apk-path", GetOverlayApkPath(),
+                          "--idmap-path", GetIdmapPath(),
+                          "--policy", "this-does-not-exist"});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_NE(result->status, EXIT_SUCCESS);
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 963f22e..c6eb71c 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -184,13 +184,14 @@
 
   std::stringstream error;
   std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
   ASSERT_THAT(idmap, NotNull());
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
   ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
-  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xca2093da);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
   ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
@@ -216,13 +217,127 @@
   ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetEntryCount(), 4U);
-  ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 8U);
   ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
   ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
   ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
   ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
 }
 
+TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/system-overlay/system-overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+                           /* enforce_overlayable */ true, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1U);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 1U);
+
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 3U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 5U);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);  // string/policy_public
+  ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_system_vendor
+}
+
+TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() +
+                                     "/system-overlay-invalid/system-overlay-invalid.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+                           /* enforce_overlayable */ true, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1U);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 1U);
+
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 5U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);   // string/not_overlayable
+  ASSERT_EQ(types[0]->GetEntry(1), kNoEntry);  // string/policy_product
+  ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);   // string/policy_public
+  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);   // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);   // string/policy_system_vendor
+}
+
+TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() +
+                                     "/system-overlay-invalid/system-overlay-invalid.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+                           /* enforce_overlayable */ false, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1U);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 1U);
+
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 5U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);  // string/not_overlayable
+  ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/policy_product
+  ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_public
+  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);  // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);  // string/policy_system_vendor
+}
+
 TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
   std::string target_apk_path(GetTestDataPath());
   for (int i = 0; i < 32; i++) {
@@ -239,7 +354,8 @@
 
   std::stringstream error;
   std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
   ASSERT_THAT(idmap, IsNull());
 }
 
@@ -255,8 +371,9 @@
   ASSERT_THAT(overlay_apk, NotNull());
 
   std::stringstream error;
-  std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+  std::unique_ptr<const Idmap> idmap = Idmap::FromApkAssets(
+      target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+      /* enforce_overlayable */ true, error);
   ASSERT_THAT(idmap, NotNull());
 
   std::stringstream stream;
diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp
new file mode 100644
index 0000000..ab567ad
--- /dev/null
+++ b/cmds/idmap2/tests/PoliciesTests.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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 <string>
+
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+#include "idmap2/Policies.h"
+
+using android::idmap2::PolicyBitmask;
+using android::idmap2::PolicyFlags;
+
+namespace android::idmap2 {
+
+TEST(PoliciesTests, PoliciesToBitmasks) {
+  const Result<PolicyBitmask> bitmask1 = PoliciesToBitmask({"system"}, std::cerr);
+  ASSERT_NE(bitmask1, kResultError);
+  ASSERT_EQ(bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION);
+
+  const Result<PolicyBitmask> bitmask2 = PoliciesToBitmask({"system", "vendor"}, std::cerr);
+  ASSERT_NE(bitmask2, kResultError);
+  ASSERT_EQ(bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+
+  const Result<PolicyBitmask> bitmask3 = PoliciesToBitmask({"vendor", "system"}, std::cerr);
+  ASSERT_NE(bitmask3, kResultError);
+  ASSERT_EQ(bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+
+  const Result<PolicyBitmask> bitmask4 =
+      PoliciesToBitmask({"public", "product", "system", "vendor"}, std::cerr);
+  ASSERT_NE(bitmask4, kResultError);
+  ASSERT_EQ(bitmask4, PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
+                          PolicyFlags::POLICY_SYSTEM_PARTITION |
+                          PolicyFlags::POLICY_VENDOR_PARTITION);
+
+  const Result<PolicyBitmask> bitmask5 =
+      PoliciesToBitmask({"system", "system", "system"}, std::cerr);
+  ASSERT_NE(bitmask5, kResultError);
+  ASSERT_EQ(bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION);
+
+  const Result<PolicyBitmask> bitmask6 = PoliciesToBitmask({""}, std::cerr);
+  ASSERT_EQ(bitmask6, kResultError);
+
+  const Result<PolicyBitmask> bitmask7 = PoliciesToBitmask({"foo"}, std::cerr);
+  ASSERT_EQ(bitmask7, kResultError);
+
+  const Result<PolicyBitmask> bitmask8 = PoliciesToBitmask({"system", "foo"}, std::cerr);
+  ASSERT_EQ(bitmask8, kResultError);
+
+  const Result<PolicyBitmask> bitmask9 = PoliciesToBitmask({"system", ""}, std::cerr);
+  ASSERT_EQ(bitmask9, kResultError);
+
+  const Result<PolicyBitmask> bitmask10 = PoliciesToBitmask({"system "}, std::cerr);
+  ASSERT_EQ(bitmask10, kResultError);
+}
+
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index 7736bc0..eaa47cd 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -25,6 +25,7 @@
 #include "androidfw/Idmap.h"
 
 #include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
 #include "idmap2/PrettyPrintVisitor.h"
 
 #include "TestHelpers.h"
@@ -32,6 +33,7 @@
 using ::testing::NotNull;
 
 using android::ApkAssets;
+using android::idmap2::PolicyBitmask;
 
 namespace android::idmap2 {
 
@@ -46,7 +48,8 @@
 
   std::stringstream error;
   std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
   ASSERT_THAT(idmap, NotNull());
 
   std::stringstream stream;
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 0318cd2..b58c61a 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -42,7 +42,8 @@
 
   std::stringstream error;
   std::unique_ptr<const Idmap> idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
+                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
   ASSERT_THAT(idmap, NotNull());
 
   std::stringstream stream;
@@ -51,7 +52,7 @@
 
   ASSERT_NE(stream.str().find("00000000: 504d4449  magic\n"), std::string::npos);
   ASSERT_NE(stream.str().find("00000004: 00000001  version\n"), std::string::npos);
-  ASSERT_NE(stream.str().find("00000008: f5ad1d1d  target crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("00000008: ca2093da  target crc\n"), std::string::npos);
   ASSERT_NE(stream.str().find("0000000c: d470336b  overlay crc\n"), std::string::npos);
   ASSERT_NE(stream.str().find("0000021c: 00000000  0x7f010000 -> 0x7f010000 integer/int1\n"),
             std::string::npos);
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml
new file mode 100644
index 0000000..977cd97
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="test.overlay.system.invalid">
+    <overlay
+        android:targetPackage="test.target" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/build b/cmds/idmap2/tests/data/system-overlay-invalid/build
new file mode 100644
index 0000000..920e1f8
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/build
@@ -0,0 +1,26 @@
+# Copyright (C) 2019 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.
+
+FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+
+aapt2 compile --dir res -o compiled.flata
+
+aapt2 link \
+    --no-resource-removal \
+    -I "$FRAMEWORK_RES_APK" \
+    --manifest AndroidManifest.xml \
+    -o system-overlay-invalid.apk \
+    compiled.flata
+
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
new file mode 100644
index 0000000..5127707
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- This overlay will fulfill the policies "public|system". This allows it overlay the
+         following resources. -->
+    <string name="policy_system">policy_system</string>
+    <string name="policy_system_vendor">policy_system_vendor</string>
+    <string name="policy_public">policy_public</string>
+
+    <!-- It also requests to overlay a resource that belongs to a policy the overlay does not
+         fulfill.-->
+    <string name="policy_product">policy_product</string>
+
+    <!-- It also requests to overlay a resource that is not declared as overlayable.-->
+    <string name="not_overlayable">not_overlayable</string>
+</resources>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
new file mode 100644
index 0000000..c367f82
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml
new file mode 100644
index 0000000..8af9064
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        package="test.overlay.system">
+    <overlay
+            android:targetPackage="test.target" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/system-overlay/build b/cmds/idmap2/tests/data/system-overlay/build
new file mode 100644
index 0000000..be0d239
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay/build
@@ -0,0 +1,26 @@
+# Copyright (C) 2019 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.
+
+FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+
+aapt2 compile --dir res -o compiled.flata
+
+aapt2 link \
+    --no-resource-removal \
+    -I "$FRAMEWORK_RES_APK" \
+    --manifest AndroidManifest.xml \
+    -o system-overlay.apk \
+    compiled.flata
+
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/system-overlay/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay/res/values/values.xml
new file mode 100644
index 0000000..6aaa0b0
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay/res/values/values.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- This overlay will fulfill the policies "public|system". This allows it overlay the
+     following resources. -->
+    <string name="policy_system">policy_system</string>
+    <string name="policy_system_vendor">policy_system_vendor</string>
+    <string name="policy_public">policy_public</string>
+</resources>
diff --git a/cmds/idmap2/tests/data/system-overlay/system-overlay.apk b/cmds/idmap2/tests/data/system-overlay/system-overlay.apk
new file mode 100644
index 0000000..90f30eb
--- /dev/null
+++ b/cmds/idmap2/tests/data/system-overlay/system-overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
new file mode 100644
index 0000000..de19e6f
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+<overlayable name="TestResources">
+    <!-- Publicly overlayable resources -->
+    <item type="string" name="a" />
+    <item type="string" name="b" />
+    <item type="string" name="c" />
+    <item type="string" name="str1" />
+    <item type="string" name="str2" />
+    <item type="string" name="str3" />
+    <item type="string" name="str4" />
+    <item type="string" name="x" />
+    <item type="string" name="y" />
+    <item type="string" name="z" />
+    <item type="integer" name="int1" />
+
+    <!-- Resources with partition restrictins -->
+    <policy type="system">
+        <item type="string" name="policy_system" />
+    </policy>
+
+    <policy type="system|vendor">
+        <item type="string" name="policy_system_vendor" />
+    </policy>
+
+    <policy type="product">
+        <item type="string" name="policy_product" />
+    </policy>
+
+    <policy type="public">
+        <item type="string" name="policy_public" />
+    </policy>
+</overlayable>
+</resources>
\ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index 56bf0d6..ef9012e 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -25,4 +25,12 @@
     <string name="y">y</string>
     <string name="z">z</string>
     <integer name="int1">1</integer>
+
+    <!-- This resources is not marked as overlayable -->
+    <string name="not_overlayable">not_overlayable</string>
+
+    <string name="policy_system">policy_system</string>
+    <string name="policy_system_vendor">policy_system_vendor</string>
+    <string name="policy_product">policy_product</string>
+    <string name="policy_public">policy_public</string>
 </resources>
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 18ecc27..9a6220d 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 74edffb..0c861cf 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -16,15 +16,20 @@
 
 package com.android.commands.input;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.hardware.input.InputManager;
 import android.os.SystemClock;
-import android.util.Log;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
+import com.android.internal.os.BaseCommand;
+
+import java.io.PrintStream;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -33,9 +38,14 @@
  * desired character output.
  */
 
-public class Input {
+public class Input extends BaseCommand {
     private static final String TAG = "Input";
     private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+    private static final String INVALID_DISPLAY_ARGUMENTS =
+            "Error: Invalid arguments for display ID.";
+
+    private static final float DEFAULT_PRESSURE = 1.0f;
+    private static final float NO_PRESSURE = 0.0f;
 
     private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
         put("keyboard", InputDevice.SOURCE_KEYBOARD);
@@ -50,6 +60,7 @@
         put("joystick", InputDevice.SOURCE_JOYSTICK);
     }};
 
+    private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>();
 
     /**
      * Command-line entry point.
@@ -60,217 +71,292 @@
         (new Input()).run(args);
     }
 
-    private void run(String[] args) {
-        if (args.length < 1) {
-            showUsage();
-            return;
-        }
+    Input() {
+        COMMANDS.put("text", new InputText());
+        COMMANDS.put("keyevent", new InputKeyEvent());
+        COMMANDS.put("tap", new InputTap());
+        COMMANDS.put("swipe", new InputSwipe());
+        COMMANDS.put("draganddrop", new InputDragAndDrop());
+        COMMANDS.put("press", new InputPress());
+        COMMANDS.put("roll", new InputRoll());
+        COMMANDS.put("motionevent", new InputMotionEvent());
+    }
 
-        int index = 0;
-        String command = args[index];
+    @Override
+    public void onRun() throws Exception {
+        String arg = nextArgRequired();
         int inputSource = InputDevice.SOURCE_UNKNOWN;
-        if (SOURCES.containsKey(command)) {
-            inputSource = SOURCES.get(command);
-            index++;
-            command = args[index];
-        }
-        final int length = args.length - index;
 
-        try {
-            if (command.equals("text")) {
-                if (length == 2) {
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
-                    sendText(inputSource, args[index+1]);
-                    return;
+        // Get source (optional).
+        if (SOURCES.containsKey(arg)) {
+            inputSource = SOURCES.get(arg);
+            arg = nextArgRequired();
+        }
+
+        // Get displayId (optional).
+        int displayId = INVALID_DISPLAY;
+        if ("-d".equals(arg)) {
+            displayId = getDisplayId();
+            arg = nextArgRequired();
+        }
+
+        // Get command and run.
+        InputCmd cmd = COMMANDS.get(arg);
+        if (cmd != null) {
+            try {
+                cmd.run(inputSource, displayId);
+                return;
+            } catch (NumberFormatException ex) {
+                throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
+            }
+        }
+
+        throw new IllegalArgumentException("Error: Unknown command: " + arg);
+    }
+
+    private int getDisplayId() {
+        String displayArg = nextArgRequired();
+        if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
+            return INVALID_DISPLAY;
+        } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
+            return DEFAULT_DISPLAY;
+        } else {
+            try {
+                final int displayId = Integer.parseInt(displayArg);
+                if (displayId == INVALID_DISPLAY) {
+                    return INVALID_DISPLAY;
                 }
-            } else if (command.equals("keyevent")) {
-                if (length >= 2) {
-                    final boolean longpress = "--longpress".equals(args[index + 1]);
-                    final int start = longpress ? index + 2 : index + 1;
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
-                    if (args.length > start) {
-                        for (int i = start; i < args.length; i++) {
-                            int keyCode = KeyEvent.keyCodeFromString(args[i]);
-                            sendKeyEvent(inputSource, keyCode, longpress);
-                        }
-                        return;
+                return Math.max(displayId, 0);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
+            }
+        }
+    }
+
+    class InputText implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+            sendText(inputSource, nextArgRequired(), displayId);
+        }
+
+        /**
+         * Convert the characters of string text into key event's and send to
+         * device.
+         *
+         * @param text is a string of characters you want to input to the device.
+         */
+        private void sendText(int source, final String text, int displayId) {
+            final StringBuffer buff = new StringBuffer(text);
+            boolean escapeFlag = false;
+            for (int i = 0; i < buff.length(); i++) {
+                if (escapeFlag) {
+                    escapeFlag = false;
+                    if (buff.charAt(i) == 's') {
+                        buff.setCharAt(i, ' ');
+                        buff.deleteCharAt(--i);
                     }
                 }
-            } else if (command.equals("tap")) {
-                if (length == 3) {
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                    sendTap(inputSource, Float.parseFloat(args[index+1]),
-                            Float.parseFloat(args[index+2]));
-                    return;
+                if (buff.charAt(i) == '%') {
+                    escapeFlag = true;
                 }
-            } else if (command.equals("swipe")) {
-                int duration = -1;
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                switch (length) {
-                    case 6:
-                        duration = Integer.parseInt(args[index+5]);
-                    case 5:
-                        sendSwipe(inputSource,
-                                Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
-                                Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
-                                duration);
-                        return;
-                }
-            } else if (command.equals("draganddrop")) {
-                int duration = -1;
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                switch (length) {
-                    case 6:
-                        duration = Integer.parseInt(args[index+5]);
-                    case 5:
-                        sendDragAndDrop(inputSource,
-                                Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
-                                Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
-                                duration);
-                        return;
-                }
-            } else if (command.equals("press")) {
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
-                if (length == 1) {
-                    sendTap(inputSource, 0.0f, 0.0f);
-                    return;
-                }
-            } else if (command.equals("roll")) {
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
-                if (length == 3) {
-                    sendMove(inputSource, Float.parseFloat(args[index+1]),
-                            Float.parseFloat(args[index+2]));
-                    return;
-                }
-            } else {
-                System.err.println("Error: Unknown command: " + command);
-                showUsage();
-                return;
             }
-        } catch (NumberFormatException ex) {
+
+            final char[] chars = buff.toString().toCharArray();
+            final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+            final KeyEvent[] events = kcm.getEvents(chars);
+            for (int i = 0; i < events.length; i++) {
+                KeyEvent e = events[i];
+                if (source != e.getSource()) {
+                    e.setSource(source);
+                }
+                e.setDisplayId(displayId);
+                injectKeyEvent(e);
+            }
         }
-        System.err.println(INVALID_ARGUMENTS + command);
-        showUsage();
+    }
+
+    class InputKeyEvent implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            String arg = nextArgRequired();
+            final boolean longpress = "--longpress".equals(arg);
+            if (longpress) {
+                arg = nextArgRequired();
+            }
+
+            do {
+                final int keycode = KeyEvent.keyCodeFromString(arg);
+                sendKeyEvent(inputSource, keycode, longpress, displayId);
+            } while ((arg = nextArg()) != null);
+        }
+
+        private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            int repeatCount = 0;
+
+            KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
+                    0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0/*flags*/,
+                    inputSource);
+            event.setDisplayId(displayId);
+
+            injectKeyEvent(event);
+            if (longpress) {
+                repeatCount++;
+                injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+                        KeyEvent.FLAG_LONG_PRESS));
+            }
+            injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+        }
+    }
+
+    class InputTap implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendTap(inputSource, Float.parseFloat(nextArgRequired()),
+                    Float.parseFloat(nextArgRequired()), displayId);
+        }
+
+        void sendTap(int inputSource, float x, float y, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
+                    displayId);
+            injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
+        }
+    }
+
+    class InputPress extends InputTap {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+            sendTap(inputSource, 0.0f, 0.0f, displayId);
+        }
+    }
+
+    class InputSwipe implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendSwipe(inputSource, displayId, false);
+        }
+
+        void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
+            // Parse two points and duration.
+            final float x1 = Float.parseFloat(nextArgRequired());
+            final float y1 = Float.parseFloat(nextArgRequired());
+            final float x2 = Float.parseFloat(nextArgRequired());
+            final float y2 = Float.parseFloat(nextArgRequired());
+            String durationArg = nextArg();
+            int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
+            if (duration < 0) {
+                duration = 300;
+            }
+
+            final long down = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
+                    displayId);
+            if (isDragDrop) {
+                // long press until drag start.
+                try {
+                    Thread.sleep(ViewConfiguration.getLongPressTimeout());
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            long now = SystemClock.uptimeMillis();
+            final long endTime = down + duration;
+            while (now < endTime) {
+                final long elapsedTime = now - down;
+                final float alpha = (float) elapsedTime / duration;
+                injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
+                        lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+                now = SystemClock.uptimeMillis();
+            }
+            injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
+                    displayId);
+        }
+    }
+
+    class InputDragAndDrop extends InputSwipe {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendSwipe(inputSource, displayId, true);
+        }
+    }
+
+    class InputRoll implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+            sendMove(inputSource, Float.parseFloat(nextArgRequired()),
+                    Float.parseFloat(nextArgRequired()), displayId);
+        }
+
+        /**
+         * Sends a simple zero-pressure move event.
+         *
+         * @param inputSource the InputDevice.SOURCE_* sending the input event
+         * @param dx change in x coordinate due to move
+         * @param dy change in y coordinate due to move
+         */
+        private void sendMove(int inputSource, float dx, float dy, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
+                    displayId);
+        }
+    }
+
+    class InputMotionEvent implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendMotionEvent(inputSource, nextArgRequired(), Float.parseFloat(nextArgRequired()),
+                    Float.parseFloat(nextArgRequired()), displayId);
+        }
+
+        private void sendMotionEvent(int inputSource, String motionEventType, float x, float y,
+                int displayId) {
+            final int action;
+            final float pressure;
+
+            switch (motionEventType.toUpperCase()) {
+                case "DOWN":
+                    action = MotionEvent.ACTION_DOWN;
+                    pressure = DEFAULT_PRESSURE;
+                    break;
+                case "UP":
+                    action = MotionEvent.ACTION_UP;
+                    pressure = NO_PRESSURE;
+                    break;
+                case "MOVE":
+                    action = MotionEvent.ACTION_MOVE;
+                    pressure = DEFAULT_PRESSURE;
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown motionevent " + motionEventType);
+            }
+
+            final long now = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
+        }
     }
 
     /**
-     * Convert the characters of string text into key event's and send to
-     * device.
-     *
-     * @param text is a string of characters you want to input to the device.
+     * Abstract class for command
+     * use nextArgRequired or nextArg to check next argument if necessary.
      */
-    private void sendText(int source, String text) {
-
-        StringBuffer buff = new StringBuffer(text);
-
-        boolean escapeFlag = false;
-        for (int i=0; i<buff.length(); i++) {
-            if (escapeFlag) {
-                escapeFlag = false;
-                if (buff.charAt(i) == 's') {
-                    buff.setCharAt(i, ' ');
-                    buff.deleteCharAt(--i);
-                }
-            }
-            if (buff.charAt(i) == '%') {
-                escapeFlag = true;
-            }
-        }
-
-        char[] chars = buff.toString().toCharArray();
-
-        KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-        KeyEvent[] events = kcm.getEvents(chars);
-        for(int i = 0; i < events.length; i++) {
-            KeyEvent e = events[i];
-            if (source != e.getSource()) {
-                e.setSource(source);
-            }
-            injectKeyEvent(e);
-        }
+    private interface InputCmd {
+        void run(int inputSource, int displayId);
     }
 
-    private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {
-        long now = SystemClock.uptimeMillis();
-        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
-        if (longpress) {
-            injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,
-                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,
-                    inputSource));
-        }
-        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
-    }
-
-    private void sendTap(int inputSource, float x, float y) {
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f);
-    }
-
-    private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) {
-        if (duration < 0) {
-            duration = 300;
-        }
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
-        long startTime = now;
-        long endTime = startTime + duration;
-        while (now < endTime) {
-            long elapsedTime = now - startTime;
-            float alpha = (float) elapsedTime / duration;
-            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
-                    lerp(y1, y2, alpha), 1.0f);
-            now = SystemClock.uptimeMillis();
-        }
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
-    }
-
-    private void sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2,
-            int dragDuration) {
-        if (dragDuration < 0) {
-            dragDuration = 300;
-        }
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
-        try {
-            Thread.sleep(ViewConfiguration.getLongPressTimeout());
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
-        now = SystemClock.uptimeMillis();
-        long startTime = now;
-        long endTime = startTime + dragDuration;
-        while (now < endTime) {
-            long elapsedTime = now - startTime;
-            float alpha = (float) elapsedTime / dragDuration;
-            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
-                    lerp(y1, y2, alpha), 1.0f);
-            now = SystemClock.uptimeMillis();
-        }
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
-    }
-
-    /**
-     * Sends a simple zero-pressure move event.
-     *
-     * @param inputSource the InputDevice.SOURCE_* sending the input event
-     * @param dx change in x coordinate due to move
-     * @param dy change in y coordinate due to move
-     */
-    private void sendMove(int inputSource, float dx, float dy) {
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f);
-    }
-
-    private void injectKeyEvent(KeyEvent event) {
-        Log.i(TAG, "injectKeyEvent: " + event);
+    private static void injectKeyEvent(KeyEvent event) {
         InputManager.getInstance().injectInputEvent(event,
                 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
     }
 
-    private int getInputDeviceId(int inputSource) {
+    private static int getInputDeviceId(int inputSource) {
         final int DEFAULT_DEVICE_ID = 0;
         int[] devIds = InputDevice.getDeviceIds();
         for (int devId : devIds) {
@@ -287,22 +373,27 @@
      *
      * @param inputSource the InputDevice.SOURCE_* sending the input event
      * @param action the MotionEvent.ACTION_* for the event
+     * @param downTime the value of the ACTION_DOWN event happened
      * @param when the value of SystemClock.uptimeMillis() at which the event happened
      * @param x x coordinate of event
      * @param y y coordinate of event
      * @param pressure pressure of event
      */
-    private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) {
+    private static void injectMotionEvent(int inputSource, int action, long downTime, long when,
+            float x, float y, float pressure, int displayId) {
         final float DEFAULT_SIZE = 1.0f;
         final int DEFAULT_META_STATE = 0;
         final float DEFAULT_PRECISION_X = 1.0f;
         final float DEFAULT_PRECISION_Y = 1.0f;
         final int DEFAULT_EDGE_FLAGS = 0;
-        MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE,
+        MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure, DEFAULT_SIZE,
                 DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
                 getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
         event.setSource(inputSource);
-        Log.i(TAG, "injectMotionEvent: " + event);
+        if (displayId == INVALID_DISPLAY && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            displayId = DEFAULT_DISPLAY;
+        }
+        event.setDisplayId(displayId);
         InputManager.getInstance().injectInputEvent(event,
                 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
     }
@@ -315,24 +406,30 @@
         return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
     }
 
-    private void showUsage() {
-        System.err.println("Usage: input [<source>] <command> [<arg>...]");
-        System.err.println();
-        System.err.println("The sources are: ");
+    @Override
+    public void onShowUsage(PrintStream out) {
+        out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
+        out.println();
+        out.println("The sources are: ");
         for (String src : SOURCES.keySet()) {
-            System.err.println("      " + src);
+            out.println("      " + src);
         }
-        System.err.println();
-        System.err.println("The commands and default sources are:");
-        System.err.println("      text <string> (Default: touchscreen)");
-        System.err.println("      keyevent [--longpress] <key code number or name> ..."
+        out.println();
+        out.printf("-d: specify the display ID.\n"
+                + "      (Default: %d for key event, %d for motion event if not specified.)",
+                INVALID_DISPLAY, DEFAULT_DISPLAY);
+        out.println();
+        out.println("The commands and default sources are:");
+        out.println("      text <string> (Default: touchscreen)");
+        out.println("      keyevent [--longpress] <key code number or name> ..."
                 + " (Default: keyboard)");
-        System.err.println("      tap <x> <y> (Default: touchscreen)");
-        System.err.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+        out.println("      tap <x> <y> (Default: touchscreen)");
+        out.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
                 + " (Default: touchscreen)");
-        System.err.println("      draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+        out.println("      draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
                 + " (Default: touchscreen)");
-        System.err.println("      press (Default: trackball)");
-        System.err.println("      roll <dx> <dy> (Default: trackball)");
+        out.println("      press (Default: trackball)");
+        out.println("      roll <dx> <dy> (Default: trackball)");
+        out.println("      event <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
     }
 }
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index c6d2bc8..d160b73 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -19,12 +19,12 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
-import android.content.pm.ParceledListSlice;
 import android.media.MediaMetadata;
+import android.media.session.ControllerCallbackLink;
 import android.media.session.ISessionController;
-import android.media.session.ISessionControllerCallback;
 import android.media.session.ISessionManager;
-import android.media.session.ParcelableVolumeInfo;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.QueueItem;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.HandlerThread;
@@ -178,13 +178,7 @@
                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
     }
 
-    class ControllerMonitor extends ISessionControllerCallback.Stub {
-        private final ISessionController mController;
-
-        public ControllerMonitor(ISessionController controller) {
-            mController = controller;
-        }
-
+    class ControllerCallbackStub extends ControllerCallbackLink.CallbackStub {
         @Override
         public void onSessionDestroyed() {
             System.out.println("onSessionDestroyed. Enter q to quit.");
@@ -208,25 +202,35 @@
         }
 
         @Override
-        public void onQueueChanged(ParceledListSlice queue) throws RemoteException {
+        public void onQueueChanged(List<QueueItem> queue) {
             System.out.println("onQueueChanged, "
-                    + (queue == null ? "null queue" : " size=" + queue.getList().size()));
+                    + (queue == null ? "null queue" : " size=" + queue.size()));
         }
 
         @Override
-        public void onQueueTitleChanged(CharSequence title) throws RemoteException {
+        public void onQueueTitleChanged(CharSequence title) {
             System.out.println("onQueueTitleChange " + title);
         }
 
         @Override
-        public void onExtrasChanged(Bundle extras) throws RemoteException {
+        public void onExtrasChanged(Bundle extras) {
             System.out.println("onExtrasChanged " + extras);
         }
 
         @Override
-        public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException {
+        public void onVolumeInfoChanged(PlaybackInfo info) {
             System.out.println("onVolumeInfoChanged " + info);
         }
+    }
+
+    private class ControllerMonitor {
+        private final ISessionController mController;
+        private final ControllerCallbackLink mControllerCallbackLink;
+
+        ControllerMonitor(ISessionController controller) {
+            mController = controller;
+            mControllerCallbackLink = new ControllerCallbackLink(new ControllerCallbackStub());
+        }
 
         void printUsageMessage() {
             try {
@@ -244,7 +248,7 @@
                 @Override
                 protected void onLooperPrepared() {
                     try {
-                        mController.registerCallbackListener(PACKAGE_NAME, ControllerMonitor.this);
+                        mController.registerCallbackListener(PACKAGE_NAME, mControllerCallbackLink);
                     } catch (RemoteException e) {
                         System.out.println("Error registering monitor callback");
                     }
@@ -287,7 +291,7 @@
             } finally {
                 cbThread.getLooper().quit();
                 try {
-                    mController.unregisterCallbackListener(this);
+                    mController.unregisterCallbackListener(mControllerCallbackLink);
                 } catch (Exception e) {
                     // ignoring
                 }
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 8fa2980..3d74f8b 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -81,8 +81,7 @@
         case ui::Dataspace::V0_SRGB:
             return SkColorSpace::MakeSRGB();
         case ui::Dataspace::DISPLAY_P3:
-            return SkColorSpace::MakeRGB(
-                    SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
         default:
             return nullptr;
     }
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index 9d37cdb..ad5eae3 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -57,7 +57,7 @@
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
                 if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
-                                                            rule_id, configKey)) {
+                                                            subscription.id(), rule_id, configKey)) {
                     ALOGW("Failed to generate perfetto traces.");
                 }
                 break;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5c53a3a..ef3eac0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -25,7 +25,11 @@
 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/bluetooth/hci/enums.proto";
+import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto";
+import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
+import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
 import "frameworks/base/core/proto/android/server/location/enums.proto";
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
@@ -172,7 +176,23 @@
         WifiEnabledStateChanged wifi_enabled_state_changed = 113;
         WifiRunningStateChanged wifi_running_state_changed = 114;
         AppCompacted app_compacted = 115;
-        NetworkDnsEventReported network_dns_event_Reported = 116;
+        NetworkDnsEventReported network_dns_event_reported = 116;
+        DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = 117;
+        DocsUIPickResultReported docs_ui_pick_result_reported = 118;
+        DocsUISearchModeReported docs_ui_search_mode_reported = 119;
+        DocsUISearchTypeReported docs_ui_search_type_reported = 120;
+        DataStallEvent data_stall_event = 121;
+        RescuePartyResetReported rescue_party_reset_reported = 122;
+        SignedConfigReported signed_config_reported = 123;
+        GnssNiEventReported gnss_ni_event_reported = 124;
+        BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125;
+        BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126;
+        BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127;
+        AppDowngraded app_downgraded = 128;
+        AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129;
+        LowStorageStateChanged low_storage_state_changed = 130;
+        GnssNfwNotificationReported gnss_nfw_notification_reported = 131;
+        GnssConfigurationReported gnss_configuration_reported = 132;
     }
 
     // Pulled events will start at field 10000.
@@ -212,7 +232,7 @@
         NumFingerprints num_fingerprints = 10031;
         DiskIo disk_io = 10032;
         PowerProfile power_profile = 10033;
-        ProcStats proc_stats_pkg_proc = 10034;
+        ProcStatsPkgProc proc_stats_pkg_proc = 10034;
         ProcessCpuTime process_cpu_time = 10035;
         NativeProcessMemoryState native_process_memory_state = 10036;
         CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
@@ -1288,10 +1308,12 @@
 }
 
 /**
- * Logs when a Bluetooth device connects and disconnects.
+ * Logs when profiles on a Bluetooth device connects and disconnects.
  *
  * Logged from:
- *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java
+ *
+ * Next Tag: 5
  */
 message BluetoothConnectionStateChanged {
     // The state of the connection.
@@ -1300,13 +1322,152 @@
     // An identifier that can be used to match connect and disconnect events.
     // Currently is last two bytes of a hash of a device level ID and
     // the mac address of the bluetooth device that is connected.
-    optional int32 obfuscated_id = 2;
+    // Deprecated: use obfuscated_id instead, this one is always 0 for Q+
+    optional int32 OBSOLETE_obfuscated_id = 2 [deprecated = true];
     // The profile that is connected. Eg. GATT, A2DP, HEADSET.
     // From android.bluetooth.BluetoothAdapter.java
+    // Default: 0 when not used
     optional int32 bt_profile = 3;
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 4 [(android.os.statsd.log_mode) = MODE_BYTES];
 }
 
 /**
+ * Logs when a Bluetooth device connects and disconnects over ACL
+ *
+ * Logged from:
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java
+ *
+ * Next Tag: 3
+ */
+message BluetoothAclConnectionStateChanged {
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // The state of the connection.
+    // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED.
+    optional android.bluetooth.ConnectionStateEnum state = 2;
+}
+
+/**
+ * Logs when a Bluetooth device connects and disconnects over SCO
+ *
+ * Logged from:
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetClientStateMachine.java
+ *
+ * Next Tag: 4
+ */
+message BluetoothScoConnectionStateChanged {
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // The state of the connection.
+    // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED.
+    optional android.bluetooth.ConnectionStateEnum state = 2;
+    // Codec used for this SCO connection
+    // Default: UNKNOWN
+    optional android.bluetooth.hfp.ScoCodec codec = 3;
+}
+
+// Logs when there is an event affecting Bluetooth device's link layer connection.
+// - This event is triggered when there is a related HCI command or event
+// - Users of this metrics can deduce Bluetooth device's connection state from these events
+// - HCI commands are logged before the command is sent, after receiving command status, and after
+//   receiving command complete
+// - HCI events are logged when they arrive
+//
+// Low level log from system/bt
+//
+// Bluetooth classic commands:
+// - CMD_CREATE_CONNECTION
+// - CMD_DISCONNECT
+// - CMD_CREATE_CONNECTION_CANCEL
+// - CMD_ACCEPT_CONNECTION_REQUEST
+// - CMD_REJECT_CONNECTION_REQUEST
+// - CMD_SETUP_ESCO_CONNECTION
+// - CMD_ACCEPT_ESCO_CONNECTION
+// - CMD_REJECT_ESCO_CONNECTION
+// - CMD_ENH_SETUP_ESCO_CONNECTION
+// - CMD_ENH_ACCEPT_ESCO_CONNECTION
+//
+// Bluetooth low energy commands:
+// - CMD_BLE_CREATE_LL_CONN [Only logged on error or when initiator filter policy is 0x00]
+// - CMD_BLE_CREATE_CONN_CANCEL [Only logged when there is an error]
+// - CMD_BLE_EXTENDED_CREATE_CONNECTION [Only logged on error or when initiator filter policy is 0x00]
+// - CMD_BLE_CLEAR_WHITE_LIST
+// - CMD_BLE_ADD_WHITE_LIST
+// - CMD_BLE_REMOVE_WHITE_LIST
+//
+// Bluetooth classic events:
+// - EVT_CONNECTION_COMP
+// - EVT_CONNECTION_REQUEST
+// - EVT_DISCONNECTION_COMP
+// - EVT_ESCO_CONNECTION_COMP
+// - EVT_ESCO_CONNECTION_CHANGED
+//
+// Bluetooth low energy meta events:
+// - BLE_EVT_CONN_COMPLETE_EVT
+// - BLE_EVT_ENHANCED_CONN_COMPLETE_EVT
+//
+// Next tag: 10
+message BluetoothLinkLayerConnectionEvent {
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // Connection handle of this connection if available
+    // Range: 0x0000 - 0x0EFF (12 bits)
+    // Default: 0xFFFF if the handle is unknown
+    optional int32 connection_handle = 2;
+    // Direction of the link
+    // Default: DIRECTION_UNKNOWN
+    optional android.bluetooth.DirectionEnum direction = 3;
+    // Type of this link
+    // Default: LINK_TYPE_UNKNOWN
+    optional android.bluetooth.LinkTypeEnum type = 4;
+
+    // Reason metadata for this link layer connection event, rules for interpretation:
+    // 1. If hci_cmd is set and valid, hci_event can be either EVT_COMMAND_STATUS or
+    //    EVT_COMMAND_COMPLETE, ignore hci_ble_event in this case
+    // 2. If hci_event is set to EVT_BLE_META, look at hci_ble_event; otherwise, if hci_event is
+    //    set and valid, ignore hci_ble_event
+
+    // HCI command associated with this event
+    // Default: CMD_UNKNOWN
+    optional android.bluetooth.hci.CommandEnum hci_cmd = 5;
+    // HCI event associated with this event
+    // Default: EVT_UNKNOWN
+    optional android.bluetooth.hci.EventEnum hci_event = 6;
+    // HCI BLE meta event associated with this event
+    // Default: BLE_EVT_UNKNOWN
+    optional android.bluetooth.hci.BleMetaEventEnum hci_ble_event = 7;
+    // HCI command status code if this is triggerred by hci_cmd
+    // Default: STATUS_UNKNOWN
+    optional android.bluetooth.hci.StatusEnum cmd_status = 8;
+    // HCI reason code associated with this event
+    // Default: STATUS_UNKNOWN
+    optional android.bluetooth.hci.StatusEnum reason_code = 9;
+}
+
+
+/**
  * Logs when something is plugged into or removed from the USB-C connector.
  *
  * Logged from:
@@ -1631,6 +1792,47 @@
 }
 
 /**
+ * Logs when a volume entered low Storage state.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+ */
+message LowStorageStateChanged {
+    // Volume that ran out of storage.
+    optional string volume_description = 1;
+
+    enum State {
+        UNKNOWN = 0;
+        OFF = 1;
+        ON = 2;
+    }
+    optional State state = 2;
+}
+
+/**
+ * Logs when an app is downgraded.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+ */
+message AppDowngraded {
+    optional string package_name = 1;
+    // Size of the package (all data) before being downgraded.
+    optional int64 size_in_bytes_before = 2;
+    // Size of the package (all data) after being downgraded.
+    optional int64 size_in_bytes_after = 3;
+
+    optional bool aggressive = 4;
+}
+
+/**
+ * Logs when an app is optimized after being downgraded.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+ */
+message AppOptimizedAfterDowngraded {
+    optional string package_name = 1;
+}
+
+/**
  * Logs when an app crashes.
  * Logged from:
  *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3175,7 +3377,8 @@
     optional int32 process_uid = 1;
     // Process name.
     optional string process_name = 2;
-
+    // Package name.
+    optional string package_name = 7;
     // Total count of the times this association appeared.
     optional int32 total_count = 3;
 
@@ -3254,6 +3457,9 @@
     }
     repeated Status status = 7;
 
+    // Number of pages available of various types and sizes, representation fragmentation.
+    repeated ProcessStatsAvailablePagesProto available_pages = 10;
+
     // Stats for each process.
     repeated ProcessStatsProto process_stats = 8;
 
@@ -3261,6 +3467,21 @@
     repeated ProcessStatsPackageProto package_stats = 9;
 }
 
+message ProcessStatsAvailablePagesProto {
+    // Node these pages are in (as per /proc/pagetypeinfo)
+    optional int32 node = 1;
+
+    // Zone these pages are in (as per /proc/pagetypeinfo)
+    optional string zone = 2;
+
+    // Label for the type of these pages (as per /proc/pagetypeinfo)
+    optional string label = 3;
+
+    // Distribution of number of pages available by order size.  First entry in array is
+    // order 0, second is order 1, etc.  Each order increase is a doubling of page size.
+    repeated int32 pages_per_order = 4;
+}
+
 /**
  * Pulled from ProcessStatsService.java
  */
@@ -3268,6 +3489,13 @@
     optional ProcessStatsSectionProto proc_stats_section = 1;
 }
 
+/**
+ * Pulled from ProcessStatsService.java
+ */
+message ProcStatsPkgProc {
+    optional ProcessStatsSectionProto proc_stats_section = 1;
+}
+
 message PowerProfileProto {
     optional double cpu_suspend = 1;
 
@@ -3656,6 +3884,52 @@
 }
 
 /**
+ * Logs the package name that launches docsui picker mode.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIPickerLaunchedFromReported {
+    optional string package_name = 1;
+}
+
+/**
+ * Logs the search type.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUISearchTypeReported {
+    optional android.stats.docsui.SearchType search_type = 1;
+}
+
+/**
+ * Logs the search mode.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUISearchModeReported {
+    optional android.stats.docsui.SearchMode search_mode = 1;
+}
+
+/**
+ * Logs the pick result information.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIPickResultReported {
+    optional int32 total_action_count = 1;
+    optional int64 duration_millis = 2;
+    optional int32 file_count= 3;
+    optional bool is_searching = 4;
+    optional android.stats.docsui.Root picked_from = 5;
+    optional android.stats.docsui.MimeType mime_type = 6;
+    optional int32 repeatedly_pick_times = 7;
+}
+
+/**
  * Logs when an app's memory is compacted.
  *
  * Logged from:
@@ -3741,7 +4015,7 @@
     //bionic/libc/include/netdb.h
     //system/netd/resolv/include/netd_resolv/resolv.h
     enum ReturnCode {
-        EAI_NOERR = 0;
+        EAI_NO_ERROR = 0;
         EAI_ADDRFAMILY = 1;
         EAI_AGAIN = 2;
         EAI_BADFLAGS = 3;
@@ -3764,3 +4038,228 @@
     // The latency period(in microseconds) it took for this DNS lookup to complete.
     optional int32 latency_micros = 3;
 }
+
+/**
+ * Logs when a data stall event occurs.
+ *
+ * Log from:
+ *     frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+ */
+message DataStallEvent {
+    // Data stall evaluation type.
+    // See frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+    // Refer to the definition of DATA_STALL_EVALUATION_TYPE_*.
+    optional int32 evaluation_type = 1;
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.ProbeResult validation_result = 2;
+    // See definition in data_stall_event.proto.
+    optional android.net.NetworkCapabilitiesProto.Transport network_type = 3;
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.WifiData wifi_info = 4 [(log_mode) = MODE_BYTES];
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.CellularData cell_info = 5 [(log_mode) = MODE_BYTES];
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.DnsEvent dns_event = 6 [(log_mode) = MODE_BYTES];
+}
+
+/*
+ * Logs when RescueParty resets some set of experiment flags.
+ *
+ * Logged from:
+ *     frameworks/base/services/core/java/com/android/server/RescueParty.java
+ */
+message RescuePartyResetReported {
+    // The rescue level of this reset. A value of 0 indicates missing or unknown level information.
+    optional int32 rescue_level = 1;
+}
+
+/**
+ * Logs when signed config is received from an APK, and if that config was applied successfully.
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+ */
+message SignedConfigReported {
+    enum Type {
+        UNKNOWN_TYPE = 0;
+        GLOBAL_SETTINGS = 1;
+    }
+    optional Type type = 1;
+
+    // The final status of the signed config received.
+    enum Status {
+        UNKNOWN_STATUS = 0;
+        APPLIED = 1;
+        BASE64_FAILURE_CONFIG = 2;
+        BASE64_FAILURE_SIGNATURE = 3;
+        SECURITY_EXCEPTION = 4;
+        INVALID_CONFIG = 5;
+        OLD_CONFIG = 6;
+        SIGNATURE_CHECK_FAILED = 7;
+        NOT_APPLICABLE = 8;
+        SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT = 9;
+    }
+    optional Status status = 2;
+
+    // The version of the signed config processed.
+    optional int32 version = 3;
+
+    // The package name that the config was extracted from.
+    optional string from_package = 4;
+
+    enum Key {
+        NO_KEY = 0;
+        DEBUG = 1;
+        PRODUCTION = 2;
+    }
+    // Which key was used to verify the config.
+    optional Key verified_with = 5;
+}
+
+/*
+ * Logs GNSS Network-Initiated (NI) location events.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java
+ */
+message GnssNiEventReported {
+    // The type of GnssNiEvent.
+    enum EventType {
+        UNKNOWN = 0;
+        NI_REQUEST = 1;
+        NI_RESPONSE = 2;
+    }
+    optional EventType event_type = 1;
+
+    // An ID generated by HAL to associate NI notifications and UI responses.
+    optional int32 notification_id = 2;
+
+    // A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc.
+    optional android.server.location.GnssNiType ni_type = 3;
+
+    // NI requires notification.
+    optional bool need_notify = 4;
+
+    // NI requires verification.
+    optional bool need_verify = 5;
+
+    // NI requires privacy override, no notification/minimal trace.
+    optional bool privacy_override = 6;
+
+    // Timeout period to wait for user response. Set to 0 for no timeout limit. Specified in
+    // seconds.
+    optional int32 timeout = 7;
+
+    // Default response when timeout.
+    optional android.server.location.GnssUserResponseType default_response = 8;
+
+    // String representing the requester of the network inititated location request.
+    optional string requestor_id = 9;
+
+    // Notification message text string representing the service(for eg. SUPL-service) who sent the
+    // network initiated location request.
+    optional string text = 10;
+
+    // requestorId decoding scheme.
+    optional android.server.location.GnssNiEncodingType requestor_id_encoding = 11;
+
+    // Notification message text decoding scheme.
+    optional android.server.location.GnssNiEncodingType text_encoding = 12;
+
+    // True if SUPL ES is enabled.
+    optional bool is_supl_es_enabled = 13;
+
+    // True if GNSS location is enabled.
+    optional bool is_location_enabled = 14;
+
+    // GNSS NI responses which define the response in NI structures.
+    optional android.server.location.GnssUserResponseType user_response = 15;
+}
+
+/**
+ * Logs GNSS non-framework (NFW) location notification.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java
+ */
+message GnssNfwNotificationReported {
+    // Package name of the Android proxy application representing the non-framework entity that
+    // requested location. Set to empty string if unknown.
+    optional string proxy_app_package_name = 1;
+
+    // Protocol stack that initiated the non-framework location request.
+    optional android.server.location.NfwProtocolStack protocol_stack = 2;
+
+    // Name of the protocol stack if protocol_stack field is set to OTHER_PROTOCOL_STACK. Otherwise,
+    // set to empty string. This field is opaque to the framework and used for logging purposes.
+    optional string other_protocol_stack_name = 3;
+
+    // Source initiating/receiving the location information.
+    optional android.server.location.NfwRequestor requestor = 4;
+
+    // Identity of the endpoint receiving the location information. For example, carrier name, OEM
+    // name, SUPL SLP/E-SLP FQDN, chipset vendor name, etc. This field is opaque to the framework
+    // and used for logging purposes.
+    optional string requestor_id = 5;
+
+    // Indicates whether location information was provided for this request.
+    optional android.server.location.NfwResponseType response_type = 6;
+
+    // True if the device is in user initiated emergency session.
+    optional bool in_emergency_mode = 7;
+
+    // True if cached location is provided.
+    optional bool is_cached_location = 8;
+
+    // True if proxy app permission mismatch between framework and GNSS HAL.
+    optional bool is_permission_mismatched = 9;
+}
+
+/**
+ * Logs GNSS configuration as defined in IGnssConfiguration.hal.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/location/GnssConfiguration.java
+ */
+message GnssConfigurationReported {
+    // SUPL host name.
+    optional string supl_host = 1;
+
+    // SUPL port number.
+    optional int32 supl_port = 2;
+
+    // C2K host name.
+    optional string c2k_host = 3;
+
+    // C2K port number.
+    optional int32 c2k_port = 4;
+
+    // The SUPL version requested by Carrier.
+    optional int32 supl_ver = 5;
+
+    // The SUPL mode.
+    optional android.server.location.SuplMode supl_mode = 6;
+
+    // True if NI emergency SUPL restrictions is enabled.
+    optional bool supl_es = 7;
+
+    // LTE Positioning Profile settings
+    optional android.server.location.LppProfile lpp_profile = 8;
+
+    // Positioning protocol on A-Glonass system.
+    optional android.server.location.GlonassPosProtocol a_glonass_pos_protocol_select = 9;
+
+    // True if emergency PDN is used. Otherwise, regular PDN is used.
+    optional bool use_emergency_pdn_for_emergency_supl= 10;
+
+    // Configurations of how GPS functionalities should be locked when user turns off GPS On setting.
+    optional android.server.location.GpsLock gps_lock = 11;
+
+    // Number of seconds to extend the emergency session duration post emergency call.
+    optional int32 es_extension_sec = 12;
+
+    // The full list of package names of proxy Android applications representing the non-framework
+    // location access entities (on/off the device) for which the framework user has granted
+    // non-framework location access permission. The package names are concatenated in one string
+    // with spaces as separators.
+    optional string enabled_proxy_app_package_name_list = 13;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 42cc543..0c4c330 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -39,6 +39,7 @@
 namespace statsd {
 
 bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+                                            int64_t subscription_id,
                                             int64_t alert_id,
                                             const ConfigKey& configKey) {
     VLOG("Starting trace collection through perfetto");
@@ -48,9 +49,11 @@
         return false;
     }
 
-    char alertId[20];
-    char configId[20];
-    char configUid[20];
+    char subscriptionId[25];
+    char alertId[25];
+    char configId[25];
+    char configUid[25];
+    snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id);
     snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id);
     snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId());
     snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid());
@@ -94,7 +97,7 @@
 
         execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
               kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid",
-              configUid, nullptr);
+              configUid, "--subscription-id", subscriptionId, nullptr);
 
         // execl() doesn't return in case of success, if we get here something
         // failed.
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index 1e7f728..ab2c195 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -32,6 +32,7 @@
 // This method returns immediately after passing the config and does NOT wait
 // for the full duration of the trace.
 bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+                                            int64_t subscription_id,
                                             int64_t alert_id,
                                             const ConfigKey& configKey);
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 67a1a47..c2878f0 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -93,7 +93,8 @@
                                           StatsdStats::kAtomDimensionKeySizeLimitMap.end()
                                   ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
                                   : StatsdStats::kDimensionKeySizeHardLimit),
-      mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()) {
+      mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()),
+      mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) {
     mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
     mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
     int64_t bucketSizeMills = 0;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index a1a5061..df08779 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -74,6 +74,9 @@
                           const int64_t version) override {
         std::lock_guard<std::mutex> lock(mMutex);
 
+        if (!mSplitBucketForAppUpgrade) {
+            return;
+        }
         if (eventTimeNs > getCurrentBucketEndTimeNs()) {
             // Flush full buckets on the normal path up to the latest bucket boundary.
             flushIfNeededLocked(eventTimeNs);
@@ -176,11 +179,14 @@
 
     const size_t mGaugeAtomsPerDimensionLimit;
 
+    const bool mSplitBucketForAppUpgrade;
+
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
+    FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
     FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
     FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 9a8e3bd..4122d84 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -105,7 +105,8 @@
       mUseZeroDefaultBase(metric.use_zero_default_base()),
       mHasGlobalBase(false),
       mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
-                                                      : StatsdStats::kPullMaxDelayNs) {
+                                                      : StatsdStats::kPullMaxDelayNs),
+      mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
         bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4865aee..69eb0af 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -57,6 +57,9 @@
     void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
                           const int64_t version) override {
         std::lock_guard<std::mutex> lock(mMutex);
+        if (!mSplitBucketForAppUpgrade) {
+            return;
+        }
         if (mIsPulled && mCondition) {
             pullAndMatchEventsLocked(eventTimeNs - 1);
         }
@@ -185,6 +188,8 @@
 
     const int64_t mMaxPullDelayNs;
 
+    const bool mSplitBucketForAppUpgrade;
+
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
@@ -193,6 +198,7 @@
     FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 381ac32..9d3a669 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -244,6 +244,8 @@
   optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
 
   optional int32 max_pull_delay_sec = 13 [default = 10];
+
+  optional bool split_bucket_for_app_upgrade = 14 [default = true];
 }
 
 message ValueMetric {
@@ -290,6 +292,8 @@
   optional bool skip_zero_diff_output = 14 [default = true];
 
   optional int32 max_pull_delay_sec = 16 [default = 10];
+
+  optional bool split_bucket_for_app_upgrade = 17 [default = true];
 }
 
 message Alert {
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 2799107..0ffbb54 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -276,8 +276,9 @@
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
     atomMatcher.set_atom_id(tagId);
-    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -295,9 +296,8 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard,
-                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
 
     vector<shared_ptr<LogEvent>> allData;
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -337,6 +337,58 @@
                          ->mValue.int_value);
 }
 
+TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    metric.set_split_bucket_for_app_upgrade(false);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(2);
+
+    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());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
+
+    vector<shared_ptr<LogEvent>> allData;
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+    event->write("some value");
+    event->write(1);
+    event->init();
+    allData.push_back(event);
+    gaugeProducer.onDataPulled(allData);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
+                         ->second.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+
+    gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
+                         ->second.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+}
+
 TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
     GaugeMetric metric;
     metric.set_id(metricId);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 67570fc..9cfe343 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -641,7 +641,8 @@
     valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
     EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+    EXPECT_EQ(20L,
+              valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -652,7 +653,48 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
     EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+    EXPECT_EQ(20L,
+              valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+}
+
+TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    metric.set_split_bucket_for_app_upgrade(false);
+
+    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>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+                                      eventMatcherWizard, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event->write(tagId);
+    event->write(100);
+    event->init();
+    allData.push_back(event);
+
+    valueProducer.onDataPulled(allData);
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+
+    valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(bucketStartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
 }
 
 TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 7a993fd..02eff0b 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -56,7 +56,6 @@
 Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessPss([I)[J
-Landroid/app/IActivityManager$Stub$Proxy;->isAppForeground(I)Z
 Landroid/app/IActivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/app/IActivityManager$Stub$Proxy;->setActivityController(Landroid/app/IActivityController;Z)V
 Landroid/app/IActivityManager$Stub$Proxy;->updatePersistentConfiguration(Landroid/content/res/Configuration;)V
@@ -491,7 +490,6 @@
 Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
 Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
 Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
-Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
 Landroid/location/INetInitiatedListener$Stub;-><init>()V
 Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
 Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
@@ -1110,18 +1108,6 @@
 Landroid/os/UserManager;->mService:Landroid/os/IUserManager;
 Landroid/os/UserManager;->removeUser(I)Z
 Landroid/os/Vibrator;-><init>()V
-Landroid/os/VintfObject;->getHalNamesAndVersions()[Ljava/lang/String;
-Landroid/os/VintfObject;->getSepolicyVersion()Ljava/lang/String;
-Landroid/os/VintfObject;->getTargetFrameworkCompatibilityMatrixVersion()Ljava/lang/Long;
-Landroid/os/VintfObject;->getVndkSnapshots()Ljava/util/Map;
-Landroid/os/VintfObject;->report()[Ljava/lang/String;
-Landroid/os/VintfRuntimeInfo;->getCpuInfo()Ljava/lang/String;
-Landroid/os/VintfRuntimeInfo;->getHardwareId()Ljava/lang/String;
-Landroid/os/VintfRuntimeInfo;->getKernelVersion()Ljava/lang/String;
-Landroid/os/VintfRuntimeInfo;->getNodeName()Ljava/lang/String;
-Landroid/os/VintfRuntimeInfo;->getOsName()Ljava/lang/String;
-Landroid/os/VintfRuntimeInfo;->getOsRelease()Ljava/lang/String;
-Landroid/os/VintfRuntimeInfo;->getOsVersion()Ljava/lang/String;
 Landroid/os/WorkSource;-><init>(Landroid/os/Parcel;)V
 Landroid/os/WorkSource;->mNames:[Ljava/lang/String;
 Landroid/os/WorkSource;->mNum:I
@@ -1494,7 +1480,6 @@
 Landroid/test/AndroidTestCase;->getTestContext()Landroid/content/Context;
 Landroid/test/AndroidTestCase;->setTestContext(Landroid/content/Context;)V
 Landroid/test/InstrumentationTestCase;->runMethod(Ljava/lang/reflect/Method;I)V
-Landroid/test/RepetitiveTest;->numIterations()I
 Landroid/util/Singleton;-><init>()V
 Landroid/util/XmlPullAttributes;-><init>(Lorg/xmlpull/v1/XmlPullParser;)V
 Landroid/util/XmlPullAttributes;->mParser:Lorg/xmlpull/v1/XmlPullParser;
@@ -1827,22 +1812,10 @@
 Lcom/android/internal/os/RuntimeInit;->initialized:Z
 Lcom/android/internal/os/RuntimeInit;->main([Ljava/lang/String;)V
 Lcom/android/internal/os/RuntimeInit;->mApplicationObject:Landroid/os/IBinder;
-Lcom/android/internal/os/ZygoteConnection$Arguments;-><init>([Ljava/lang/String;)V
-Lcom/android/internal/os/ZygoteConnection$Arguments;->effectiveCapabilities:J
-Lcom/android/internal/os/ZygoteConnection$Arguments;->gid:I
-Lcom/android/internal/os/ZygoteConnection$Arguments;->gids:[I
-Lcom/android/internal/os/ZygoteConnection$Arguments;->permittedCapabilities:J
-Lcom/android/internal/os/ZygoteConnection$Arguments;->remainingArgs:[Ljava/lang/String;
-Lcom/android/internal/os/ZygoteConnection$Arguments;->rlimits:Ljava/util/ArrayList;
-Lcom/android/internal/os/ZygoteConnection$Arguments;->uid:I
-Lcom/android/internal/os/ZygoteConnection;->applyUidSecurityPolicy(Lcom/android/internal/os/ZygoteConnection$Arguments;Landroid/net/Credentials;)V
 Lcom/android/internal/os/ZygoteConnection;->closeSocket()V
-Lcom/android/internal/os/ZygoteConnection;->getFileDesciptor()Ljava/io/FileDescriptor;
-Lcom/android/internal/os/ZygoteConnection;->intArray2d:[[I
 Lcom/android/internal/os/ZygoteConnection;->mSocket:Landroid/net/LocalSocket;
 Lcom/android/internal/os/ZygoteConnection;->mSocketOutStream:Ljava/io/DataOutputStream;
 Lcom/android/internal/os/ZygoteConnection;->peer:Landroid/net/Credentials;
-Lcom/android/internal/os/ZygoteConnection;->readArgumentList()[Ljava/lang/String;
 Lcom/android/internal/os/ZygoteInit;->main([Ljava/lang/String;)V
 Lcom/android/internal/os/ZygoteInit;->mResources:Landroid/content/res/Resources;
 Lcom/android/internal/os/ZygoteSecurityException;-><init>(Ljava/lang/String;)V
@@ -2768,94 +2741,6 @@
 Lcom/android/internal/telephony/Connection;->mIsIncoming:Z
 Lcom/android/internal/telephony/Connection;->mNumberPresentation:I
 Lcom/android/internal/telephony/Connection;->setVideoState(I)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getApnType()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getReason()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isConnectable()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isEnabled()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isReady()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mApnType:Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCount:I
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCountLock:Ljava/lang/Object;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->setState(Lcom/android/internal/telephony/DctConstants$State;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;->mApnContext:Lcom/android/internal/telephony/dataconnection/ApnContext;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->clearSettings()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->dumpToLog()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)Z
-Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectParams:Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mId:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mInactiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mLinkProperties:Landroid/net/LinkProperties;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->updateTcpBufferSizes(I)V
-Lcom/android/internal/telephony/dataconnection/DcController;->lr(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll;
-Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->createAllApnList()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getActiveApnTypes()[Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getOverallState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getUiccRecords(I)Lcom/android/internal/telephony/uicc/IccRecords;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isConnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isOnlySingleDcAllowed(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->loge(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAllApnSettings:Ljava/util/ArrayList;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mApnContexts:Ljava/util/concurrent/ConcurrentHashMap;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAttached:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAutoAttachOnCreation:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDataConnectionTracker:Landroid/os/Handler;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDisconnectPendingCount:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsPsRestricted:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsScreenOn:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollEnabled:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollPeriod:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPrioritySortedApnContexts:Ljava/util/PriorityQueue;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mProvisioningSpinner:Landroid/app/ProgressDialog;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mResolver:Landroid/content/ContentResolver;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mState:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyDataConnection(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyOffApnsOfAvailability(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentDataStallAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentProvisioningApnAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubIdChanged()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setInitialAttachApn()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setPreferredApn(I)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setRadio(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setupDataOnConnectableApns(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startDataStallAlarm(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
@@ -3290,7 +3175,7 @@
 Lcom/android/internal/telephony/ITelephonyRegistry;->listen(Ljava/lang/String;Lcom/android/internal/telephony/IPhoneStateListener;IZ)V
 Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallState(ILjava/lang/String;)V
 Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCellInfo(Ljava/util/List;)V
-Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataConnectionFailed(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataConnectionFailed(Ljava/lang/String;)V
 Lcom/android/internal/telephony/IWapPushManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IWapPushManager;
 Lcom/android/internal/telephony/IWapPushManager;->addPackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZZ)Z
 Lcom/android/internal/telephony/IWapPushManager;->deletePackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
@@ -3655,11 +3540,9 @@
 Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mContext:Landroid/content/Context;
 Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mCurrentlyActiveUserId:I
 Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mIccId:[Ljava/lang/String;
-Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mInsertSimState:[I
 Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mPackageManager:Landroid/content/pm/IPackageManager;
 Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mPhone:[Lcom/android/internal/telephony/Phone;
 Lcom/android/internal/telephony/SubscriptionInfoUpdater;->PROJECT_SIM_NUM:I
-Lcom/android/internal/telephony/SubscriptionInfoUpdater;->updateSubscriptionInfoByIccId()V
 Lcom/android/internal/telephony/TelephonyCapabilities;->supportsAdn(I)Z
 Lcom/android/internal/telephony/TelephonyProperties;->PROPERTY_ICC_OPERATOR_NUMERIC:Ljava/lang/String;
 Lcom/android/internal/telephony/test/InterpreterEx;-><init>(Ljava/lang/String;)V
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index f2ad268..35098a0 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -1090,7 +1090,7 @@
          * called) or the service has been disconnected, this method will have
          * no effect and return {@code false}.
          *
-         * @param scale the magnification scale to set, must be >= 1 and <= 5
+         * @param scale the magnification scale to set, must be >= 1 and <= 8
          * @param animate {@code true} to animate from the current scale or
          *                {@code false} to set the scale immediately
          * @return {@code true} on success, {@code false} on failure
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5b8261e..836627e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.os.Process.myUid;
 
 import static java.lang.Character.MIN_VALUE;
 
@@ -121,7 +122,9 @@
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
 import android.view.autofill.IAutofillWindowPresenter;
+import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
 import android.widget.AdapterView;
 import android.widget.Toast;
 import android.widget.Toolbar;
@@ -1022,7 +1025,9 @@
      *
      * @return The content capture manager
      */
-    @NonNull private ContentCaptureManager getContentCaptureManager() {
+    @Nullable private ContentCaptureManager getContentCaptureManager() {
+        // ContextCapture disabled for system apps
+        if (!UserHandle.isApp(myUid())) return null;
         if (mContentCaptureManager == null) {
             mContentCaptureManager = getSystemService(ContentCaptureManager.class);
         }
@@ -1030,13 +1035,15 @@
     }
 
     /** @hide */ private static final int CONTENT_CAPTURE_START = 1;
-    /** @hide */ private static final int CONTENT_CAPTURE_FLUSH = 2;
-    /** @hide */ private static final int CONTENT_CAPTURE_STOP = 3;
+    /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 2;
+    /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 3;
+    /** @hide */ private static final int CONTENT_CAPTURE_STOP = 4;
 
     /** @hide */
     @IntDef(prefix = { "CONTENT_CAPTURE_" }, value = {
             CONTENT_CAPTURE_START,
-            CONTENT_CAPTURE_FLUSH,
+            CONTENT_CAPTURE_PAUSE,
+            CONTENT_CAPTURE_RESUME,
             CONTENT_CAPTURE_STOP
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -1045,17 +1052,24 @@
 
     private void notifyContentCaptureManagerIfNeeded(@ContentCaptureNotificationType int type) {
         final ContentCaptureManager cm = getContentCaptureManager();
-        if (cm == null || !cm.isContentCaptureEnabled()) {
-            return;
-        }
+        if (cm == null) return;
+
         switch (type) {
             case CONTENT_CAPTURE_START:
                 //TODO(b/111276913): decide whether the InteractionSessionId should be
                 // saved / restored in the activity bundle - probably not
-                cm.onActivityStarted(mToken, getComponentName());
+                int flags = 0;
+                if ((getWindow().getAttributes().flags
+                        & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+                    flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+                }
+                cm.onActivityStarted(mToken, getComponentName(), flags);
                 break;
-            case CONTENT_CAPTURE_FLUSH:
-                cm.flush();
+            case CONTENT_CAPTURE_PAUSE:
+                cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_PAUSED);
+                break;
+            case CONTENT_CAPTURE_RESUME:
+                cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_RESUMED);
                 break;
             case CONTENT_CAPTURE_STOP:
                 cm.onActivityStopped();
@@ -1747,7 +1761,7 @@
             }
         }
         mCalled = true;
-        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_FLUSH);
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_RESUME);
     }
 
     /**
@@ -2141,7 +2155,7 @@
             }
         }
         mCalled = true;
-        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_FLUSH);
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
     }
 
     /**
@@ -2158,6 +2172,7 @@
      * for helping activities determine the proper time to cancel a notification.
      *
      * @see #onUserInteraction()
+     * @see android.content.Intent#FLAG_ACTIVITY_NO_USER_ACTION
      */
     protected void onUserLeaveHint() {
     }
@@ -8282,6 +8297,33 @@
     }
 
     /**
+     * Specifies whether this {@link Activity} should be shown on top of the lock screen whenever
+     * the lockscreen is up and this activity has another activity behind it with the showWhenLock
+     * attribute set. That is, this activity is only visible on the lock screen if there is another
+     * activity with the showWhenLock attribute visible at the same time on the lock screen. A use
+     * case for this is permission dialogs, that should only be visible on the lock screen if their
+     * requesting activity is also visible. This value can be set as a manifest attribute using
+     * android.R.attr#inheritShowWhenLocked.
+     *
+     * @param inheritShowWhenLocked {@code true} to show the {@link Activity} on top of the lock
+     *                              screen when this activity has another activity behind it with
+     *                              the showWhenLock attribute set; {@code false} otherwise.
+     * @see #setShowWhenLocked(boolean)
+     * See android.R.attr#inheritShowWhenLocked
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
+        try {
+            ActivityTaskManager.getService().setInheritShowWhenLocked(
+                    mToken, inheritShowWhenLocked);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to call setInheritShowWhenLocked", e);
+        }
+    }
+
+    /**
      * Specifies whether the screen should be turned on when the {@link Activity} is resumed.
      * Normally an activity will be transitioned to the stopped state if it is started while the
      * screen if off, but with this flag set the activity will cause the screen to turn on if the
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d423260..e0b8d78 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3705,11 +3705,16 @@
      * Returns whether switching to provided user was successful.
      *
      * @param user the user to switch to.
+     *
+     * @throws IllegalArgumentException if the user is null.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
-    public boolean switchUser(UserHandle user) {
+    public boolean switchUser(@NonNull UserHandle user) {
+        if (user == null) {
+            throw new IllegalArgumentException("UserHandle cannot be null.");
+        }
         return switchUser(user.getIdentifier());
     }
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0b50916..5cac048 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -258,8 +258,8 @@
             Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
             boolean sticky, int userId, boolean allowBackgroundActivityStarts);
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
-            String resolvedType, boolean fgRequired, String callingPackage, int userId)
-            throws TransactionTooLargeException;
+            String resolvedType, boolean fgRequired, String callingPackage, int userId,
+            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
 
     public abstract void disconnectActivityFromServices(Object connectionHolder);
     public abstract void cleanUpServices(int userId, ComponentName component, Intent baseIntent);
@@ -314,4 +314,16 @@
 
     /** Returns mount mode for process running with given pid */
     public abstract int getStorageMountMode(int pid, int uid);
+
+    /** Returns true if the given uid is the app in the foreground. */
+    public abstract boolean isAppForeground(int uid);
+
+    /** Remove pending backup for the given userId. */
+    public abstract void clearPendingBackup(int userId);
+
+    /**
+     * When power button is very long pressed, call this interface to do some pre-shutdown work
+     * like persisting database etc.
+     */
+    public abstract void prepareForPossibleShutdown();
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8faf08a..7767f04 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -271,6 +271,13 @@
     @UnsupportedAppUsage
     final H mH = new H();
     final Executor mExecutor = new HandlerExecutor(mH);
+    /**
+     * Maps from activity token to local record of running activities in this process.
+     *
+     * This variable is readable if the code is running in activity thread or holding {@link
+     * #mResourcesManager}. It's only writable if the code is running in activity thread and holding
+     * {@link #mResourcesManager}.
+     */
     @UnsupportedAppUsage
     final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
     /** The activities to be truly destroyed (not include relaunch). */
@@ -434,6 +441,10 @@
         Configuration newConfig;
         Configuration createdConfig;
         Configuration overrideConfig;
+        // Used to save the last reported configuration from server side so that activity
+        // configuration transactions can always use the latest configuration.
+        @GuardedBy("this")
+        private Configuration mPendingOverrideConfig;
         // Used for consolidating configs before sending on to Activity.
         private Configuration tmpConfig = new Configuration();
         // Callback used for updating activity override config.
@@ -3064,7 +3075,12 @@
             }
             r.setState(ON_CREATE);
 
-            mActivities.put(r.token, r);
+            // updatePendingActivityConfiguration() reads from mActivities to update
+            // ActivityClientRecord which runs in a different thread. Protect modifications to
+            // mActivities to avoid race.
+            synchronized (mResourcesManager) {
+                mActivities.put(r.token, r);
+            }
 
         } catch (SuperNotCalledException e) {
             throw e;
@@ -4639,7 +4655,12 @@
             r.setState(ON_DESTROY);
         }
         schedulePurgeIdler();
-        mActivities.remove(token);
+        // updatePendingActivityConfiguration() reads from mActivities to update
+        // ActivityClientRecord which runs in a different thread. Protect modifications to
+        // mActivities to avoid race.
+        synchronized (mResourcesManager) {
+            mActivities.remove(token);
+        }
         StrictMode.decrementExpectedActivityCount(activityClass);
         return r;
     }
@@ -5382,6 +5403,26 @@
         }
     }
 
+    @Override
+    public void updatePendingActivityConfiguration(IBinder activityToken,
+            Configuration overrideConfig) {
+        final ActivityClientRecord r;
+        synchronized (mResourcesManager) {
+            r = mActivities.get(activityToken);
+        }
+
+        if (r == null) {
+            if (DEBUG_CONFIGURATION) {
+                Slog.w(TAG, "Not found target activity to update its pending config.");
+            }
+            return;
+        }
+
+        synchronized (r) {
+            r.mPendingOverrideConfig = overrideConfig;
+        }
+    }
+
     /**
      * Handle new activity configuration and/or move to a different display.
      * @param activityToken Target activity token.
@@ -5401,6 +5442,24 @@
         final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
                 && displayId != r.activity.getDisplayId();
 
+        synchronized (r) {
+            if (r.mPendingOverrideConfig != null
+                    && !r.mPendingOverrideConfig.isOtherSeqNewer(overrideConfig)) {
+                overrideConfig = r.mPendingOverrideConfig;
+            }
+            r.mPendingOverrideConfig = null;
+        }
+
+        if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
+                && !movedToDifferentDisplay) {
+            if (DEBUG_CONFIGURATION) {
+                Slog.v(TAG, "Activity already handled newer configuration so drop this"
+                        + " transaction. overrideConfig=" + overrideConfig + " r.overrideConfig="
+                        + r.overrideConfig);
+            }
+            return;
+        }
+
         // Perform updates.
         r.overrideConfig = overrideConfig;
         final ViewRootImpl viewRoot = r.activity.mDecor != null
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 0b5776e..ab8f234 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -16,6 +16,10 @@
 
 package android.app;
 
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager.StackInfo;
@@ -32,11 +36,11 @@
 import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.MotionEvent;
-import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
 import android.view.SurfaceView;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -81,7 +85,6 @@
     private boolean mOpened; // Protected by mGuard.
 
     private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-    private Surface mTmpSurface = new Surface();
 
     /** The ActivityView is only allowed to contain one task. */
     private final boolean mSingleTaskInstance;
@@ -318,20 +321,20 @@
     private class SurfaceCallback implements SurfaceHolder.Callback {
         @Override
         public void surfaceCreated(SurfaceHolder surfaceHolder) {
-            mTmpSurface = new Surface();
             if (mVirtualDisplay == null) {
-                initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
+                initVirtualDisplay(new SurfaceSession());
                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
                 }
             } else {
-                // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-                // whether it has a surface. Setting a fake surface here so DisplayManager will
-                // consider this display on.
-                mVirtualDisplay.setSurface(mTmpSurface);
                 mTmpTransaction.reparent(mRootSurfaceControl,
                         mSurfaceView.getSurfaceControl().getHandle()).apply();
             }
+
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.setDisplayState(true);
+            }
+
             updateLocation();
         }
 
@@ -345,15 +348,19 @@
 
         @Override
         public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
-            mTmpSurface.release();
-            mTmpSurface = null;
             if (mVirtualDisplay != null) {
-                mVirtualDisplay.setSurface(null);
+                mVirtualDisplay.setDisplayState(false);
             }
             cleanTapExcludeRegion();
         }
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        mSurfaceView.setVisibility(visibility);
+    }
+
     private void initVirtualDisplay(SurfaceSession surfaceSession) {
         if (mVirtualDisplay != null) {
             throw new IllegalStateException("Trying to initialize for the second time.");
@@ -363,15 +370,11 @@
         final int height = mSurfaceView.getHeight();
         final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
 
-        // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-        // whether it has a surface. Setting a fake surface here so DisplayManager will consider
-        // this display on.
         mVirtualDisplay = displayManager.createVirtualDisplay(
-                DISPLAY_NAME + "@" + System.identityHashCode(this),
-                width, height, getBaseDisplayDensity(), mTmpSurface,
-                DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
+                DISPLAY_NAME + "@" + System.identityHashCode(this), width, height,
+                getBaseDisplayDensity(), null,
+                VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+                        | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
         if (mVirtualDisplay == null) {
             Log.e(TAG, "Failed to initialize ActivityView");
             return;
@@ -382,6 +385,7 @@
 
         mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
                 .setContainerLayer(true)
+                .setParent(mSurfaceView.getSurfaceControl())
                 .setName(DISPLAY_NAME)
                 .build();
 
@@ -435,11 +439,6 @@
             displayReleased = false;
         }
 
-        if (mTmpSurface != null) {
-            mTmpSurface.release();
-            mTmpSurface = null;
-        }
-
         if (displayReleased && mActivityViewCallback != null) {
             mActivityViewCallback.onActivityViewDestroyed(this);
         }
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index cf40e06..bfc216a 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.ResourceId;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -465,7 +466,7 @@
          * @param context the parent context
          */
         public Builder(Context context) {
-            this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
+            this(context, resolveDialogTheme(context, Resources.ID_NULL));
         }
 
         /**
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
index 9329fbc..2240302 100644
--- a/core/java/android/app/AppOpsManager.aidl
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -19,5 +19,7 @@
 parcelable AppOpsManager.PackageOps;
 parcelable AppOpsManager.OpEntry;
 
+parcelable AppOpsManager.HistoricalOp;
+parcelable AppOpsManager.HistoricalOps;
 parcelable AppOpsManager.HistoricalPackageOps;
-parcelable AppOpsManager.HistoricalOpEntry;
+parcelable AppOpsManager.HistoricalUidOps;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 78fe002..e155fe2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -36,26 +36,33 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.GuardedBy;
+import android.util.SparseArray;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * API for interacting with "application operation" tracking.
@@ -106,6 +113,51 @@
 
     static IBinder sToken;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "HISTORICAL_MODE_" }, value = {
+            HISTORICAL_MODE_DISABLED,
+            HISTORICAL_MODE_ENABLED_ACTIVE,
+            HISTORICAL_MODE_ENABLED_PASSIVE
+    })
+    public @interface HistoricalMode {}
+
+    /**
+     * Mode in which app op history is completely disabled.
+     * @hide
+     */
+    @TestApi
+    public static final int HISTORICAL_MODE_DISABLED = 0;
+
+    /**
+     * Mode in which app op history is enabled and app ops performed by apps would
+     * be tracked. This is the mode in which the feature is completely enabled.
+     * @hide
+     */
+    @TestApi
+    public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1;
+
+    /**
+     * Mode in which app op history is enabled but app ops performed by apps would
+     * not be tracked and the only way to add ops to the history is via explicit calls
+     * to dedicated APIs. This mode is useful for testing to allow full control of
+     * the historical content.
+     * @hide
+     */
+    @TestApi
+    public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "MODE_" }, value = {
+            MODE_ALLOWED,
+            MODE_IGNORED,
+            MODE_ERRORED,
+            MODE_DEFAULT,
+            MODE_FOREGROUND
+    })
+    public @interface Mode {}
+
     /**
      * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is
      * allowed to perform the given operation.
@@ -159,7 +211,6 @@
             "foreground",   // MODE_FOREGROUND
     };
 
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "UID_STATE_" }, value = {
@@ -173,9 +224,16 @@
     public @interface UidState {}
 
     /**
+     * Invalid UID state.
+     * @hide
+     */
+    public static final int UID_STATE_INVALID = -1;
+
+    /**
      * Metrics about an op when its uid is persistent.
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final int UID_STATE_PERSISTENT = 0;
 
@@ -183,6 +241,7 @@
      * Metrics about an op when its uid is at the top.
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final int UID_STATE_TOP = 1;
 
@@ -190,6 +249,7 @@
      * Metrics about an op when its uid is running a foreground service.
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final int UID_STATE_FOREGROUND_SERVICE = 2;
 
@@ -203,6 +263,7 @@
      * Metrics about an op when its uid is in the foreground for any other reasons.
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final int UID_STATE_FOREGROUND = 3;
 
@@ -210,6 +271,7 @@
      * Metrics about an op when its uid is in the background for any reason.
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final int UID_STATE_BACKGROUND = 4;
 
@@ -217,6 +279,7 @@
      * Metrics about an op when its uid is cached.
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final int UID_STATE_CACHED = 5;
 
@@ -237,7 +300,7 @@
     @UnsupportedAppUsage
     public static final int OP_NONE = -1;
     /** @hide Access to coarse location information. */
-    @UnsupportedAppUsage
+    @TestApi
     public static final int OP_COARSE_LOCATION = 0;
     /** @hide Access to fine location information. */
     @UnsupportedAppUsage
@@ -1645,6 +1708,9 @@
         }
     }
 
+    /** @hide */
+    public static final String KEY_HISTORICAL_OPS = "historical_ops";
+
     /**
      * Retrieve the op switch that controls the given operation.
      * @hide
@@ -1731,7 +1797,7 @@
      * Retrieve the default mode for the operation.
      * @hide
      */
-    public static int opToDefaultMode(int op) {
+    public static @Mode int opToDefaultMode(int op) {
         // STOPSHIP b/118520006: Hardcode the default values once the feature is stable.
         switch (op) {
             // SMS permissions
@@ -1795,7 +1861,7 @@
      * Retrieve the human readable mode.
      * @hide
      */
-    public static String modeToName(int mode) {
+    public static String modeToName(@Mode int mode) {
         if (mode >= 0 && mode < MODE_NAMES.length) {
             return MODE_NAMES[mode];
         }
@@ -1885,7 +1951,7 @@
     @SystemApi
     public static final class OpEntry implements Parcelable {
         private final int mOp;
-        private final int mMode;
+        private final @Mode int mMode;
         private final long[] mTimes;
         private final long[] mRejectTimes;
         private final int mDuration;
@@ -1896,7 +1962,7 @@
         /**
          * @hide
          */
-        public OpEntry(int op, int mode, long time, long rejectTime, int duration,
+        public OpEntry(int op, @Mode int mode, long time, long rejectTime, int duration,
                 int proxyUid, String proxyPackage) {
             mOp = op;
             mMode = mode;
@@ -1913,7 +1979,7 @@
         /**
          * @hide
          */
-        public OpEntry(int op, int mode, long[] times, long[] rejectTimes, int duration,
+        public OpEntry(int op, @Mode int mode, long[] times, long[] rejectTimes, int duration,
                 boolean running, int proxyUid, String proxyPackage) {
             mOp = op;
             mMode = mode;
@@ -1930,7 +1996,7 @@
         /**
          * @hide
          */
-        public OpEntry(int op, int mode, long[] times, long[] rejectTimes, int duration,
+        public OpEntry(int op, @Mode int mode, long[] times, long[] rejectTimes, int duration,
                 int proxyUid, String proxyPackage) {
             this(op, mode, times, rejectTimes, duration, duration == -1, proxyUid, proxyPackage);
         }
@@ -1953,7 +2019,7 @@
         /**
          * Return this entry's current mode, such as {@link #MODE_ALLOWED}.
          */
-        public int getMode() {
+        public @Mode int getMode() {
             return mMode;
         }
 
@@ -2086,41 +2152,774 @@
         };
     }
 
+    /** @hide */
+    public interface HistoricalOpsVisitor {
+        void visitHistoricalOps(@NonNull HistoricalOps ops);
+        void visitHistoricalUidOps(@NonNull HistoricalUidOps ops);
+        void visitHistoricalPackageOps(@NonNull HistoricalPackageOps ops);
+        void visitHistoricalOp(@NonNull HistoricalOp ops);
+    }
+
     /**
-     * This class represents historical app op information about a package. The history
-     * is aggregated information about ops for a certain amount of time such
-     * as the times the op was accessed, the times the op was rejected, the total
-     * duration the app op has been accessed.
+     * This class represents historical app op state of all UIDs for a given time interval.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final class HistoricalOps implements Parcelable {
+        private long mBeginTimeMillis;
+        private long mEndTimeMillis;
+        private @Nullable SparseArray<HistoricalUidOps> mHistoricalUidOps;
+
+        /** @hide */
+        @TestApi
+        public HistoricalOps(long beginTimeMillis, long endTimeMillis) {
+            Preconditions.checkState(beginTimeMillis <= endTimeMillis);
+            mBeginTimeMillis = beginTimeMillis;
+            mEndTimeMillis = endTimeMillis;
+        }
+
+        /** @hide */
+        public HistoricalOps(@NonNull HistoricalOps other) {
+            mBeginTimeMillis = other.mBeginTimeMillis;
+            mEndTimeMillis = other.mEndTimeMillis;
+            Preconditions.checkState(mBeginTimeMillis <= mEndTimeMillis);
+            if (other.mHistoricalUidOps != null) {
+                final int opCount = other.getUidCount();
+                for (int i = 0; i < opCount; i++) {
+                    final HistoricalUidOps origOps = other.getUidOpsAt(i);
+                    final HistoricalUidOps clonedOps = new HistoricalUidOps(origOps);
+                    if (mHistoricalUidOps == null) {
+                        mHistoricalUidOps = new SparseArray<>(opCount);
+                    }
+                    mHistoricalUidOps.put(clonedOps.getUid(), clonedOps);
+                }
+            }
+        }
+
+        private HistoricalOps(Parcel parcel) {
+            mBeginTimeMillis = parcel.readLong();
+            mEndTimeMillis = parcel.readLong();
+            final int[] uids = parcel.createIntArray();
+            if (!ArrayUtils.isEmpty(uids)) {
+                final ParceledListSlice<HistoricalUidOps> listSlice = parcel.readParcelable(
+                        HistoricalOps.class.getClassLoader());
+                final List<HistoricalUidOps> uidOps = (listSlice != null)
+                        ? listSlice.getList() : null;
+                if (uidOps == null) {
+                    return;
+                }
+                for (int i = 0; i < uids.length; i++) {
+                    if (mHistoricalUidOps == null) {
+                        mHistoricalUidOps = new SparseArray<>();
+                    }
+                    mHistoricalUidOps.put(uids[i], uidOps.get(i));
+                }
+            }
+        }
+
+        /**
+         * Splice a piece from the beginning of these ops.
+         *
+         * @param splicePoint The fraction of the data to be spliced off.
+         *
+         * @hide
+         */
+        public @NonNull HistoricalOps spliceFromBeginning(double splicePoint) {
+            return splice(splicePoint, true);
+        }
+
+        /**
+         * Splice a piece from the end of these ops.
+         *
+         * @param fractionToRemove The fraction of the data to be spliced off.
+         *
+         * @hide
+         */
+        public @NonNull HistoricalOps spliceFromEnd(double fractionToRemove) {
+            return splice(fractionToRemove, false);
+        }
+
+        /**
+         * Splice a piece from the beginning or end of these ops.
+         *
+         * @param fractionToRemove The fraction of the data to be spliced off.
+         * @param beginning Whether to splice off the beginning or the end.
+         *
+         * @return The spliced off part.
+         *
+         * @hide
+         */
+        private @Nullable HistoricalOps splice(double fractionToRemove, boolean beginning) {
+            final long spliceBeginTimeMills;
+            final long spliceEndTimeMills;
+            if (beginning) {
+                spliceBeginTimeMills = mBeginTimeMillis;
+                spliceEndTimeMills = (long) (mBeginTimeMillis
+                        + getDurationMillis() * fractionToRemove);
+                mBeginTimeMillis = spliceEndTimeMills;
+            } else {
+                spliceBeginTimeMills = (long) (mEndTimeMillis
+                        - getDurationMillis() * fractionToRemove);
+                spliceEndTimeMills = mEndTimeMillis;
+                mEndTimeMillis = spliceBeginTimeMills;
+            }
+
+            HistoricalOps splice = null;
+            final int uidCount = getUidCount();
+            for (int i = 0; i < uidCount; i++) {
+                final HistoricalUidOps origOps = getUidOpsAt(i);
+                final HistoricalUidOps spliceOps = origOps.splice(fractionToRemove);
+                if (spliceOps != null) {
+                    if (splice == null) {
+                        splice = new HistoricalOps(spliceBeginTimeMills, spliceEndTimeMills);
+                    }
+                    if (splice.mHistoricalUidOps == null) {
+                        splice.mHistoricalUidOps = new SparseArray<>();
+                    }
+                    splice.mHistoricalUidOps.put(spliceOps.getUid(), spliceOps);
+                }
+            }
+            return splice;
+        }
+
+        /**
+         * Merge the passed ops into the current ones. The time interval is a
+         * union of the current and passed in one and the passed in data is
+         * folded into the data of this instance.
+         *
+         * @hide
+         */
+        public void merge(@NonNull HistoricalOps other) {
+            mBeginTimeMillis = Math.min(mBeginTimeMillis, other.mBeginTimeMillis);
+            mEndTimeMillis = Math.max(mEndTimeMillis, other.mEndTimeMillis);
+            final int uidCount = other.getUidCount();
+            for (int i = 0; i < uidCount; i++) {
+                final HistoricalUidOps otherUidOps = other.getUidOpsAt(i);
+                final HistoricalUidOps thisUidOps = getUidOps(otherUidOps.getUid());
+                if (thisUidOps != null) {
+                    thisUidOps.merge(otherUidOps);
+                } else {
+                    if (mHistoricalUidOps == null) {
+                        mHistoricalUidOps = new SparseArray<>();
+                    }
+                    mHistoricalUidOps.put(otherUidOps.getUid(), otherUidOps);
+                }
+            }
+        }
+
+        /**
+         * AppPermissionUsage the ops to leave only the data we filter for.
+         *
+         * @param uid Uid to filter for or {@link android.os.Process#INCIDENTD_UID} for all.
+         * @param packageName Package to filter for or null for all.
+         * @param opNames Ops to filter for or null for all.
+         * @param beginTimeMillis The begin time to filter for or {@link Long#MIN_VALUE} for all.
+         * @param endTimeMillis The end time to filter for or {@link Long#MAX_VALUE} for all.
+         *
+         * @hide
+         */
+        public void filter(int uid, @Nullable String packageName, @Nullable String[] opNames,
+                long beginTimeMillis, long endTimeMillis) {
+            final long durationMillis = getDurationMillis();
+            mBeginTimeMillis = Math.max(mBeginTimeMillis, beginTimeMillis);
+            mEndTimeMillis = Math.min(mEndTimeMillis, endTimeMillis);
+            final double scaleFactor = Math.min((double) (endTimeMillis - beginTimeMillis)
+                    / (double) durationMillis, 1);
+            final int uidCount = getUidCount();
+            for (int i = uidCount - 1; i >= 0; i--) {
+                final HistoricalUidOps uidOp = mHistoricalUidOps.valueAt(i);
+                if (uid != Process.INVALID_UID && uid != uidOp.getUid()) {
+                    mHistoricalUidOps.removeAt(i);
+                } else {
+                    uidOp.filter(packageName, opNames, scaleFactor);
+                }
+            }
+        }
+
+        /** @hide */
+        public boolean isEmpty() {
+            if (getBeginTimeMillis() >= getEndTimeMillis()) {
+                return true;
+            }
+            final int uidCount = getUidCount();
+            for (int i = uidCount - 1; i >= 0; i--) {
+                final HistoricalUidOps uidOp = mHistoricalUidOps.valueAt(i);
+                if (!uidOp.isEmpty()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /** @hide */
+        public long getDurationMillis() {
+            return mEndTimeMillis - mBeginTimeMillis;
+        }
+
+        /** @hide */
+        @TestApi
+        public void increaseAccessCount(int opCode, int uid, @NonNull String packageName,
+                @UidState int uidState, long increment) {
+            getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode,
+                    packageName, uidState, increment);
+        }
+
+        /** @hide */
+        @TestApi
+        public void increaseRejectCount(int opCode, int uid, @NonNull String packageName,
+                @UidState int uidState, long increment) {
+            getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode,
+                    packageName, uidState, increment);
+        }
+
+        /** @hide */
+        @TestApi
+        public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName,
+                @UidState int uidState, long increment) {
+            getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode,
+                    packageName, uidState, increment);
+        }
+
+        /** @hide */
+        @TestApi
+        public void offsetBeginAndEndTime(long offsetMillis) {
+            mBeginTimeMillis += offsetMillis;
+            mEndTimeMillis += offsetMillis;
+        }
+
+        /** @hide */
+        public void setBeginAndEndTime(long beginTimeMillis, long endTimeMillis) {
+            mBeginTimeMillis = beginTimeMillis;
+            mEndTimeMillis = endTimeMillis;
+        }
+
+        /** @hide */
+        public void setBeginTime(long beginTimeMillis) {
+            mBeginTimeMillis = beginTimeMillis;
+        }
+
+        /** @hide */
+        public void setEndTime(long endTimeMillis) {
+            mEndTimeMillis = endTimeMillis;
+        }
+
+        /**
+         * @return The beginning of the interval in milliseconds since
+         *    epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         */
+        public long getBeginTimeMillis() {
+            return mBeginTimeMillis;
+        }
+
+        /**
+         * @return The end of the interval in milliseconds since
+         *    epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         */
+        public long getEndTimeMillis() {
+            return mEndTimeMillis;
+        }
+
+        /**
+         * Gets number of UIDs with historical ops.
+         *
+         * @return The number of UIDs with historical ops.
+         *
+         * @see #getUidOpsAt(int)
+         */
+        public int getUidCount() {
+            if (mHistoricalUidOps == null) {
+                return 0;
+            }
+            return mHistoricalUidOps.size();
+        }
+
+        /**
+         * Gets the historical UID ops at a given index.
+         *
+         * @param index The index.
+         *
+         * @return The historical UID ops at the given index.
+         *
+         * @see #getUidCount()
+         */
+        public @NonNull HistoricalUidOps getUidOpsAt(int index) {
+            if (mHistoricalUidOps == null) {
+                throw new IndexOutOfBoundsException();
+            }
+            return mHistoricalUidOps.valueAt(index);
+        }
+
+        /**
+         * Gets the historical UID ops for a given UID.
+         *
+         * @param uid The UID.
+         *
+         * @return The historical ops for the UID.
+         */
+        public @Nullable HistoricalUidOps getUidOps(int uid) {
+            if (mHistoricalUidOps == null) {
+                return null;
+            }
+            return mHistoricalUidOps.get(uid);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeLong(mBeginTimeMillis);
+            parcel.writeLong(mEndTimeMillis);
+            if (mHistoricalUidOps != null) {
+                final int uidCount = mHistoricalUidOps.size();
+                parcel.writeInt(uidCount);
+                for (int i = 0; i < uidCount; i++) {
+                    parcel.writeInt(mHistoricalUidOps.keyAt(i));
+                }
+                final List<HistoricalUidOps> opsList = new ArrayList<>(uidCount);
+                for (int i = 0; i < uidCount; i++) {
+                    opsList.add(mHistoricalUidOps.valueAt(i));
+                }
+                parcel.writeParcelable(new ParceledListSlice<>(opsList), flags);
+            } else {
+                parcel.writeInt(-1);
+            }
+        }
+
+        /**
+         * Accepts a visitor to traverse the ops tree.
+         *
+         * @param visitor The visitor.
+         *
+         * @hide
+         */
+        public void accept(@NonNull HistoricalOpsVisitor visitor) {
+            visitor.visitHistoricalOps(this);
+            final int uidCount = getUidCount();
+            for (int i = 0; i < uidCount; i++) {
+                getUidOpsAt(i).accept(visitor);
+            }
+        }
+
+        private @NonNull HistoricalUidOps getOrCreateHistoricalUidOps(int uid) {
+            if (mHistoricalUidOps == null) {
+                mHistoricalUidOps = new SparseArray<>();
+            }
+            HistoricalUidOps historicalUidOp = mHistoricalUidOps.get(uid);
+            if (historicalUidOp == null) {
+                historicalUidOp = new HistoricalUidOps(uid);
+                mHistoricalUidOps.put(uid, historicalUidOp);
+            }
+            return historicalUidOp;
+        }
+
+        /**
+         * @return Rounded value up at the 0.5 boundary.
+         *
+         * @hide
+         */
+        public static double round(double value) {
+            final BigDecimal decimalScale = new BigDecimal(value);
+            return decimalScale.setScale(0, RoundingMode.HALF_UP).doubleValue();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            final HistoricalOps other = (HistoricalOps) obj;
+            if (mBeginTimeMillis != other.mBeginTimeMillis) {
+                return false;
+            }
+            if (mEndTimeMillis != other.mEndTimeMillis) {
+                return false;
+            }
+            if (mHistoricalUidOps == null) {
+                if (other.mHistoricalUidOps != null) {
+                    return false;
+                }
+            } else if (!mHistoricalUidOps.equals(other.mHistoricalUidOps)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = (int) (mBeginTimeMillis ^ (mBeginTimeMillis >>> 32));
+            result = 31 * result + mHistoricalUidOps.hashCode();
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return getClass().getSimpleName() + "[from:"
+                    + mBeginTimeMillis + " to:" + mEndTimeMillis + "]";
+        }
+
+        public static final Creator<HistoricalOps> CREATOR = new Creator<HistoricalOps>() {
+            @Override
+            public @NonNull HistoricalOps createFromParcel(@NonNull Parcel parcel) {
+                return new HistoricalOps(parcel);
+            }
+
+            @Override
+            public @NonNull HistoricalOps[] newArray(int size) {
+                return new HistoricalOps[size];
+            }
+        };
+    }
+
+    /**
+     * This class represents historical app op state for a UID.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final class HistoricalUidOps implements Parcelable {
+        private final int mUid;
+        private @Nullable ArrayMap<String, HistoricalPackageOps> mHistoricalPackageOps;
+
+        /** @hide */
+        public HistoricalUidOps(int uid) {
+            mUid = uid;
+        }
+
+        private HistoricalUidOps(@NonNull HistoricalUidOps other) {
+            mUid = other.mUid;
+            final int opCount = other.getPackageCount();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalPackageOps origOps = other.getPackageOpsAt(i);
+                final HistoricalPackageOps cloneOps = new HistoricalPackageOps(origOps);
+                if (mHistoricalPackageOps == null) {
+                    mHistoricalPackageOps = new ArrayMap<>(opCount);
+                }
+                mHistoricalPackageOps.put(cloneOps.getPackageName(), cloneOps);
+            }
+        }
+
+        private HistoricalUidOps(@NonNull Parcel parcel) {
+            // No arg check since we always read from a trusted source.
+            mUid = parcel.readInt();
+            mHistoricalPackageOps = parcel.createTypedArrayMap(HistoricalPackageOps.CREATOR);
+        }
+
+        private @Nullable HistoricalUidOps splice(double fractionToRemove) {
+            HistoricalUidOps splice = null;
+            final int packageCount = getPackageCount();
+            for (int i = 0; i < packageCount; i++) {
+                final HistoricalPackageOps origOps = getPackageOpsAt(i);
+                final HistoricalPackageOps spliceOps = origOps.splice(fractionToRemove);
+                if (spliceOps != null) {
+                    if (splice == null) {
+                        splice = new HistoricalUidOps(mUid);
+                    }
+                    if (splice.mHistoricalPackageOps == null) {
+                        splice.mHistoricalPackageOps = new ArrayMap<>();
+                    }
+                    splice.mHistoricalPackageOps.put(spliceOps.getPackageName(), spliceOps);
+                }
+            }
+            return splice;
+        }
+
+        private void merge(@NonNull HistoricalUidOps other) {
+            final int packageCount = other.getPackageCount();
+            for (int i = 0; i < packageCount; i++) {
+                final HistoricalPackageOps otherPackageOps = other.getPackageOpsAt(i);
+                final HistoricalPackageOps thisPackageOps = getPackageOps(
+                        otherPackageOps.getPackageName());
+                if (thisPackageOps != null) {
+                    thisPackageOps.merge(otherPackageOps);
+                } else {
+                    if (mHistoricalPackageOps == null) {
+                        mHistoricalPackageOps = new ArrayMap<>();
+                    }
+                    mHistoricalPackageOps.put(otherPackageOps.getPackageName(), otherPackageOps);
+                }
+            }
+        }
+
+        private void filter(@Nullable String packageName, @Nullable String[] opNames,
+                double fractionToRemove) {
+            final int packageCount = getPackageCount();
+            for (int i = packageCount - 1; i >= 0; i--) {
+                final HistoricalPackageOps packageOps = getPackageOpsAt(i);
+                if (packageName != null && !packageName.equals(packageOps.getPackageName())) {
+                    mHistoricalPackageOps.removeAt(i);
+                } else {
+                    packageOps.filter(opNames, fractionToRemove);
+                }
+            }
+        }
+
+        private boolean isEmpty() {
+            final int packageCount = getPackageCount();
+            for (int i = packageCount - 1; i >= 0; i--) {
+                final HistoricalPackageOps packageOps = mHistoricalPackageOps.valueAt(i);
+                if (!packageOps.isEmpty()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private void increaseAccessCount(int opCode, @NonNull String packageName,
+                @UidState int uidState, long increment) {
+            getOrCreateHistoricalPackageOps(packageName).increaseAccessCount(
+                    opCode, uidState, increment);
+        }
+
+        private void increaseRejectCount(int opCode, @NonNull String packageName,
+                @UidState int uidState, long increment) {
+            getOrCreateHistoricalPackageOps(packageName).increaseRejectCount(
+                    opCode, uidState, increment);
+        }
+
+        private void increaseAccessDuration(int opCode, @NonNull String packageName,
+                @UidState int uidState, long increment) {
+            getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration(
+                    opCode, uidState, increment);
+        }
+
+        /**
+         * @return The UID for which the data is related.
+         */
+        public int getUid() {
+            return mUid;
+        }
+
+        /**
+         * Gets number of packages with historical ops.
+         *
+         * @return The number of packages with historical ops.
+         *
+         * @see #getPackageOpsAt(int)
+         */
+        public int getPackageCount() {
+            if (mHistoricalPackageOps == null) {
+                return 0;
+            }
+            return mHistoricalPackageOps.size();
+        }
+
+        /**
+         * Gets the historical package ops at a given index.
+         *
+         * @param index The index.
+         *
+         * @return The historical package ops at the given index.
+         *
+         * @see #getPackageCount()
+         */
+        public @NonNull HistoricalPackageOps getPackageOpsAt(int index) {
+            if (mHistoricalPackageOps == null) {
+                throw new IndexOutOfBoundsException();
+            }
+            return mHistoricalPackageOps.valueAt(index);
+        }
+
+        /**
+         * Gets the historical package ops for a given package.
+         *
+         * @param packageName The package.
+         *
+         * @return The historical ops for the package.
+         */
+        public @Nullable HistoricalPackageOps getPackageOps(@NonNull String packageName) {
+            if (mHistoricalPackageOps == null) {
+                return null;
+            }
+            return mHistoricalPackageOps.get(packageName);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeInt(mUid);
+            parcel.writeTypedArrayMap(mHistoricalPackageOps, flags);
+        }
+
+        private void accept(@NonNull HistoricalOpsVisitor visitor) {
+            visitor.visitHistoricalUidOps(this);
+            final int packageCount = getPackageCount();
+            for (int i = 0; i < packageCount; i++) {
+                getPackageOpsAt(i).accept(visitor);
+            }
+        }
+
+        private @NonNull HistoricalPackageOps getOrCreateHistoricalPackageOps(
+                @NonNull String packageName) {
+            if (mHistoricalPackageOps == null) {
+                mHistoricalPackageOps = new ArrayMap<>();
+            }
+            HistoricalPackageOps historicalPackageOp = mHistoricalPackageOps.get(packageName);
+            if (historicalPackageOp == null) {
+                historicalPackageOp = new HistoricalPackageOps(packageName);
+                mHistoricalPackageOps.put(packageName, historicalPackageOp);
+            }
+            return historicalPackageOp;
+        }
+
+
+        public static final Creator<HistoricalUidOps> CREATOR = new Creator<HistoricalUidOps>() {
+            @Override
+            public @NonNull HistoricalUidOps createFromParcel(@NonNull Parcel parcel) {
+                return new HistoricalUidOps(parcel);
+            }
+
+            @Override
+            public @NonNull HistoricalUidOps[] newArray(int size) {
+                return new HistoricalUidOps[size];
+            }
+        };
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            final HistoricalUidOps other = (HistoricalUidOps) obj;
+            if (mUid != other.mUid) {
+                return false;
+            }
+            if (mHistoricalPackageOps == null) {
+                if (other.mHistoricalPackageOps != null) {
+                    return false;
+                }
+            } else if (!mHistoricalPackageOps.equals(other.mHistoricalPackageOps)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mUid;
+            result = 31 * result + (mHistoricalPackageOps != null
+                    ? mHistoricalPackageOps.hashCode() : 0);
+            return result;
+        }
+    }
+
+    /**
+     * This class represents historical app op information about a package.
      *
      * @hide
      */
     @TestApi
     @SystemApi
     public static final class HistoricalPackageOps implements Parcelable {
-        private final int mUid;
         private final @NonNull String mPackageName;
-        private final @NonNull List<HistoricalOpEntry> mEntries;
+        private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
 
-        /**
-         * @hide
-         */
-        public HistoricalPackageOps(int uid, @NonNull String packageName) {
-            mUid = uid;
+        /** @hide */
+        public HistoricalPackageOps(@NonNull String packageName) {
             mPackageName = packageName;
-            mEntries = new ArrayList<>();
         }
 
-        HistoricalPackageOps(@NonNull Parcel parcel) {
-            mUid = parcel.readInt();
+        private HistoricalPackageOps(@NonNull HistoricalPackageOps other) {
+            mPackageName = other.mPackageName;
+            final int opCount = other.getOpCount();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalOp origOp = other.getOpAt(i);
+                final HistoricalOp cloneOp = new HistoricalOp(origOp);
+                if (mHistoricalOps == null) {
+                    mHistoricalOps = new ArrayMap<>(opCount);
+                }
+                mHistoricalOps.put(cloneOp.getOpName(), cloneOp);
+            }
+        }
+
+        private HistoricalPackageOps(@NonNull Parcel parcel) {
             mPackageName = parcel.readString();
-            mEntries = parcel.createTypedArrayList(HistoricalOpEntry.CREATOR);
+            mHistoricalOps = parcel.createTypedArrayMap(HistoricalOp.CREATOR);
         }
 
-        /**
-         * @hide
-         */
-        public void addEntry(@NonNull HistoricalOpEntry entry) {
-            mEntries.add(entry);
+        private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
+            HistoricalPackageOps splice = null;
+            final int opCount = getOpCount();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalOp origOps = getOpAt(i);
+                final HistoricalOp spliceOps = origOps.splice(fractionToRemove);
+                if (spliceOps != null) {
+                    if (splice == null) {
+                        splice = new HistoricalPackageOps(mPackageName);
+                    }
+                    if (splice.mHistoricalOps == null) {
+                        splice.mHistoricalOps = new ArrayMap<>();
+                    }
+                    splice.mHistoricalOps.put(spliceOps.getOpName(), spliceOps);
+                }
+            }
+            return splice;
+        }
+
+        private void merge(@NonNull HistoricalPackageOps other) {
+            final int opCount = other.getOpCount();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalOp otherOp = other.getOpAt(i);
+                final HistoricalOp thisOp = getOp(otherOp.getOpName());
+                if (thisOp != null) {
+                    thisOp.merge(otherOp);
+                } else {
+                    if (mHistoricalOps == null) {
+                        mHistoricalOps = new ArrayMap<>();
+                    }
+                    mHistoricalOps.put(otherOp.getOpName(), otherOp);
+                }
+            }
+        }
+
+        private void filter(@Nullable String[] opNames, double scaleFactor) {
+            final int opCount = getOpCount();
+            for (int i = opCount - 1; i >= 0; i--) {
+                final HistoricalOp op = mHistoricalOps.valueAt(i);
+                if (opNames != null && !ArrayUtils.contains(opNames, op.getOpName())) {
+                    mHistoricalOps.removeAt(i);
+                } else {
+                    op.filter(scaleFactor);
+                }
+            }
+        }
+
+        private boolean isEmpty() {
+            final int opCount = getOpCount();
+            for (int i = opCount - 1; i >= 0; i--) {
+                final HistoricalOp op = mHistoricalOps.valueAt(i);
+                if (!op.isEmpty()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private void increaseAccessCount(int opCode, @UidState int uidState, long increment) {
+            getOrCreateHistoricalOp(opCode).increaseAccessCount(uidState, increment);
+        }
+
+        private void increaseRejectCount(int opCode, @UidState int uidState, long increment) {
+            getOrCreateHistoricalOp(opCode).increaseRejectCount(uidState, increment);
+        }
+
+        private void increaseAccessDuration(int opCode, @UidState int uidState, long increment) {
+            getOrCreateHistoricalOp(opCode).increaseAccessDuration(uidState, increment);
         }
 
         /**
@@ -2133,36 +2932,33 @@
         }
 
         /**
-         *  Gets the UID which the data represents.
+         * Gets number historical app ops.
          *
-         * @return The UID which the data represents.
+         * @return The number historical app ops.
+         *
+         * @see #getOpAt(int)
          */
-        public int getUid() {
-            return mUid;
+        public int getOpCount() {
+            if (mHistoricalOps == null) {
+                return 0;
+            }
+            return mHistoricalOps.size();
         }
 
         /**
-         * Gets number historical app op entries.
-         *
-         * @return The number historical app op entries.
-         *
-         * @see #getEntryAt(int)
-         */
-        public int getEntryCount() {
-            return mEntries.size();
-        }
-
-        /**
-         * Gets the historical at a given index.
+         * Gets the historical op at a given index.
          *
          * @param index The index to lookup.
          *
-         * @return The entry at the given index.
+         * @return The op at the given index.
          *
-         * @see #getEntryCount()
+         * @see #getOpCount()
          */
-        public @NonNull HistoricalOpEntry getEntryAt(int index) {
-            return mEntries.get(index);
+        public @NonNull HistoricalOp getOpAt(int index) {
+            if (mHistoricalOps == null) {
+                throw new IndexOutOfBoundsException();
+            }
+            return mHistoricalOps.valueAt(index);
         }
 
         /**
@@ -2172,15 +2968,11 @@
          *
          * @return The historical entry for that op name.
          */
-        public @Nullable HistoricalOpEntry getEntry(@NonNull String opName) {
-            final int entryCount = mEntries.size();
-            for (int i = 0; i < entryCount; i++) {
-                final HistoricalOpEntry entry = mEntries.get(i);
-                if (entry.getOp().equals(opName)) {
-                    return entry;
-                }
+        public @Nullable HistoricalOp getOp(@NonNull String opName) {
+            if (mHistoricalOps == null) {
+                return null;
             }
-            return null;
+            return mHistoricalOps.get(opName);
         }
 
         @Override
@@ -2190,9 +2982,29 @@
 
         @Override
         public void writeToParcel(@NonNull Parcel parcel, int flags) {
-            parcel.writeInt(mUid);
             parcel.writeString(mPackageName);
-            parcel.writeTypedList(mEntries, flags);
+            parcel.writeTypedArrayMap(mHistoricalOps, flags);
+        }
+
+        private void accept(@NonNull HistoricalOpsVisitor visitor) {
+            visitor.visitHistoricalPackageOps(this);
+            final int opCount = getOpCount();
+            for (int i = 0; i < opCount; i++) {
+                getOpAt(i).accept(visitor);
+            }
+        }
+
+        private @NonNull HistoricalOp getOrCreateHistoricalOp(int opCode) {
+            if (mHistoricalOps == null) {
+                mHistoricalOps = new ArrayMap<>();
+            }
+            final String opStr = sOpToString[opCode];
+            HistoricalOp op = mHistoricalOps.get(opStr);
+            if (op == null) {
+                op = new HistoricalOp(opCode);
+                mHistoricalOps.put(opStr, op);
+            }
+            return op;
         }
 
         public static final Creator<HistoricalPackageOps> CREATOR =
@@ -2207,49 +3019,177 @@
                 return new HistoricalPackageOps[size];
             }
         };
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            final HistoricalPackageOps other = (HistoricalPackageOps) obj;
+            if (!mPackageName.equals(other.mPackageName)) {
+                return false;
+            }
+            if (mHistoricalOps == null) {
+                if (other.mHistoricalOps != null) {
+                    return false;
+                }
+            } else if (!mHistoricalOps.equals(other.mHistoricalOps)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mPackageName != null ? mPackageName.hashCode() : 0;
+            result = 31 * result + (mHistoricalOps != null ? mHistoricalOps.hashCode() : 0);
+            return result;
+        }
     }
 
     /**
-     * This class represents historical information about an app op. The history
-     * is aggregated information about the op for a certain amount of time such
-     * as the times the op was accessed, the times the op was rejected, the total
-     * duration the app op has been accessed.
+     * This class represents historical information about an app op.
      *
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final class HistoricalOpEntry implements Parcelable {
+    public static final class HistoricalOp implements Parcelable {
         private final int mOp;
-        private final long[] mAccessCount;
-        private final long[] mRejectCount;
-        private final long[] mAccessDuration;
+        private @Nullable long[] mAccessCount;
+        private @Nullable long[] mRejectCount;
+        private @Nullable long[] mAccessDuration;
 
-        /**
-         * @hide
-         */
-        public HistoricalOpEntry(int op) {
+        /** @hide */
+        public HistoricalOp(int op) {
             mOp = op;
             mAccessCount = new long[_NUM_UID_STATE];
             mRejectCount = new long[_NUM_UID_STATE];
             mAccessDuration = new long[_NUM_UID_STATE];
         }
 
-        HistoricalOpEntry(@NonNull Parcel parcel) {
+        private HistoricalOp(@NonNull HistoricalOp other) {
+            mOp = other.mOp;
+            if (other.mAccessCount != null) {
+                System.arraycopy(other.mAccessCount, 0, getOrCreateAccessCount(),
+                        0, other.mAccessCount.length);
+            }
+            if (other.mRejectCount != null) {
+                System.arraycopy(other.mRejectCount, 0, getOrCreateRejectCount(),
+                        0, other.mRejectCount.length);
+            }
+            if (other.mAccessDuration != null) {
+                System.arraycopy(other.mAccessDuration, 0, getOrCreateAccessDuration(),
+                        0, other.mAccessDuration.length);
+            }
+        }
+
+        private HistoricalOp(@NonNull Parcel parcel) {
             mOp = parcel.readInt();
             mAccessCount = parcel.createLongArray();
             mRejectCount = parcel.createLongArray();
             mAccessDuration = parcel.createLongArray();
         }
 
-        /**
-         * @hide
-         */
-        public void addEntry(@UidState int uidState, long accessCount,
-                long rejectCount, long accessDuration) {
-            mAccessCount[uidState] = accessCount;
-            mRejectCount[uidState] = rejectCount;
-            mAccessDuration[uidState] = accessDuration;
+        private void filter(double scaleFactor) {
+            scale(mAccessCount, scaleFactor);
+            scale(mRejectCount, scaleFactor);
+            scale(mAccessDuration, scaleFactor);
+        }
+
+        private boolean isEmpty() {
+            return !hasData(mAccessCount)
+                    && !hasData(mRejectCount)
+                    && !hasData(mAccessDuration);
+        }
+
+        private boolean hasData(@NonNull long[] array) {
+            for (long value : array) {
+                if (value != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private @Nullable HistoricalOp splice(double fractionToRemove) {
+            HistoricalOp splice = null;
+            if (mAccessCount != null) {
+                for (int i = 0; i < _NUM_UID_STATE; i++) {
+                    final long spliceAccessCount = Math.round(
+                            mAccessCount[i] * fractionToRemove);
+                    if (spliceAccessCount > 0) {
+                        if (splice == null) {
+                            splice = new HistoricalOp(mOp);
+                        }
+                        splice.getOrCreateAccessCount()[i] = spliceAccessCount;
+                        mAccessCount[i] -= spliceAccessCount;
+                    }
+                }
+            }
+
+            if (mRejectCount != null) {
+                for (int i = 0; i < _NUM_UID_STATE; i++) {
+                    final long spliceRejectCount = Math.round(
+                            mRejectCount[i] * fractionToRemove);
+
+                    if (spliceRejectCount > 0) {
+                        if (splice == null) {
+                            splice = new HistoricalOp(mOp);
+                        }
+                        splice.getOrCreateRejectCount()[i] = spliceRejectCount;
+                        mRejectCount[i] -= spliceRejectCount;
+                    }
+                }
+            }
+
+            if (mAccessDuration != null) {
+                for (int i = 0; i < _NUM_UID_STATE; i++) {
+                    final long spliceAccessDuration =  Math.round(
+                            mAccessDuration[i] * fractionToRemove);
+                    if (spliceAccessDuration > 0) {
+                        if (splice == null) {
+                            splice = new HistoricalOp(mOp);
+                        }
+                        splice.getOrCreateAccessDuration()[i] = spliceAccessDuration;
+                        mAccessDuration[i] -= spliceAccessDuration;
+                    }
+                }
+            }
+            return splice;
+        }
+
+        private void merge(@NonNull HistoricalOp other) {
+            if (other.mAccessCount != null) {
+                for (int i = 0; i < _NUM_UID_STATE; i++) {
+                    getOrCreateAccessCount()[i] += other.mAccessCount[i];
+                }
+            }
+            if (other.mRejectCount != null) {
+                for (int i = 0; i < _NUM_UID_STATE; i++) {
+                    getOrCreateRejectCount()[i] += other.mRejectCount[i];
+                }
+            }
+            if (other.mAccessDuration != null) {
+                for (int i = 0; i < _NUM_UID_STATE; i++) {
+                    getOrCreateAccessDuration()[i] += other.mAccessDuration[i];
+                }
+            }
+        }
+
+        private void increaseAccessCount(@UidState int uidState, long increment) {
+            getOrCreateAccessCount()[uidState] += increment;
+        }
+
+        private void increaseRejectCount(@UidState int uidState, long increment) {
+            getOrCreateRejectCount()[uidState] += increment;
+        }
+
+        private void increaseAccessDuration(@UidState int uidState, long increment) {
+            getOrCreateAccessDuration()[uidState] += increment;
         }
 
         /**
@@ -2257,10 +3197,15 @@
          *
          * @return The op name.
          */
-        public @NonNull String getOp() {
+        public @NonNull String getOpName() {
             return sOpToString[mOp];
         }
 
+        /** @hide */
+        public int getOpCode() {
+            return mOp;
+        }
+
         /**
          * Gets the number times the op was accessed (performed) in the foreground.
          *
@@ -2270,6 +3215,9 @@
          * @see #getAccessCount(int)
          */
         public long getForegroundAccessCount() {
+            if (mAccessCount == null) {
+                return 0;
+            }
             return sum(mAccessCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
         }
 
@@ -2282,6 +3230,9 @@
          * @see #getAccessCount(int)
          */
         public long getBackgroundAccessCount() {
+            if (mAccessCount == null) {
+                return 0;
+            }
             return sum(mAccessCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
         }
 
@@ -2299,6 +3250,9 @@
          * @see #getBackgroundAccessCount()
          */
         public long getAccessCount(@UidState int uidState) {
+            if (mAccessCount == null) {
+                return 0;
+            }
             return mAccessCount[uidState];
         }
 
@@ -2311,6 +3265,9 @@
          * @see #getRejectCount(int)
          */
         public long getForegroundRejectCount() {
+            if (mRejectCount == null) {
+                return 0;
+            }
             return sum(mRejectCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
         }
 
@@ -2323,6 +3280,9 @@
          * @see #getRejectCount(int)
          */
         public long getBackgroundRejectCount() {
+            if (mRejectCount == null) {
+                return 0;
+            }
             return sum(mRejectCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
         }
 
@@ -2340,6 +3300,9 @@
          * @see #getBackgroundRejectCount()
          */
         public long getRejectCount(@UidState int uidState) {
+            if (mRejectCount == null) {
+                return 0;
+            }
             return mRejectCount[uidState];
         }
 
@@ -2352,6 +3315,9 @@
          * @see #getAccessDuration(int)
          */
         public long getForegroundAccessDuration() {
+            if (mAccessDuration == null) {
+                return 0;
+            }
             return sum(mAccessDuration, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
         }
 
@@ -2364,6 +3330,9 @@
          * @see #getAccessDuration(int)
          */
         public long getBackgroundAccessDuration() {
+            if (mAccessDuration == null) {
+                return 0;
+            }
             return sum(mAccessDuration, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
         }
 
@@ -2381,6 +3350,9 @@
          * @see #getBackgroundAccessDuration()
          */
         public long getAccessDuration(@UidState int uidState) {
+            if (mAccessDuration == null) {
+                return 0;
+            }
             return mAccessDuration[uidState];
         }
 
@@ -2397,34 +3369,29 @@
             parcel.writeLongArray(mAccessDuration);
         }
 
-        @Override
-        public boolean equals(Object other) {
-            if (this == other) {
-                return true;
-            }
-            if (other == null || getClass() != other.getClass()) {
-                return false;
-            }
-            final HistoricalOpEntry otherInstance = (HistoricalOpEntry) other;
-            if (mOp != otherInstance.mOp) {
-                return false;
-            }
-            if (!Arrays.equals(mAccessCount, otherInstance.mAccessCount)) {
-                return false;
-            }
-            if (!Arrays.equals(mRejectCount, otherInstance.mRejectCount)) {
-                return false;
-            }
-            return Arrays.equals(mAccessDuration, otherInstance.mAccessDuration);
+        private void accept(@NonNull HistoricalOpsVisitor visitor) {
+            visitor.visitHistoricalOp(this);
         }
 
-        @Override
-        public int hashCode() {
-            int result = mOp;
-            result = 31 * result + Arrays.hashCode(mAccessCount);
-            result = 31 * result + Arrays.hashCode(mRejectCount);
-            result = 31 * result + Arrays.hashCode(mAccessDuration);
-            return result;
+        private @NonNull long[] getOrCreateAccessCount() {
+            if (mAccessCount == null) {
+                mAccessCount = new long[_NUM_UID_STATE];
+            }
+            return mAccessCount;
+        }
+
+        private @NonNull long[] getOrCreateRejectCount() {
+            if (mRejectCount == null) {
+                mRejectCount = new long[_NUM_UID_STATE];
+            }
+            return mRejectCount;
+        }
+
+        private @NonNull long[] getOrCreateAccessDuration() {
+            if (mAccessDuration == null) {
+                mAccessDuration = new long[_NUM_UID_STATE];
+            }
+            return mAccessDuration;
         }
 
         /**
@@ -2444,17 +3411,62 @@
             return totalCount;
         }
 
-        public static final Creator<HistoricalOpEntry> CREATOR = new Creator<HistoricalOpEntry>() {
+        /**
+         * Multiplies the entries in the array with the passed in scale factor and
+         * rounds the result at up 0.5 boundary.
+         *
+         * @param data The data to scale.
+         * @param scaleFactor The scale factor.
+         */
+        private static void scale(@NonNull long[] data, double scaleFactor) {
+            if (data != null) {
+                for (int i = 0; i < _NUM_UID_STATE; i++) {
+                    data[i] = (long) HistoricalOps.round((double) data[i] * scaleFactor);
+                }
+            }
+        }
+
+        public static final Creator<HistoricalOp> CREATOR = new Creator<HistoricalOp>() {
             @Override
-            public @NonNull HistoricalOpEntry createFromParcel(@NonNull Parcel source) {
-                return new HistoricalOpEntry(source);
+            public @NonNull HistoricalOp createFromParcel(@NonNull Parcel source) {
+                return new HistoricalOp(source);
             }
 
             @Override
-            public @NonNull HistoricalOpEntry[] newArray(int size) {
-                return new HistoricalOpEntry[size];
+            public @NonNull HistoricalOp[] newArray(int size) {
+                return new HistoricalOp[size];
             }
         };
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            final HistoricalOp other = (HistoricalOp) obj;
+            if (mOp != other.mOp) {
+                return false;
+            }
+            if (!Arrays.equals(mAccessCount, other.mAccessCount)) {
+                return false;
+            }
+            if (!Arrays.equals(mRejectCount, other.mRejectCount)) {
+                return false;
+            }
+            return Arrays.equals(mAccessDuration, other.mAccessDuration);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mOp;
+            result = 31 * result + Arrays.hashCode(mAccessCount);
+            result = 31 * result + Arrays.hashCode(mRejectCount);
+            result = 31 * result + Arrays.hashCode(mAccessDuration);
+            return result;
+        }
     }
 
     /**
@@ -2520,6 +3532,24 @@
      * @param ops The set of operations you are interested in, or null if you want all of them.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
+    public @NonNull List<AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[] ops) {
+        final int opCount = ops.length;
+        final int[] opCodes = new int[opCount];
+        for (int i = 0; i < opCount; i++) {
+            opCodes[i] = sOpStrToOp.get(ops[i]);
+        }
+        final List<AppOpsManager.PackageOps> result = getPackagesForOps(opCodes);
+        return (result != null) ? result : Collections.emptyList();
+    }
+
+    /**
+     * Retrieve current operation state for all applications.
+     *
+     * @param ops The set of operations you are interested in, or null if you want all of them.
+     * @hide
+     */
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
     @UnsupportedAppUsage
     public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
@@ -2536,11 +3566,17 @@
      * @param uid The uid of the application of interest.
      * @param packageName The name of the application of interest.
      * @param ops The set of operations you are interested in, or null if you want all of them.
+     *
+     * @deprecated The int op codes are not stable and you should use the string based op
+     * names which are stable and namespaced. Use
+     * {@link #getOpsForPackage(int, String, String...)})}.
+     *
      * @hide
      */
+    @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
-    public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
+    public List<PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
         try {
             return mService.getOpsForPackage(uid, packageName, ops);
         } catch (RemoteException e) {
@@ -2549,7 +3585,49 @@
     }
 
     /**
-     * Retrieve historical app op stats for a package.
+     * Retrieve current operation state for one application. The UID and the
+     * package must match.
+     *
+     * @param uid The uid of the application of interest.
+     * @param packageName The name of the application of interest.
+     * @param ops The set of operations you are interested in, or null if you want all of them.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
+    public @NonNull List<AppOpsManager.PackageOps> getOpsForPackage(int uid,
+            @NonNull String packageName, @Nullable String... ops) {
+        int[] opCodes = null;
+        if (ops != null) {
+            opCodes = new int[ops.length];
+            for (int i = 0; i < ops.length; i++) {
+                opCodes[i] = strOpToOp(ops[i]);
+            }
+        }
+        try {
+            final List<PackageOps> result = mService.getOpsForPackage(uid, packageName, opCodes);
+            if (result == null) {
+                return Collections.emptyList();
+            }
+            return result;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieve historical app op stats for a period.
+     *
+     * <p>Historical data can be obtained
+     * for a specific package by specifying the <code>packageName</code> argument,
+     * for a specific UID if specifying the <code>uid</code> argument, for a
+     * specific package in a UID by specifying the <code>packageName</code>
+     * and the <code>uid</code> arguments, for all packages by passing
+     * {@link android.os.Process#INVALID_UID} and <code>null</code> for the
+     *  <code>uid</code> and <code>packageName</code> arguments, respectively.
+     *  Similarly, you can specify the <code>opNames</code> argument to get
+     *  data only for these ops or <code>null</code> for all ops.
      *
      * @param uid The UID to query for.
      * @param packageName The package to query for.
@@ -2558,10 +3636,12 @@
      *     negative.
      * @param endTimeMillis The end of the interval in milliseconds since
      *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
-     *     {@code beginTimeMillis}.
+     *     {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent
+     *     history including ops that happen while this call is in flight.
      * @param opNames The ops to query for. Pass {@code null} for all ops.
-     *
-     * @return The historical ops or {@code null} if there are no ops for this package.
+     * @param executor Executor on which to run the callback. If <code>null</code>
+     *     the callback is executed on the default executor running on the main thread.
+     * @param callback Callback on which to deliver the result.
      *
      * @throws IllegalArgumentException If any of the argument contracts is violated.
      *
@@ -2570,47 +3650,78 @@
     @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
-    public @Nullable HistoricalPackageOps getHistoricalPackagesOps(
-            int uid, @NonNull String packageName, @Nullable String[] opNames,
-            long beginTimeMillis, long endTimeMillis) {
+    public void getHistoricalOps(int uid, @Nullable String packageName,
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @NonNull Executor executor, @NonNull Consumer<HistoricalOps> callback) {
+        Preconditions.checkNotNull(executor, "executor cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
         try {
-            return mService.getHistoricalPackagesOps(uid, packageName, opNames,
-                    beginTimeMillis, endTimeMillis);
+            mService.getHistoricalOps(uid, packageName, opNames, beginTimeMillis, endTimeMillis,
+                    new RemoteCallback((result) -> {
+                final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callback.accept(ops));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Retrieve historical app op stats for all packages.
+     * Retrieve historical app op stats for a period.
      *
+     * <p>Historical data can be obtained
+     * for a specific package by specifying the <code>packageName</code> argument,
+     * for a specific UID if specifying the <code>uid</code> argument, for a
+     * specific package in a UID by specifying the <code>packageName</code>
+     * and the <code>uid</code> arguments, for all packages by passing
+     * {@link android.os.Process#INVALID_UID} and <code>null</code> for the
+     *  <code>uid</code> and <code>packageName</code> arguments, respectively.
+     *  Similarly, you can specify the <code>opNames</code> argument to get
+     *  data only for these ops or <code>null</code> for all ops.
+     *  <p>
+     *  This method queries only the on disk state and the returned ops are raw,
+     *  which is their times are relative to the history start as opposed to the
+     *  epoch start.
+     *
+     * @param uid The UID to query for.
+     * @param packageName The package to query for.
      * @param beginTimeMillis The beginning of the interval in milliseconds since
-     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
-     *     negative.
+     *      history start. History time grows as one goes into the past.
      * @param endTimeMillis The end of the interval in milliseconds since
-     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
+     *      history start. History time grows as one goes into the past. Must be after
      *     {@code beginTimeMillis}.
      * @param opNames The ops to query for. Pass {@code null} for all ops.
-     *
-     * @return The historical ops or an empty list if there are no ops for any package.
+     * @param executor Executor on which to run the callback. If <code>null</code>
+     *     the callback is executed on the default executor running on the main thread.
+     * @param callback Callback on which to deliver the result.
      *
      * @throws IllegalArgumentException If any of the argument contracts is violated.
      *
      * @hide
      */
     @TestApi
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
-    public @NonNull List<HistoricalPackageOps> getAllHistoricPackagesOps(
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis) {
+    public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @Nullable Executor executor, @NonNull Consumer<HistoricalOps> callback) {
+        Preconditions.checkNotNull(executor, "executor cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
         try {
-            @SuppressWarnings("unchecked")
-            final ParceledListSlice<HistoricalPackageOps> payload =
-                    mService.getAllHistoricalPackagesOps(opNames, beginTimeMillis, endTimeMillis);
-            if (payload != null) {
-                return payload.getList();
-            }
-            return Collections.emptyList();
+            mService.getHistoricalOpsFromDiskRaw(uid, packageName, opNames, beginTimeMillis,
+                    endTimeMillis, new RemoteCallback((result) -> {
+               final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callback.accept(ops));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2627,7 +3738,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
-    public void setUidMode(int code, int uid, int mode) {
+    public void setUidMode(int code, int uid, @Mode int mode) {
         try {
             mService.setUidMode(code, uid, mode);
         } catch (RemoteException e) {
@@ -2648,7 +3759,7 @@
     @SystemApi
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
-    public void setUidMode(String appOp, int uid, int mode) {
+    public void setUidMode(String appOp, int uid, @Mode int mode) {
         try {
             mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
         } catch (RemoteException e) {
@@ -2680,7 +3791,7 @@
     /** @hide */
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
-    public void setMode(int code, int uid, String packageName, int mode) {
+    public void setMode(int code, int uid, String packageName, @Mode int mode) {
         try {
             mService.setMode(code, uid, packageName, mode);
         } catch (RemoteException e) {
@@ -2701,7 +3812,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
-    public void setMode(String op, int uid, String packageName, int mode) {
+    public void setMode(String op, int uid, String packageName, @Mode int mode) {
         try {
             mService.setMode(strOpToOp(op), uid, packageName, mode);
         } catch (RemoteException e) {
@@ -2722,7 +3833,7 @@
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     @UnsupportedAppUsage
-    public void setRestriction(int code, @AttributeUsage int usage, int mode,
+    public void setRestriction(int code, @AttributeUsage int usage, @Mode int mode,
             String[] exceptionPackages) {
         try {
             final int uid = Binder.getCallingUid();
@@ -3507,6 +4618,104 @@
     }
 
     /**
+     * Configures the app ops persistence for testing.
+     *
+     * @param mode The mode in which the historical registry operates.
+     * @param baseSnapshotInterval The base interval on which we would be persisting a snapshot of
+     *   the historical data. The history is recursive where every subsequent step encompasses
+     *   {@code compressionStep} longer interval with {@code compressionStep} distance between
+     *    snapshots.
+     * @param compressionStep The compression step in every iteration.
+     *
+     * @see #HISTORICAL_MODE_DISABLED
+     * @see #HISTORICAL_MODE_ENABLED_ACTIVE
+     * @see #HISTORICAL_MODE_ENABLED_PASSIVE
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+    public void setHistoryParameters(@HistoricalMode int mode, long baseSnapshotInterval,
+            int compressionStep) {
+        try {
+            mService.setHistoryParameters(mode, baseSnapshotInterval, compressionStep);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Offsets the history by the given duration.
+     *
+     * @param offsetMillis The offset duration.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+    public void offsetHistory(long offsetMillis) {
+        try {
+            mService.offsetHistory(offsetMillis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Adds ops to the history directly. This could be useful for testing especially
+     * when the historical registry operates in {@link #HISTORICAL_MODE_ENABLED_PASSIVE}
+     * mode.
+     *
+     * @param ops The ops to add to the history.
+     *
+     * @see #setHistoryParameters(int, long, int)
+     * @see #HISTORICAL_MODE_ENABLED_PASSIVE
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+    public void addHistoricalOps(@NonNull HistoricalOps ops) {
+        try {
+            mService.addHistoricalOps(ops);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resets the app ops persistence for testing.
+     *
+     * @see #setHistoryParameters(int, long, int)
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+    public void resetHistoryParameters() {
+        try {
+            mService.resetHistoryParameters();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Clears all app ops history.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+    public void clearHistory() {
+        try {
+            mService.clearHistory();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns all supported operation names.
      * @hide
      */
@@ -3538,4 +4747,64 @@
         }
         return time;
     }
+
+    /** @hide */
+    public static String uidStateToString(@UidState int uidState) {
+        switch (uidState) {
+            case UID_STATE_PERSISTENT: {
+                return "UID_STATE_PERSISTENT";
+            }
+            case UID_STATE_TOP: {
+                return "UID_STATE_TOP";
+            }
+            case UID_STATE_FOREGROUND_SERVICE: {
+                return "UID_STATE_FOREGROUND_SERVICE";
+            }
+            case UID_STATE_FOREGROUND: {
+                return "UID_STATE_FOREGROUND";
+            }
+            case UID_STATE_BACKGROUND: {
+                return "UID_STATE_BACKGROUND";
+            }
+            case UID_STATE_CACHED: {
+                return "UID_STATE_CACHED";
+            }
+            default: {
+                return "UNKNOWN";
+            }
+        }
+    }
+
+    /** @hide */
+    public static int parseHistoricalMode(@NonNull String mode) {
+        switch (mode) {
+            case "HISTORICAL_MODE_ENABLED_ACTIVE": {
+                return HISTORICAL_MODE_ENABLED_ACTIVE;
+            }
+            case "HISTORICAL_MODE_ENABLED_PASSIVE": {
+                return HISTORICAL_MODE_ENABLED_PASSIVE;
+            }
+            default: {
+                return HISTORICAL_MODE_DISABLED;
+            }
+        }
+    }
+
+    /** @hide */
+    public static String historicalModeToString(@HistoricalMode int mode) {
+        switch (mode) {
+            case HISTORICAL_MODE_DISABLED: {
+                return "HISTORICAL_MODE_DISABLED";
+            }
+            case HISTORICAL_MODE_ENABLED_ACTIVE: {
+                return "HISTORICAL_MODE_ENABLED_ACTIVE";
+            }
+            case HISTORICAL_MODE_ENABLED_PASSIVE: {
+                return "HISTORICAL_MODE_ENABLED_PASSIVE";
+            }
+            default: {
+                return "UNKNOWN";
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 94983e1..9bcb36f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2276,6 +2276,16 @@
     }
 
     @Override
+    public String[] setDistractingPackageRestrictions(String[] packages, int distractionFlags) {
+        try {
+            return mPM.setDistractingPackageRestrictionsAsUser(packages, distractionFlags,
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
             PersistableBundle appExtras, PersistableBundle launcherExtras,
             String dialogMessage) {
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index b5295d1..07dbb6b 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -124,6 +124,10 @@
     /** Restart the activity after it was stopped. */
     public abstract void performRestartActivity(IBinder token, boolean start);
 
+    /** Set pending activity configuration in case it will be updated by other transaction item. */
+    public abstract void updatePendingActivityConfiguration(IBinder activityToken,
+            Configuration overrideConfig);
+
     /** Deliver activity (override) configuration change. */
     public abstract void handleActivityConfigurationChanged(IBinder activityToken,
             Configuration overrideConfig, int displayId);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 4bd935c..088c245 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -31,7 +31,7 @@
 import android.content.DialogInterface;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
-import android.content.res.ResourceId;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Build;
@@ -183,7 +183,7 @@
 
     Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
         if (createContextThemeWrapper) {
-            if (themeResId == ResourceId.ID_NULL) {
+            if (themeResId == Resources.ID_NULL) {
                 final TypedValue outValue = new TypedValue();
                 context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                 themeResId = outValue.resourceId;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fb519b6..fb65da1 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -317,7 +317,6 @@
      */
     void requestWifiBugReport(in String shareTitle, in String shareDescription);
 
-    void clearPendingBackup();
     Intent getIntentForIntentSender(in IIntentSender sender);
     // This is not public because you need to be very careful in how you
     // manage your activity to make sure it is always the uid you expect.
@@ -421,7 +420,6 @@
     void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
-    boolean isAppForeground(int uid);
     void removeStack(int stackId);
     void makePackageIdle(String packageName, int userId);
     int getMemoryTrimLevel();
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index dd87dc3..cc6c999 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -419,6 +419,7 @@
     void updateLockTaskFeatures(int userId, int flags);
 
     void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
+    void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
     void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
 
     /**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 163be8e..199c133 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -65,9 +65,9 @@
     boolean areNotificationsEnabled(String pkg);
     int getPackageImportance(String pkg);
 
-    void setAppOverlaysAllowed(String pkg, int uid, boolean allowed);
-    boolean areAppOverlaysAllowed(String pkg);
-    boolean areAppOverlaysAllowedForPackage(String pkg, int uid);
+    void setBubblesAllowed(String pkg, int uid, boolean allowed);
+    boolean areBubblesAllowed(String pkg);
+    boolean areBubblesAllowedForPackage(String pkg, int uid);
 
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 3a2038d..666f721 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -142,19 +142,20 @@
      *
      * @param which either {@link WallpaperManager#FLAG_LOCK}
      * or {@link WallpaperManager#FLAG_SYSTEM}
+     * @param displayId Which display is interested
      * @return colors of chosen wallpaper
      */
-    WallpaperColors getWallpaperColors(int which, int userId);
+    WallpaperColors getWallpaperColors(int which, int userId, int displayId);
 
     /**
-     * Register a callback to receive color updates
+     * Register a callback to receive color updates from a display
      */
-    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
 
     /**
-     * Unregister a callback that was receiving color updates
+     * Unregister a callback that was receiving color updates from a display
      */
-    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
 
     /**
      * Called from SystemUI when it shows the AoD UI.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e066f06..b8d748d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -384,9 +384,7 @@
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
-        STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header);
         STANDARD_LAYOUTS.add(R.layout.notification_template_header);
-        STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient);
     }
 
     /**
@@ -1276,7 +1274,7 @@
     private String mShortcutId;
     private CharSequence mSettingsText;
 
-    private PendingIntent mAppOverlayIntent;
+    private BubbleMetadata mBubbleMetadata;
 
     /** @hide */
     @IntDef(prefix = { "GROUP_ALERT_" }, value = {
@@ -1427,18 +1425,13 @@
          */
         public static final int SEMANTIC_ACTION_CALL = 10;
 
-        /**
-         * {@code SemanticAction}: Contextual action - dependent on the current notification. E.g.
-         * open a Map application with an address shown in the notification.
-         */
-        public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11;
-
         private final Bundle mExtras;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         private Icon mIcon;
         private final RemoteInput[] mRemoteInputs;
         private boolean mAllowGeneratedReplies = true;
         private final @SemanticAction int mSemanticAction;
+        private final boolean mIsContextual;
 
         /**
          * Small icon representing the action.
@@ -1474,6 +1467,7 @@
             mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
             mAllowGeneratedReplies = in.readInt() == 1;
             mSemanticAction = in.readInt();
+            mIsContextual = in.readInt() == 1;
         }
 
         /**
@@ -1482,13 +1476,13 @@
         @Deprecated
         public Action(int icon, CharSequence title, PendingIntent intent) {
             this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true,
-                    SEMANTIC_ACTION_NONE);
+                    SEMANTIC_ACTION_NONE, false /* isContextual */);
         }
 
         /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
         private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
                 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
-                       @SemanticAction int semanticAction) {
+                       @SemanticAction int semanticAction, boolean isContextual) {
             this.mIcon = icon;
             if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
                 this.icon = icon.getResId();
@@ -1499,6 +1493,7 @@
             this.mRemoteInputs = remoteInputs;
             this.mAllowGeneratedReplies = allowGeneratedReplies;
             this.mSemanticAction = semanticAction;
+            this.mIsContextual = isContextual;
         }
 
         /**
@@ -1546,6 +1541,15 @@
         }
 
         /**
+         * Returns whether this is a contextual Action, i.e. whether the action is dependent on the
+         * notification message body. An example of a contextual action could be an action opening a
+         * map application with an address shown in the notification.
+         */
+        public boolean isContextual() {
+            return mIsContextual;
+        }
+
+        /**
          * Get the list of inputs to be collected from the user that ONLY accept data when this
          * action is sent. These remote inputs are guaranteed to return true on a call to
          * {@link RemoteInput#isDataOnly}.
@@ -1570,6 +1574,7 @@
             private final Bundle mExtras;
             private ArrayList<RemoteInput> mRemoteInputs;
             private @SemanticAction int mSemanticAction;
+            private boolean mIsContextual;
 
             /**
              * Construct a new builder for {@link Action} object.
@@ -1684,6 +1689,16 @@
             }
 
             /**
+             * Sets whether this {@link Action} is a contextual action, i.e. whether the action is
+             * dependent on the notification message body. An example of a contextual action could
+             * be an action opening a map application with an address shown in the notification.
+             */
+            public Builder setContextual(boolean isContextual) {
+                mIsContextual = isContextual;
+                return this;
+            }
+
+            /**
              * Apply an extender to this action builder. Extenders may be used to add
              * metadata or change options on this builder.
              */
@@ -1697,7 +1712,7 @@
              * necessary to display the action.
              */
             private void checkContextualActionNullFields() {
-                if (mSemanticAction != SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) return;
+                if (!mIsContextual) return;
 
                 if (mIcon == null) {
                     throw new NullPointerException("Contextual Actions must contain a valid icon");
@@ -1743,7 +1758,7 @@
                 RemoteInput[] textInputsArr = textInputs.isEmpty()
                         ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
                 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
-                        mAllowGeneratedReplies, mSemanticAction);
+                        mAllowGeneratedReplies, mSemanticAction, mIsContextual);
             }
         }
 
@@ -1756,7 +1771,8 @@
                     mExtras == null ? new Bundle() : new Bundle(mExtras),
                     getRemoteInputs(),
                     getAllowGeneratedReplies(),
-                    getSemanticAction());
+                    getSemanticAction(),
+                    isContextual());
         }
 
         @Override
@@ -1784,6 +1800,7 @@
             out.writeTypedArray(mRemoteInputs, flags);
             out.writeInt(mAllowGeneratedReplies ? 1 : 0);
             out.writeInt(mSemanticAction);
+            out.writeInt(mIsContextual ? 1 : 0);
         }
 
         public static final Parcelable.Creator<Action> CREATOR =
@@ -2073,8 +2090,7 @@
                 SEMANTIC_ACTION_UNMUTE,
                 SEMANTIC_ACTION_THUMBS_UP,
                 SEMANTIC_ACTION_THUMBS_DOWN,
-                SEMANTIC_ACTION_CALL,
-                SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
+                SEMANTIC_ACTION_CALL
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface SemanticAction {}
@@ -2260,7 +2276,7 @@
 
         mGroupAlertBehavior = parcel.readInt();
         if (parcel.readInt() != 0) {
-            mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+            mBubbleMetadata = BubbleMetadata.CREATOR.createFromParcel(parcel);
         }
 
         mAllowSystemGeneratedContextualActions = parcel.readBoolean();
@@ -2378,7 +2394,7 @@
         that.mBadgeIcon = this.mBadgeIcon;
         that.mSettingsText = this.mSettingsText;
         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
-        that.mAppOverlayIntent = this.mAppOverlayIntent;
+        that.mBubbleMetadata = this.mBubbleMetadata;
         that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
 
         if (!heavy) {
@@ -2701,9 +2717,9 @@
 
         parcel.writeInt(mGroupAlertBehavior);
 
-        if (mAppOverlayIntent != null) {
+        if (mBubbleMetadata != null) {
             parcel.writeInt(1);
-            mAppOverlayIntent.writeToParcel(parcel, 0);
+            mBubbleMetadata.writeToParcel(parcel, 0);
         } else {
             parcel.writeInt(0);
         }
@@ -3123,11 +3139,11 @@
     }
 
     /**
-     * Returns the intent that will be used to display app content in a floating window over the
-     * existing foreground activity.
+     * Returns the bubble metadata that will be used to display app content in a floating window
+     * over the existing foreground activity.
      */
-    public PendingIntent getAppOverlayIntent() {
-        return mAppOverlayIntent;
+    public BubbleMetadata getBubbleMetadata() {
+        return mBubbleMetadata;
     }
 
     /**
@@ -3230,8 +3246,7 @@
     }
 
     /**
-     * Returns the actions that are contextual (marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) out
-     * of the actions in this notification.
+     * Returns the actions that are contextual out of the actions in this notification.
      *
      * @hide
      */
@@ -3240,8 +3255,7 @@
 
         List<Notification.Action> contextualActions = new ArrayList<>();
         for (Notification.Action action : actions) {
-            if (action.getSemanticAction()
-                    == Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+            if (action.isContextual()) {
                 contextualActions.add(action);
             }
         }
@@ -3492,19 +3506,18 @@
         }
 
         /**
-         * Sets the intent that will be used to display app content in a floating window
-         * over the existing foreground activity.
+         * Sets the {@link BubbleMetadata} that will be used to display app content in a floating
+         * window over the existing foreground activity.
          *
-         * <p>This intent will be ignored unless this notification is posted to a channel that
-         * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p>
+         * <p>This data will be ignored unless the notification is posted to a channel that
+         * allows {@link NotificationChannel#canBubble() bubbles}.</p>
          *
-         * <p>Notifications with a valid and allowed app overlay intent will be displayed as
-         * floating windows outside of the notification shade on unlocked devices. When a user
-         * interacts with one of these windows, this app overlay intent will be invoked and
-         * displayed.</p>
+         * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state
+         * outside of the notification shade on unlocked devices. When a user interacts with the
+         * collapsed state, the bubble intent will be invoked and displayed.</b>
          */
-        public Builder setAppOverlayIntent(PendingIntent intent) {
-            mN.mAppOverlayIntent = intent;
+        public Builder setBubbleMetadata(BubbleMetadata data) {
+            mN.mBubbleMetadata = data;
             return this;
         }
 
@@ -4555,9 +4568,7 @@
             if (p.title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
-                if (!p.ambient) {
-                    setTextViewColorPrimary(contentView, R.id.title, p);
-                }
+                setTextViewColorPrimary(contentView, R.id.title, p);
                 contentView.setViewLayoutWidth(R.id.title, showProgress
                         ? ViewGroup.LayoutParams.WRAP_CONTENT
                         : ViewGroup.LayoutParams.MATCH_PARENT);
@@ -4566,9 +4577,7 @@
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
                         : com.android.internal.R.id.text;
                 contentView.setTextViewText(textId, processTextSpans(p.text));
-                if (!p.ambient) {
-                    setTextViewColorSecondary(contentView, textId, p);
-                }
+                setTextViewColorSecondary(contentView, textId, p);
                 contentView.setViewVisibility(textId, View.VISIBLE);
             }
 
@@ -4827,7 +4836,7 @@
             if (mN.mLargeIcon == null && mN.largeIcon != null) {
                 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
             }
-            boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient;
+            boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon;
             if (showLargeIcon) {
                 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
                 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
@@ -4841,7 +4850,7 @@
          * @return if the reply icon is visible
          */
         private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) {
-            boolean actionVisible = !p.hideReplyIcon && !p.ambient;
+            boolean actionVisible = !p.hideReplyIcon;
             Action action = null;
             if (actionVisible) {
                 action = findReplyAction();
@@ -4881,21 +4890,18 @@
         private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
             bindSmallIcon(contentView, p);
             bindHeaderAppName(contentView, p);
-            if (!p.ambient) {
-                // Ambient view does not have these
-                bindHeaderText(contentView, p);
-                bindHeaderTextSecondary(contentView, p);
-                bindHeaderChronometerAndTime(contentView, p);
-                bindProfileBadge(contentView, p);
-                bindAlertedIcon(contentView, p);
-            }
+            bindHeaderText(contentView, p);
+            bindHeaderTextSecondary(contentView, p);
+            bindHeaderChronometerAndTime(contentView, p);
+            bindProfileBadge(contentView, p);
+            bindAlertedIcon(contentView, p);
             bindActivePermissions(contentView, p);
             bindExpandButton(contentView, p);
             mN.mUsesStandardHeader = true;
         }
 
         private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
-            int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p);
+            int color = getNeutralColor(p);
             contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
@@ -5006,13 +5012,12 @@
             if (isColorized(p)) {
                 setTextViewColorPrimary(contentView, R.id.app_name_text, p);
             } else {
-                contentView.setTextColor(R.id.app_name_text,
-                        p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p));
+                contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p));
             }
         }
 
         private boolean isColorized(StandardTemplateParams p) {
-            return p.allowColorization && !p.ambient && mN.isColorized();
+            return p.allowColorization && mN.isColorized();
         }
 
         private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
@@ -5062,8 +5067,7 @@
                 List<Notification.Action> actions) {
             List<Notification.Action> nonContextualActions = new ArrayList<>();
             for (Notification.Action action : actions) {
-                if (action.getSemanticAction()
-                        != Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+                if (!action.isContextual()) {
                     nonContextualActions.add(action);
                 }
             }
@@ -5083,7 +5087,7 @@
             List<Notification.Action> nonContextualActions = filterOutContextualActions(mActions);
 
             int N = nonContextualActions.size();
-            boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
+            boolean emphazisedMode = mN.fullScreenIntent != null;
             big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
             if (N > 0) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
@@ -5108,7 +5112,7 @@
             }
 
             CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
-            if (!p.ambient && validRemoteInput && replyText != null
+            if (validRemoteInput && replyText != null
                     && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])
                     && p.maxRemoteInputHistory > 0) {
                 boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER);
@@ -5225,11 +5229,10 @@
          * Construct a RemoteViews for the final notification header only. This will not be
          * colorized.
          *
-         * @param ambient if true, generate the header for the ambient display layout.
          * @hide
          */
-        public RemoteViews makeNotificationHeader(boolean ambient) {
-            return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this));
+        public RemoteViews makeNotificationHeader() {
+            return makeNotificationHeader(mParams.reset().fillTextsFrom(this));
         }
 
         /**
@@ -5242,8 +5245,7 @@
             // Headers on their own are never colorized
             p.disallowColorization();
             RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
-                    p.ambient ? R.layout.notification_template_ambient_header
-                            : R.layout.notification_template_header);
+                    R.layout.notification_template_header);
             resetNotificationHeader(header);
             bindNotificationHeader(header, p);
             return header;
@@ -5255,11 +5257,7 @@
          * @hide
          */
         public RemoteViews makeAmbientNotification() {
-            RemoteViews ambient = applyStandardTemplateWithActions(
-                    R.layout.notification_template_material_ambient,
-                    mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false),
-                    null /* result */);
-            return ambient;
+            return createHeadsUpContentView(false /* increasedHeight */);
         }
 
         private void hideLine1Text(RemoteViews result) {
@@ -5363,14 +5361,8 @@
             }
             mN.extras = publicExtras;
             RemoteViews view;
-            if (ambient) {
-                publicExtras.putCharSequence(EXTRA_TITLE,
-                        mContext.getString(com.android.internal.R.string.notification_hidden_text));
-                view = makeAmbientNotification();
-            } else{
-                view = makeNotificationHeader(false /* ambient */);
-                view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
-            }
+            view = makeNotificationHeader();
+            view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
             mN.extras = savedBundle;
             mN.mLargeIcon = largeIcon;
             mN.largeIcon = largeIconLegacy;
@@ -5390,7 +5382,6 @@
         public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
             StandardTemplateParams p = mParams.reset()
                     .forceDefaultColor()
-                    .ambient(false)
                     .fillTextsFrom(this);
             if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
                 p.summaryText(createSummaryText());
@@ -5481,8 +5472,7 @@
                 if (isColorized(p)) {
                     setTextViewColorPrimary(button, R.id.action0, p);
                 } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) {
-                    button.setTextColor(R.id.action0,
-                            p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p));
+                    button.setTextColor(R.id.action0, resolveContrastColor(p));
                 }
             }
             button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
@@ -5575,13 +5565,8 @@
         }
 
         private CharSequence processLegacyText(CharSequence charSequence) {
-            return processLegacyText(charSequence, false /* ambient */);
-        }
-
-        private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
             boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
-            boolean wantLightText = ambient;
-            if (isAlreadyLightText != wantLightText) {
+            if (isAlreadyLightText) {
                 return getColorUtil().invertCharSequenceColors(charSequence);
             } else {
                 return charSequence;
@@ -5595,9 +5580,7 @@
                 StandardTemplateParams p) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
             int color;
-            if (p.ambient) {
-                color = resolveAmbientColor(p);
-            } else if (isColorized(p)) {
+            if (isColorized(p)) {
                 color = getPrimaryTextColor(p);
             } else {
                 color = resolveContrastColor(p);
@@ -5684,17 +5667,6 @@
             return mNeutralColor;
         }
 
-        int resolveAmbientColor(StandardTemplateParams p) {
-            int rawColor = getRawColor(p);
-            if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) {
-                return mCachedAmbientColor;
-            }
-            final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor);
-
-            mCachedAmbientColorIsFor = rawColor;
-            return mCachedAmbientColor = contrasted;
-        }
-
         /**
          * Apply the unstyled operations and return a new {@link Notification} object.
          * @hide
@@ -8407,6 +8379,186 @@
         }
     }
 
+    /**
+     * Encapsulates the information needed to display a notification as a bubble.
+     *
+     * <p>A bubble is used to display app content in a floating window over the existing
+     * foreground activity. A bubble has a collapsed state represented by an icon,
+     * {@link BubbleMetadata.Builder#setIcon(Icon)} and an expanded state which is populated
+     * via {@link BubbleMetadata.Builder#setIntent(PendingIntent)}.</p>
+     *
+     * <b>Notifications with a valid and allowed bubble will display in collapsed state
+     * outside of the notification shade on unlocked devices. When a user interacts with the
+     * collapsed bubble, the bubble intent will be invoked and displayed.</b>
+     *
+     * @see Notification.Builder#setBubbleMetadata(BubbleMetadata)
+     */
+    public static final class BubbleMetadata implements Parcelable {
+
+        private PendingIntent mPendingIntent;
+        private CharSequence mTitle;
+        private Icon mIcon;
+        private int mDesiredHeight;
+
+        private BubbleMetadata(PendingIntent intent, CharSequence title, Icon icon, int height) {
+            mPendingIntent = intent;
+            mTitle = title;
+            mIcon = icon;
+            mDesiredHeight = height;
+        }
+
+        private BubbleMetadata(Parcel in) {
+            mPendingIntent = PendingIntent.CREATOR.createFromParcel(in);
+            mTitle = in.readCharSequence();
+            mIcon = Icon.CREATOR.createFromParcel(in);
+            mDesiredHeight = in.readInt();
+        }
+
+        /**
+         * @return the pending intent used to populate the floating window for this bubble.
+         */
+        public PendingIntent getIntent() {
+            return mPendingIntent;
+        }
+
+        /**
+         * @return the title that will appear along with the app content defined by
+         * {@link #getIntent()} for this bubble.
+         */
+        public CharSequence getTitle() {
+            return mTitle;
+        }
+
+        /**
+         * @return the icon that will be displayed for this bubble when it is collapsed.
+         */
+        public Icon getIcon() {
+            return mIcon;
+        }
+
+        /**
+         * @return the ideal height for the floating window that app content defined by
+         * {@link #getIntent()} for this bubble.
+         */
+        public int getDesiredHeight() {
+            return mDesiredHeight;
+        }
+
+        public static final Parcelable.Creator<BubbleMetadata> CREATOR =
+                new Parcelable.Creator<BubbleMetadata>() {
+
+                    @Override
+                    public BubbleMetadata createFromParcel(Parcel source) {
+                        return new BubbleMetadata(source);
+                    }
+
+                    @Override
+                    public BubbleMetadata[] newArray(int size) {
+                        return new BubbleMetadata[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            mPendingIntent.writeToParcel(out, 0);
+            out.writeCharSequence(mTitle);
+            mIcon.writeToParcel(out, 0);
+            out.writeInt(mDesiredHeight);
+        }
+
+        /**
+         * Builder to construct a {@link BubbleMetadata} object.
+         */
+        public static class Builder {
+
+            private PendingIntent mPendingIntent;
+            private CharSequence mTitle;
+            private Icon mIcon;
+            private int mDesiredHeight;
+
+            /**
+             * Constructs a new builder object.
+             */
+            public Builder() {
+            }
+
+            /**
+             * Sets the intent that will be used when the bubble is expanded. This will display the
+             * app content in a floating window over the existing foreground activity.
+             */
+            public BubbleMetadata.Builder setIntent(PendingIntent intent) {
+                if (intent == null) {
+                    throw new IllegalArgumentException("Bubble requires non-null pending intent");
+                }
+                mPendingIntent = intent;
+                return this;
+            }
+
+            /**
+             * Sets the title that will appear along with the app content for this bubble.
+             *
+             * <p>A title is required and should expect to fit on a single line and make sense when
+             * shown with the content defined by {@link #setIntent(PendingIntent)}.</p>
+             */
+            public BubbleMetadata.Builder setTitle(CharSequence title) {
+                if (TextUtils.isEmpty(title)) {
+                    throw new IllegalArgumentException("Bubbles require non-null or empty title");
+                }
+                mTitle = title;
+                return this;
+            }
+
+            /**
+             * Sets the icon that will represent the bubble when it is collapsed.
+             *
+             * <p>An icon is required and should be representative of the content within the bubble.
+             * If your app produces multiple bubbles, the image should be unique for each of them.
+             * </p>
+             */
+            public BubbleMetadata.Builder setIcon(Icon icon) {
+                if (icon == null) {
+                    throw new IllegalArgumentException("Bubbles require non-null icon");
+                }
+                mIcon = icon;
+                return this;
+            }
+
+            /**
+             * Sets the desired height for the app content defined by
+             * {@link #setIntent(PendingIntent)}, this height may not be respected if there is not
+             * enough space on the screen or if the provided height is too small to be useful.
+             */
+            public BubbleMetadata.Builder setDesiredHeight(int height) {
+                mDesiredHeight = Math.max(height, 0);
+                return this;
+            }
+
+            /**
+             * Creates the {@link BubbleMetadata} defined by this builder.
+             * <p>Will throw {@link IllegalStateException} if required fields have not been set
+             * on this builder.</p>
+             */
+            public BubbleMetadata build() {
+                if (mPendingIntent == null) {
+                    throw new IllegalStateException("Must supply pending intent to bubble");
+                }
+                if (TextUtils.isEmpty(mTitle)) {
+                    throw new IllegalStateException("Must supply a title for the bubble");
+                }
+                if (mIcon == null) {
+                    throw new IllegalStateException("Must supply an icon for the bubble");
+                }
+                return new BubbleMetadata(mPendingIntent, mTitle, mIcon, mDesiredHeight);
+            }
+        }
+    }
+
+
     // When adding a new Style subclass here, don't forget to update
     // Builder.getNotificationStyleClass.
 
@@ -9950,7 +10102,6 @@
 
     private static class StandardTemplateParams {
         boolean hasProgress = true;
-        boolean ambient = false;
         CharSequence title;
         CharSequence text;
         CharSequence headerTextSecondary;
@@ -9963,7 +10114,6 @@
 
         final StandardTemplateParams reset() {
             hasProgress = true;
-            ambient = false;
             title = null;
             text = null;
             summaryText = null;
@@ -10019,22 +10169,15 @@
             return this;
         }
 
-        final StandardTemplateParams ambient(boolean ambient) {
-            Preconditions.checkState(title == null && text == null, "must set ambient before text");
-            this.ambient = ambient;
-            return this;
-        }
-
         final StandardTemplateParams fillTextsFrom(Builder b) {
             Bundle extras = b.mN.extras;
-            this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
+            this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
 
-            // Big text notifications should contain their content when viewed in ambient mode.
             CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT);
-            if (!ambient || TextUtils.isEmpty(text)) {
+            if (TextUtils.isEmpty(text)) {
                 text = extras.getCharSequence(EXTRA_TEXT);
             }
-            this.text = b.processLegacyText(text, ambient);
+            this.text = b.processLegacyText(text);
             this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
             return this;
         }
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 41ceaaf..e95d62f 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -19,6 +19,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.NotificationManager.Importance;
 import android.content.ContentResolver;
@@ -84,7 +85,7 @@
     private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
     private static final String ATT_GROUP = "group";
     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
-    private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
+    private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
     private static final String DELIMITER = ",";
 
     /**
@@ -120,7 +121,7 @@
     /**
      * @hide
      */
-    public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100;
+    public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100;
 
     /**
      * @hide
@@ -133,7 +134,7 @@
             USER_LOCKED_VIBRATION,
             USER_LOCKED_SOUND,
             USER_LOCKED_SHOW_BADGE,
-            USER_LOCKED_ALLOW_APP_OVERLAY
+            USER_LOCKED_ALLOW_BUBBLE
     };
 
     private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -143,7 +144,7 @@
             NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_DELETED = false;
     private static final boolean DEFAULT_SHOW_BADGE = true;
-    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+    private static final boolean DEFAULT_ALLOW_BUBBLE = true;
 
     @UnsupportedAppUsage
     private final String mId;
@@ -167,7 +168,8 @@
     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
     // If this is a blockable system notification channel.
     private boolean mBlockableSystem = false;
-    private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+    private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
+    private boolean mImportanceLockedByOEM;
 
     /**
      * Creates a notification channel.
@@ -229,7 +231,8 @@
         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
         mLightColor = in.readInt();
         mBlockableSystem = in.readBoolean();
-        mAllowAppOverlay = in.readBoolean();
+        mAllowBubbles = in.readBoolean();
+        mImportanceLockedByOEM = in.readBoolean();
     }
 
     @Override
@@ -282,7 +285,8 @@
         }
         dest.writeInt(mLightColor);
         dest.writeBoolean(mBlockableSystem);
-        dest.writeBoolean(mAllowAppOverlay);
+        dest.writeBoolean(mAllowBubbles);
+        dest.writeBoolean(mImportanceLockedByOEM);
     }
 
     /**
@@ -476,7 +480,7 @@
 
     /**
      * Sets whether notifications posted to this channel can appear outside of the notification
-     * shade, floating over other apps' content.
+     * shade, floating over other apps' content as a bubble.
      *
      * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is,
      * channels whose {@link #getImportance() importance} is <
@@ -484,10 +488,10 @@
      *
      * <p>Only modifiable before the channel is submitted to
      *      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p>
-     * @see Notification#getAppOverlayIntent()
+     * @see Notification#getBubbleMetadata()
      */
-    public void setAllowAppOverlay(boolean allowAppOverlay) {
-        mAllowAppOverlay = allowAppOverlay;
+    public void setAllowBubbles(boolean allowBubbles) {
+        mAllowBubbles = allowBubbles;
     }
 
     /**
@@ -606,16 +610,16 @@
      * Returns whether notifications posted to this channel can display outside of the notification
      * shade, in a floating window on top of other apps.
      */
-    public boolean canOverlayApps() {
-        return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH;
+    public boolean canBubble() {
+        return isBubbleAllowed() && getImportance() >= IMPORTANCE_HIGH;
     }
 
     /**
-     * Like {@link #canOverlayApps()}, but only checks the permission, not the importance.
+     * Like {@link #canBubble()}, but only checks the permission, not the importance.
      * @hide
      */
-    public boolean isAppOverlayAllowed() {
-        return mAllowAppOverlay;
+    public boolean isBubbleAllowed() {
+        return mAllowBubbles;
     }
 
     /**
@@ -649,6 +653,22 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public void setImportanceLockedByOEM(boolean locked) {
+        mImportanceLockedByOEM = locked;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public boolean isImportanceLockedByOEM() {
+        return mImportanceLockedByOEM;
+    }
+
+    /**
      * 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.
      * @see #getImportance()
@@ -699,7 +719,7 @@
         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
         setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
-        setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
+        setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
     }
 
     @Nullable
@@ -818,8 +838,8 @@
         if (isBlockableSystem()) {
             out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
         }
-        if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
-            out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
+        if (canBubble() != DEFAULT_ALLOW_BUBBLE) {
+            out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble()));
         }
 
         out.endTag(null, TAG_CHANNEL);
@@ -863,7 +883,7 @@
         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
         record.put(ATT_GROUP, getGroup());
         record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
-        record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
+        record.put(ATT_ALLOW_BUBBLE, canBubble());
         return record;
     }
 
@@ -952,25 +972,26 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         NotificationChannel that = (NotificationChannel) o;
-        return getImportance() == that.getImportance() &&
-                mBypassDnd == that.mBypassDnd &&
-                getLockscreenVisibility() == that.getLockscreenVisibility() &&
-                mLights == that.mLights &&
-                getLightColor() == that.getLightColor() &&
-                getUserLockedFields() == that.getUserLockedFields() &&
-                isFgServiceShown() == that.isFgServiceShown() &&
-                mVibrationEnabled == that.mVibrationEnabled &&
-                mShowBadge == that.mShowBadge &&
-                isDeleted() == that.isDeleted() &&
-                isBlockableSystem() == that.isBlockableSystem() &&
-                mAllowAppOverlay == that.mAllowAppOverlay &&
-                Objects.equals(getId(), that.getId()) &&
-                Objects.equals(getName(), that.getName()) &&
-                Objects.equals(mDesc, that.mDesc) &&
-                Objects.equals(getSound(), that.getSound()) &&
-                Arrays.equals(mVibration, that.mVibration) &&
-                Objects.equals(getGroup(), that.getGroup()) &&
-                Objects.equals(getAudioAttributes(), that.getAudioAttributes());
+        return getImportance() == that.getImportance()
+                && mBypassDnd == that.mBypassDnd
+                && getLockscreenVisibility() == that.getLockscreenVisibility()
+                && mLights == that.mLights
+                && getLightColor() == that.getLightColor()
+                && getUserLockedFields() == that.getUserLockedFields()
+                && isFgServiceShown() == that.isFgServiceShown()
+                && mVibrationEnabled == that.mVibrationEnabled
+                && mShowBadge == that.mShowBadge
+                && isDeleted() == that.isDeleted()
+                && isBlockableSystem() == that.isBlockableSystem()
+                && mAllowBubbles == that.mAllowBubbles
+                && Objects.equals(getId(), that.getId())
+                && Objects.equals(getName(), that.getName())
+                && Objects.equals(mDesc, that.mDesc)
+                && Objects.equals(getSound(), that.getSound())
+                && Arrays.equals(mVibration, that.mVibration)
+                && Objects.equals(getGroup(), that.getGroup())
+                && Objects.equals(getAudioAttributes(), that.getAudioAttributes())
+                && mImportanceLockedByOEM == that.mImportanceLockedByOEM;
     }
 
     @Override
@@ -979,7 +1000,8 @@
                 getLockscreenVisibility(), getSound(), mLights, getLightColor(),
                 getUserLockedFields(),
                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
-                getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay);
+                getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
+                mImportanceLockedByOEM);
         result = 31 * result + Arrays.hashCode(mVibration);
         return result;
     }
@@ -1006,7 +1028,8 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
-                + ", mAllowAppOverlay=" + mAllowAppOverlay
+                + ", mAllowBubbles=" + mAllowBubbles
+                + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + '}';
         pw.println(prefix + output);
     }
@@ -1032,7 +1055,8 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
-                + ", mAllowAppOverlay=" + mAllowAppOverlay
+                + ", mAllowBubbles=" + mAllowBubbles
+                + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + '}';
     }
 
@@ -1066,7 +1090,7 @@
             mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
         }
         proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
-        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
+        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles);
 
         proto.end(token);
     }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index aad3253..43614fe 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1080,14 +1080,14 @@
      * notification shade, floating over other apps' content.
      *
      * <p>This value will be ignored for notifications that are posted to channels that do not
-     * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+     * allow bubbles ({@link NotificationChannel#canBubble()}.
      *
-     * @see Notification#getAppOverlayIntent()
+     * @see Notification#getBubbleMetadata()
      */
-    public boolean areAppOverlaysAllowed() {
+    public boolean areBubblesAllowed() {
         INotificationManager service = getService();
         try {
-            return service.areAppOverlaysAllowed(mContext.getPackageName());
+            return service.areBubblesAllowed(mContext.getPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 9ddf4bd..2dc225a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -21,8 +21,11 @@
 import android.app.ContextImpl.ServiceInitializationState;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
+import android.app.contentsuggestions.ContentSuggestionsManager;
+import android.app.contentsuggestions.IContentSuggestionsManager;
 import android.app.job.IJobScheduler;
 import android.app.job.JobScheduler;
+import android.app.prediction.AppPredictionManager;
 import android.app.role.RoleManager;
 import android.app.slice.SliceManager;
 import android.app.timedetector.TimeDetector;
@@ -42,6 +45,8 @@
 import android.content.Context;
 import android.content.IRestrictionsManager;
 import android.content.RestrictionsManager;
+import android.content.om.IOverlayManager;
+import android.content.om.OverlayManager;
 import android.content.pm.CrossProfileApps;
 import android.content.pm.ICrossProfileApps;
 import android.content.pm.IShortcutService;
@@ -95,8 +100,10 @@
 import android.net.EthernetManager;
 import android.net.IConnectivityManager;
 import android.net.IEthernetManager;
+import android.net.IIpMemoryStore;
 import android.net.IIpSecService;
 import android.net.INetworkPolicyManager;
+import android.net.IpMemoryStore;
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
@@ -120,6 +127,7 @@
 import android.nfc.NfcManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
+import android.os.BugreportManager;
 import android.os.Build;
 import android.os.DeviceIdleManager;
 import android.os.DropBoxManager;
@@ -127,6 +135,7 @@
 import android.os.IBatteryPropertiesRegistrar;
 import android.os.IBinder;
 import android.os.IDeviceIdleController;
+import android.os.IDumpstate;
 import android.os.IHardwarePropertiesManager;
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
@@ -321,10 +330,21 @@
 
         registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
                 new StaticServiceFetcher<NetworkStack>() {
-                @Override
-                public NetworkStack createService() {
-                    return new NetworkStack();
-                }});
+                    @Override
+                    public NetworkStack createService() {
+                        return new NetworkStack();
+                    }});
+
+        registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class,
+                new CachedServiceFetcher<IpMemoryStore>() {
+                    @Override
+                    public IpMemoryStore createService(final ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(
+                                Context.IP_MEMORY_STORE_SERVICE);
+                        IIpMemoryStore service = IIpMemoryStore.Stub.asInterface(b);
+                        return new IpMemoryStore(ctx, service);
+                    }});
 
         registerService(Context.IPSEC_SERVICE, IpSecManager.class,
                 new CachedServiceFetcher<IpSecManager>() {
@@ -419,10 +439,11 @@
             }});
 
         registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class,
-                new StaticServiceFetcher<TextServicesManager>() {
+                new CachedServiceFetcher<TextServicesManager>() {
             @Override
-            public TextServicesManager createService() {
-                return TextServicesManager.getInstance();
+            public TextServicesManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                return TextServicesManager.createInstance(ctx);
             }});
 
         registerService(Context.KEYGUARD_SERVICE, KeyguardManager.class,
@@ -1035,6 +1056,14 @@
                 return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
             }});
 
+        registerService(Context.OVERLAY_SERVICE, OverlayManager.class,
+                new CachedServiceFetcher<OverlayManager>() {
+            @Override
+            public OverlayManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getServiceOrThrow(Context.OVERLAY_SERVICE);
+                return new OverlayManager(ctx, IOverlayManager.Stub.asInterface(b));
+            }});
+
         registerService(Context.NETWORK_WATCHLIST_SERVICE, NetworkWatchlistManager.class,
                 new CachedServiceFetcher<NetworkWatchlistManager>() {
                     @Override
@@ -1069,6 +1098,16 @@
                 return new IncidentManager(ctx);
             }});
 
+        registerService(Context.BUGREPORT_SERVICE, BugreportManager.class,
+                new CachedServiceFetcher<BugreportManager>() {
+                    @Override
+                    public BugreportManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(Context.BUGREPORT_SERVICE);
+                        return new BugreportManager(ctx.getOuterContext(),
+                                IDumpstate.Stub.asInterface(b));
+                    }});
+
         registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class,
                 new CachedServiceFetcher<AutofillManager>() {
             @Override
@@ -1095,6 +1134,29 @@
                 return null;
             }});
 
+        registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class,
+                new CachedServiceFetcher<AppPredictionManager>() {
+            @Override
+            public AppPredictionManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                return new AppPredictionManager(ctx);
+            }
+        });
+
+        registerService(Context.CONTENT_SUGGESTIONS_SERVICE,
+                ContentSuggestionsManager.class,
+                new CachedServiceFetcher<ContentSuggestionsManager>() {
+                    @Override
+                    public ContentSuggestionsManager createService(ContextImpl ctx) {
+                        // No throw as this is an optional service
+                        IBinder b = ServiceManager.getService(
+                                Context.CONTENT_SUGGESTIONS_SERVICE);
+                        IContentSuggestionsManager service =
+                                IContentSuggestionsManager.Stub.asInterface(b);
+                        return new ContentSuggestionsManager(service);
+                    }
+                });
+
         registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
             @Override
             public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException {
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index ace814a..38a98d3c8 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -56,6 +56,7 @@
      * eg. A launcher may set its text color to black if this flag is specified.
      * @hide
      */
+    @SystemApi
     public static final int HINT_SUPPORTS_DARK_TEXT = 1 << 0;
 
     /**
@@ -64,6 +65,7 @@
      * eg. A launcher may set its drawer color to black if this flag is specified.
      * @hide
      */
+    @SystemApi
     public static final int HINT_SUPPORTS_DARK_THEME = 1 << 1;
 
     /**
@@ -234,7 +236,7 @@
      * @see WallpaperColors#fromDrawable(Drawable)
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
             @Nullable Color tertiaryColor, int colorHints) {
 
@@ -349,7 +351,7 @@
      * @return True if dark text is supported.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public int getColorHints() {
         return mColorHints;
     }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 27ae0b0..a929fe0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -307,13 +307,14 @@
          * @param callback Listener
          * @param handler Thread to call it from. Main thread if null.
          * @param userId Owner of the wallpaper or UserHandle.USER_ALL
+         * @param displayId Caller comes from which display
          */
         public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
-                @Nullable Handler handler, int userId) {
+                @Nullable Handler handler, int userId, int displayId) {
             synchronized (this) {
                 if (!mColorCallbackRegistered) {
                     try {
-                        mService.registerWallpaperColorsCallback(this, userId);
+                        mService.registerWallpaperColorsCallback(this, userId, displayId);
                         mColorCallbackRegistered = true;
                     } catch (RemoteException e) {
                         // Failed, service is gone
@@ -329,16 +330,17 @@
          *
          * @param callback listener
          * @param userId Owner of the wallpaper or UserHandle.USER_ALL
+         * @param displayId Which display is interested
          */
         public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
-                int userId) {
+                int userId, int displayId) {
             synchronized (this) {
                 mColorListeners.removeIf(pair -> pair.first == callback);
 
                 if (mColorListeners.size() == 0 && mColorCallbackRegistered) {
                     mColorCallbackRegistered = false;
                     try {
-                        mService.unregisterWallpaperColorsCallback(this, userId);
+                        mService.unregisterWallpaperColorsCallback(this, userId, displayId);
                     } catch (RemoteException e) {
                         // Failed, service is gone
                         Log.w(TAG, "Can't unregister color updates", e);
@@ -370,14 +372,14 @@
             }
         }
 
-        WallpaperColors getWallpaperColors(int which, int userId) {
+        WallpaperColors getWallpaperColors(int which, int userId, int displayId) {
             if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
                 throw new IllegalArgumentException(
                         "Must request colors for exactly one kind of wallpaper");
             }
 
             try {
-                return mService.getWallpaperColors(which, userId);
+                return mService.getWallpaperColors(which, userId, displayId);
             } catch (RemoteException e) {
                 // Can't get colors, connection lost.
             }
@@ -894,7 +896,7 @@
     @UnsupportedAppUsage
     public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
             @NonNull Handler handler, int userId) {
-        sGlobals.addOnColorsChangedListener(listener, handler, userId);
+        sGlobals.addOnColorsChangedListener(listener, handler, userId, mContext.getDisplayId());
     }
 
     /**
@@ -913,7 +915,7 @@
      */
     public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
             int userId) {
-        sGlobals.removeOnColorsChangedListener(callback, userId);
+        sGlobals.removeOnColorsChangedListener(callback, userId, mContext.getDisplayId());
     }
 
     /**
@@ -947,7 +949,7 @@
      */
     @UnsupportedAppUsage
     public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
-        return sGlobals.getWallpaperColors(which, userId);
+        return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId());
     }
 
     /**
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 2990b57..e0a15a5 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityThread.isSystem;
 import static android.app.WindowConfigurationProto.ACTIVITY_TYPE;
 import static android.app.WindowConfigurationProto.APP_BOUNDS;
+import static android.app.WindowConfigurationProto.BOUNDS;
 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
 import static android.view.Surface.rotationToString;
 
@@ -585,6 +586,9 @@
         }
         protoOutputStream.write(WINDOWING_MODE, mWindowingMode);
         protoOutputStream.write(ACTIVITY_TYPE, mActivityType);
+        if (mBounds != null) {
+            mBounds.writeToProto(protoOutputStream, BOUNDS);
+        }
         protoOutputStream.end(token);
     }
 
@@ -606,6 +610,10 @@
                         mAppBounds = new Rect();
                         mAppBounds.readFromProto(proto, APP_BOUNDS);
                         break;
+                    case (int) BOUNDS:
+                        mBounds = new Rect();
+                        mBounds.readFromProto(proto, BOUNDS);
+                        break;
                     case (int) WINDOWING_MODE:
                         mWindowingMode = proto.readInt(WINDOWING_MODE);
                         break;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7da67d9..6d7c547 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2168,6 +2168,74 @@
     public @interface SetPrivateDnsModeResultConstants {}
 
     /**
+     * Activity action: Starts the administrator to get the mode for the provisioning.
+     * This intent may contain the following extras:
+     * <ul>
+     *     <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_IMEI}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_SERIAL_NUMBER}</li>
+     * </ul>
+     *
+     * <p>The target activity should return one of the following values in
+     * {@link #EXTRA_PROVISIONING_MODE} as result:
+     * <ul>
+     *     <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li>
+     *     <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li>
+     *     <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE}</li>
+     * </ul>
+     *
+     * <p>The target activity may also return the account that needs to be migrated from primary
+     * user to managed profile in case of a profile owner provisioning in
+     * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} as result.
+     */
+    public static final String ACTION_GET_PROVISIONING_MODE =
+            "android.app.action.GET_PROVISIONING_MODE";
+
+    /**
+     * A string extra holding the IMEI (International Mobile Equipment Identity) of the device.
+     */
+    public static final String EXTRA_PROVISIONING_IMEI = "android.app.extra.PROVISIONING_IMEI";
+
+    /**
+     * A string extra holding the serial number of the device.
+     */
+    public static final String EXTRA_PROVISIONING_SERIAL_NUMBER =
+            "android.app.extra.PROVISIONING_SERIAL_NUMBER";
+
+    /**
+     * An intent extra holding the provisioning mode returned by the administrator.
+     * The value for this extra should be one of the following:
+     * <ul>
+     *     <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li>
+     *     <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li>
+     *     <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE}</li>
+     * </ul>
+     */
+    public static final String EXTRA_PROVISIONING_MODE =
+            "android.app.extra.PROVISIONING_MODE";
+
+    /**
+     * The provisioning mode for fully managed device.
+     */
+    public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1;
+
+    /**
+     * The provisioning mode for managed profile.
+     */
+    public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2;
+
+    /**
+     * The provisioning mode for managed profile on a fully managed device.
+     */
+    public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 3;
+
+    /**
+     * Activity action: Starts the administrator to show policy compliance for the provisioning.
+     */
+    public static final String ACTION_ADMIN_POLICY_COMPLIANCE =
+            "android.app.action.ADMIN_POLICY_COMPLIANCE";
+
+    /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
      * @param admin The administrator component to check for.
@@ -6815,8 +6883,12 @@
     }
 
     /**
-     * Returns the list of input methods permitted by the device or profiles
-     * owners of the current user.  (*Not* calling user, due to a limitation in InputMethodManager.)
+     * Returns the list of input methods permitted by the device or profiles owners.
+     *
+     * <p>On {@link android.os.Build.VERSION_CODES#Q} and later devices, this method returns the
+     * result for the calling user.</p>
+     *
+     * <p>On Android P and prior devices, this method returns the result for the current user.</p>
      *
      * <p>Null means all input methods are allowed, if a non-null list is returned
      * it will contain the intersection of the permitted lists for any device or profile
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index cc4d4b1a..7d03f00 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1699,7 +1699,8 @@
 
         @Override
         public void setVisibility(int visibility) {
-            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
+            mNode.mFlags = (mNode.mFlags & ~ViewNode.FLAGS_VISIBILITY_MASK)
+                    | (visibility & ViewNode.FLAGS_VISIBILITY_MASK);
         }
 
         @Override
diff --git a/core/java/android/app/contentsuggestions/ClassificationsRequest.aidl b/core/java/android/app/contentsuggestions/ClassificationsRequest.aidl
new file mode 100644
index 0000000..a66413b
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ClassificationsRequest.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.contentsuggestions;
+
+parcelable ClassificationsRequest;
diff --git a/core/java/android/app/contentsuggestions/ClassificationsRequest.java b/core/java/android/app/contentsuggestions/ClassificationsRequest.java
new file mode 100644
index 0000000..3f71518
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ClassificationsRequest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.app.contentsuggestions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class ClassificationsRequest implements Parcelable {
+    @NonNull
+    private final List<ContentSelection> mSelections;
+    @Nullable
+    private final Bundle mExtras;
+
+    private ClassificationsRequest(@NonNull List<ContentSelection> selections,
+            @Nullable Bundle extras) {
+        mSelections = selections;
+        mExtras = extras;
+    }
+
+    /**
+     * Return request selections.
+     */
+    public List<ContentSelection> getSelections() {
+        return mSelections;
+    }
+
+    /**
+     * Return the request extras.
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedList(mSelections);
+        dest.writeBundle(mExtras);
+    }
+
+    public static final Creator<ClassificationsRequest> CREATOR =
+            new Creator<ClassificationsRequest>() {
+        @Override
+        public ClassificationsRequest createFromParcel(Parcel source) {
+            return new ClassificationsRequest(
+                    source.createTypedArrayList(ContentSelection.CREATOR),
+                    source.readBundle());
+        }
+
+        @Override
+        public ClassificationsRequest[] newArray(int size) {
+            return new ClassificationsRequest[size];
+        }
+    };
+
+    /**
+     * A builder for classifications request events.
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+
+        private final List<ContentSelection> mSelections;
+        private Bundle mExtras;
+
+        public Builder(@NonNull List<ContentSelection> selections) {
+            mSelections = selections;
+        }
+
+        /**
+         * Sets the request extras.
+         */
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds a new request instance.
+         */
+        public ClassificationsRequest build() {
+            return new ClassificationsRequest(mSelections, mExtras);
+        }
+    }
+}
diff --git a/core/java/android/app/contentsuggestions/ContentClassification.aidl b/core/java/android/app/contentsuggestions/ContentClassification.aidl
new file mode 100644
index 0000000..f67b2c8
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ContentClassification.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.contentsuggestions;
+
+parcelable ContentClassification;
diff --git a/core/java/android/app/contentsuggestions/ContentClassification.java b/core/java/android/app/contentsuggestions/ContentClassification.java
new file mode 100644
index 0000000..f18520f
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ContentClassification.java
@@ -0,0 +1,79 @@
+/*
+ * 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.app.contentsuggestions;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class ContentClassification implements Parcelable {
+    @NonNull
+    private final String mClassificationId;
+    @NonNull
+    private final Bundle mExtras;
+
+    public ContentClassification(@NonNull String classificationId, @NonNull Bundle extras) {
+        mClassificationId = classificationId;
+        mExtras = extras;
+    }
+
+    /**
+     * Return the classification id.
+     */
+    public String getId() {
+        return mClassificationId;
+    }
+
+    /**
+     * Return the classification extras.
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mClassificationId);
+        dest.writeBundle(mExtras);
+    }
+
+    public static final Creator<ContentClassification> CREATOR =
+            new Creator<ContentClassification>() {
+        @Override
+        public ContentClassification createFromParcel(Parcel source) {
+            return new ContentClassification(
+                    source.readString(),
+                    source.readBundle());
+        }
+
+        @Override
+        public ContentClassification[] newArray(int size) {
+            return new ContentClassification[size];
+        }
+    };
+}
diff --git a/core/java/android/app/contentsuggestions/ContentSelection.aidl b/core/java/android/app/contentsuggestions/ContentSelection.aidl
new file mode 100644
index 0000000..e626d69
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ContentSelection.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.contentsuggestions;
+
+parcelable ContentSelection;
diff --git a/core/java/android/app/contentsuggestions/ContentSelection.java b/core/java/android/app/contentsuggestions/ContentSelection.java
new file mode 100644
index 0000000..a8917f7
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ContentSelection.java
@@ -0,0 +1,79 @@
+/*
+ * 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.app.contentsuggestions;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class ContentSelection implements Parcelable {
+    @NonNull
+    private final String mSelectionId;
+    @NonNull
+    private final Bundle mExtras;
+
+    public ContentSelection(@NonNull String selectionId, @NonNull Bundle extras) {
+        mSelectionId = selectionId;
+        mExtras = extras;
+    }
+
+    /**
+     * Return the selection id.
+     */
+    public String getId() {
+        return mSelectionId;
+    }
+
+    /**
+     * Return the selection extras.
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mSelectionId);
+        dest.writeBundle(mExtras);
+    }
+
+    public static final Creator<ContentSelection> CREATOR =
+            new Creator<ContentSelection>() {
+        @Override
+        public ContentSelection createFromParcel(Parcel source) {
+            return new ContentSelection(
+                    source.readString(),
+                    source.readBundle());
+        }
+
+        @Override
+        public ContentSelection[] newArray(int size) {
+            return new ContentSelection[size];
+        }
+    };
+}
diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
new file mode 100644
index 0000000..b4d8977
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
@@ -0,0 +1,230 @@
+/*
+ * 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.app.contentsuggestions;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * When provided with content from an app, can suggest selections and classifications of that
+ * content.
+ *
+ * <p>The content is mainly a snapshot of a running task, the selections will be text and image
+ * selections with that image content. These mSelections can then be classified to find actions and
+ * entities on those selections.
+ *
+ * <p>Only accessible to blessed components such as Overview.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ContentSuggestionsManager {
+    private static final String TAG = ContentSuggestionsManager.class.getSimpleName();
+
+    @Nullable
+    private final IContentSuggestionsManager mService;
+
+    /** @hide */
+    public ContentSuggestionsManager(@Nullable IContentSuggestionsManager service) {
+        mService = service;
+    }
+
+    /**
+     * Hints to the system that a new context image for the provided task should be sent to the
+     * system content suggestions service.
+     *
+     * @param taskId of the task to snapshot.
+     * @param imageContextRequestExtras sent with with request to provide implementation specific
+     *                                  extra information.
+     */
+    public void provideContextImage(int taskId, @NonNull Bundle imageContextRequestExtras) {
+        if (mService == null) {
+            Log.e(TAG, "provideContextImage called, but no ContentSuggestionsManager configured");
+            return;
+        }
+
+        try {
+            mService.provideContextImage(taskId, imageContextRequestExtras);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Suggest content selections, based on the provided task id and optional
+     * location on screen provided in the request. Called after provideContextImage().
+     * The result can be passed to
+     * {@link #classifyContentSelections(ClassificationsRequest, Executor, ClassificationsCallback)}
+     *  to classify actions and entities on these selections.
+     *
+     * @param request containing the task and point location.
+     * @param callbackExecutor to execute the provided callback on.
+     * @param callback to receive the selections.
+     */
+    public void suggestContentSelections(
+            @NonNull SelectionsRequest request,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull SelectionsCallback callback) {
+        if (mService == null) {
+            Log.e(TAG,
+                    "suggestContentSelections called, but no ContentSuggestionsManager configured");
+            return;
+        }
+
+        try {
+            mService.suggestContentSelections(
+                    request, new SelectionsCallbackWrapper(callback, callbackExecutor));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Classify actions and entities in content selections, as returned from
+     * suggestContentSelections. Note these selections may be modified by the
+     * caller before being passed here.
+     *
+     * @param request containing the selections to classify.
+     * @param callbackExecutor to execute the provided callback on.
+     * @param callback to receive the classifications.
+     */
+    public void classifyContentSelections(
+            @NonNull ClassificationsRequest request,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull ClassificationsCallback callback) {
+        if (mService == null) {
+            Log.e(TAG, "classifyContentSelections called, "
+                    + "but no ContentSuggestionsManager configured");
+            return;
+        }
+
+        try {
+            mService.classifyContentSelections(
+                    request, new ClassificationsCallbackWrapper(callback, callbackExecutor));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report telemetry for interaction with suggestions / classifications.
+     *
+     * @param requestId the id for the associated interaction
+     * @param interaction to report back to the system content suggestions service.
+     */
+    public void notifyInteraction(@NonNull String requestId, @NonNull Bundle interaction) {
+        if (mService == null) {
+            Log.e(TAG, "notifyInteraction called, but no ContentSuggestionsManager configured");
+            return;
+        }
+
+        try {
+            mService.notifyInteraction(requestId, interaction);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Callback to receive content selections from
+     *  {@link #suggestContentSelections(SelectionsRequest, Executor, SelectionsCallback)}.
+     */
+    public interface SelectionsCallback {
+        /**
+         * Async callback called when the content suggestions service has selections available.
+         * These can be modified and sent back to the manager for classification. The contents of
+         * the selection is implementation dependent.
+         *
+         * @param statusCode as defined by the implementation of content suggestions service.
+         * @param selections not {@code null}, but can be size {@code 0}.
+         */
+        void onContentSelectionsAvailable(
+                int statusCode, @NonNull List<ContentSelection> selections);
+    }
+
+    /**
+     * Callback to receive classifications from
+     * {@link #classifyContentSelections(ClassificationsRequest, Executor, ClassificationsCallback)}
+     */
+    public interface ClassificationsCallback {
+        /**
+         * Async callback called when the content suggestions service has classified selections. The
+         * contents of the classification is implementation dependent.
+         *
+         * @param statusCode as defined by the implementation of content suggestions service.
+         * @param classifications not {@code null}, but can be size {@code 0}.
+         */
+        void onContentClassificationsAvailable(int statusCode,
+                @NonNull List<ContentClassification> classifications);
+    }
+
+    private static class SelectionsCallbackWrapper extends ISelectionsCallback.Stub {
+        private final SelectionsCallback mCallback;
+        private final Executor mExecutor;
+
+        SelectionsCallbackWrapper(
+                @NonNull SelectionsCallback callback, @NonNull Executor executor) {
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onContentSelectionsAvailable(
+                int statusCode, List<ContentSelection> selections) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mCallback.onContentSelectionsAvailable(statusCode, selections));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    private static final class ClassificationsCallbackWrapper extends
+            IClassificationsCallback.Stub {
+        private final ClassificationsCallback mCallback;
+        private final Executor mExecutor;
+
+        ClassificationsCallbackWrapper(@NonNull ClassificationsCallback callback,
+                @NonNull Executor executor) {
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onContentClassificationsAvailable(
+                int statusCode, List<ContentClassification> classifications) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mCallback.onContentClassificationsAvailable(statusCode, classifications));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/contentsuggestions/IClassificationsCallback.aidl b/core/java/android/app/contentsuggestions/IClassificationsCallback.aidl
new file mode 100644
index 0000000..69f5d55
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/IClassificationsCallback.aidl
@@ -0,0 +1,25 @@
+/**
+ * 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.app.contentsuggestions;
+
+import android.app.contentsuggestions.ContentClassification;
+
+/** @hide */
+oneway interface IClassificationsCallback {
+    void onContentClassificationsAvailable(
+            int statusCode, in List<ContentClassification> classifications);
+}
diff --git a/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl
new file mode 100644
index 0000000..24f5ad8
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl
@@ -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.app.contentsuggestions;
+
+import android.app.contentsuggestions.IClassificationsCallback;
+import android.app.contentsuggestions.ISelectionsCallback;
+import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.SelectionsRequest;
+import android.os.Bundle;
+
+/** @hide */
+oneway interface IContentSuggestionsManager {
+    void provideContextImage(
+            int taskId,
+            in Bundle imageContextRequestExtras);
+    void suggestContentSelections(
+            in SelectionsRequest request,
+            in ISelectionsCallback callback);
+    void classifyContentSelections(
+            in ClassificationsRequest request,
+            in IClassificationsCallback callback);
+    void notifyInteraction(in String requestId, in Bundle interaction);
+}
diff --git a/core/java/android/app/contentsuggestions/ISelectionsCallback.aidl b/core/java/android/app/contentsuggestions/ISelectionsCallback.aidl
new file mode 100644
index 0000000..1952583
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/ISelectionsCallback.aidl
@@ -0,0 +1,25 @@
+/**
+ * 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.app.contentsuggestions;
+
+import android.app.contentsuggestions.ContentSelection;
+import android.os.Bundle;
+
+/** @hide */
+oneway interface ISelectionsCallback {
+    void onContentSelectionsAvailable(int statusCode, in List<ContentSelection> selections);
+}
diff --git a/core/java/android/app/contentsuggestions/SelectionsRequest.aidl b/core/java/android/app/contentsuggestions/SelectionsRequest.aidl
new file mode 100644
index 0000000..f5ce7c3
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/SelectionsRequest.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.contentsuggestions;
+
+parcelable SelectionsRequest;
diff --git a/core/java/android/app/contentsuggestions/SelectionsRequest.java b/core/java/android/app/contentsuggestions/SelectionsRequest.java
new file mode 100644
index 0000000..16f3e6b
--- /dev/null
+++ b/core/java/android/app/contentsuggestions/SelectionsRequest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.app.contentsuggestions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class SelectionsRequest implements Parcelable {
+    private final int mTaskId;
+    @Nullable
+    private final Point mInterestPoint;
+    @Nullable
+    private final Bundle mExtras;
+
+    private SelectionsRequest(int taskId, @Nullable Point interestPoint, @Nullable Bundle extras) {
+        mTaskId = taskId;
+        mInterestPoint = interestPoint;
+        mExtras = extras;
+    }
+
+    /**
+     * Return the request task id.
+     */
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * Return the request point of interest.
+     */
+    public Point getInterestPoint() {
+        return mInterestPoint;
+    }
+
+    /**
+     * Return the request extras.
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mTaskId);
+        dest.writeTypedObject(mInterestPoint, flags);
+        dest.writeBundle(mExtras);
+    }
+
+    public static final Creator<SelectionsRequest> CREATOR =
+            new Creator<SelectionsRequest>() {
+        @Override
+        public SelectionsRequest createFromParcel(Parcel source) {
+            return new SelectionsRequest(
+                    source.readInt(), source.readTypedObject(Point.CREATOR), source.readBundle());
+        }
+
+        @Override
+        public SelectionsRequest[] newArray(int size) {
+            return new SelectionsRequest[size];
+        }
+    };
+
+    /**
+     * A builder for selections requests events.
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+
+        private final int mTaskId;
+        private Point mInterestPoint;
+        private Bundle mExtras;
+
+        public Builder(int taskId) {
+            mTaskId = taskId;
+        }
+
+        /**
+         * Sets the request extras.
+         */
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Sets the request interest point.
+         */
+        public Builder setInterestPoint(@NonNull Point interestPoint) {
+            mInterestPoint = interestPoint;
+            return this;
+        }
+
+        /**
+         * Builds a new request instance.
+         */
+        public SelectionsRequest build() {
+            return new SelectionsRequest(mTaskId, mInterestPoint, mExtras);
+        }
+    }
+}
diff --git a/core/java/android/app/prediction/AppPredictionContext.aidl b/core/java/android/app/prediction/AppPredictionContext.aidl
new file mode 100644
index 0000000..5767bf4
--- /dev/null
+++ b/core/java/android/app/prediction/AppPredictionContext.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.prediction;
+
+parcelable AppPredictionContext;
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
new file mode 100644
index 0000000..87ccb66
--- /dev/null
+++ b/core/java/android/app/prediction/AppPredictionContext.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 android.app.prediction;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * TODO(b/111701043): Add java docs
+ *
+ * @hide
+ */
+@SystemApi
+public final class AppPredictionContext implements Parcelable {
+
+    private final int mPredictedTargetCount;
+    @NonNull
+    private final String mUiSurface;
+    @NonNull
+    private final String mPackageName;
+    @Nullable
+    private final Bundle mExtras;
+
+    private AppPredictionContext(@NonNull String uiSurface, int numPredictedTargets,
+            @NonNull String packageName, @Nullable Bundle extras) {
+        mUiSurface = uiSurface;
+        mPredictedTargetCount = numPredictedTargets;
+        mPackageName = packageName;
+        mExtras = extras;
+    }
+
+    private AppPredictionContext(Parcel parcel) {
+        mUiSurface = parcel.readString();
+        mPredictedTargetCount = parcel.readInt();
+        mPackageName = parcel.readString();
+        mExtras = parcel.readBundle();
+    }
+
+    public String getUiSurface() {
+        return mUiSurface;
+    }
+
+    public int getPredictedTargetCount() {
+        return mPredictedTargetCount;
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mUiSurface);
+        dest.writeInt(mPredictedTargetCount);
+        dest.writeString(mPackageName);
+        dest.writeBundle(mExtras);
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    public static final Parcelable.Creator<AppPredictionContext> CREATOR =
+            new Parcelable.Creator<AppPredictionContext>() {
+                public AppPredictionContext createFromParcel(Parcel parcel) {
+                    return new AppPredictionContext(parcel);
+                }
+
+                public AppPredictionContext[] newArray(int size) {
+                    return new AppPredictionContext[size];
+                }
+            };
+
+    /**
+     * A builder for app prediction contexts.
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+
+        @NonNull
+        private final String mPackageName;
+
+        private int mPredictedTargetCount;
+        @Nullable
+        private String mUiSurface;
+        @Nullable
+        private Bundle mExtras;
+
+        /**
+         * @hide
+         */
+        public Builder(@NonNull Context context) {
+            mPackageName = context.getPackageName();
+        }
+
+
+        /**
+         * Sets the number of prediction targets as a hint.
+         */
+        public Builder setPredictedTargetCount(int predictedTargetCount) {
+            mPredictedTargetCount = predictedTargetCount;
+            return this;
+        }
+
+        /**
+         * Sets the UI surface.
+         */
+        public Builder setUiSurface(@Nullable String uiSurface) {
+            mUiSurface = uiSurface;
+            return this;
+        }
+
+        /**
+         * Sets the extras.
+         */
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds a new context instance.
+         */
+        public AppPredictionContext build() {
+            return new AppPredictionContext(mUiSurface, mPredictedTargetCount, mPackageName,
+                    mExtras);
+        }
+    }
+}
diff --git a/core/java/android/app/prediction/AppPredictionManager.java b/core/java/android/app/prediction/AppPredictionManager.java
new file mode 100644
index 0000000..f8578d4
--- /dev/null
+++ b/core/java/android/app/prediction/AppPredictionManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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.app.prediction;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * TODO (b/111701043) : Add java doc
+ * @hide
+ */
+@SystemApi
+public final class AppPredictionManager {
+
+    private final Context mContext;
+
+    /**
+     * @hide
+     */
+    public AppPredictionManager(Context context) {
+        mContext = Preconditions.checkNotNull(context);
+    }
+
+    /**
+     * Creates a new app prediction session.
+     */
+    public AppPredictor createAppPredictionSession(
+            @NonNull AppPredictionContext predictionContext) {
+        return new AppPredictor(mContext, predictionContext);
+    }
+}
diff --git a/core/java/android/app/prediction/AppPredictionSessionId.aidl b/core/java/android/app/prediction/AppPredictionSessionId.aidl
new file mode 100644
index 0000000..e829526
--- /dev/null
+++ b/core/java/android/app/prediction/AppPredictionSessionId.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.prediction;
+
+parcelable AppPredictionSessionId;
diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java
new file mode 100644
index 0000000..1d7308e
--- /dev/null
+++ b/core/java/android/app/prediction/AppPredictionSessionId.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.prediction;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * TODO (b/111701043) : Add java doc
+ *
+ * @hide
+ */
+@SystemApi
+public final class AppPredictionSessionId implements Parcelable {
+
+    private final String mId;
+
+    /**
+     * @hide
+     */
+    public AppPredictionSessionId(@NonNull String id) {
+        mId = id;
+    }
+
+    private AppPredictionSessionId(Parcel p) {
+        mId = p.readString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+        AppPredictionSessionId other = (AppPredictionSessionId) o;
+        return mId.equals(other.mId);
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return mId;
+    }
+
+    @Override
+    public int hashCode() {
+        // Ensure that the id has a consistent hash
+        return mId.hashCode();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    public static final Parcelable.Creator<AppPredictionSessionId> CREATOR =
+            new Parcelable.Creator<AppPredictionSessionId>() {
+                public AppPredictionSessionId createFromParcel(Parcel parcel) {
+                    return new AppPredictionSessionId(parcel);
+                }
+
+                public AppPredictionSessionId[] newArray(int size) {
+                    return new AppPredictionSessionId[size];
+                }
+            };
+}
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
new file mode 100644
index 0000000..2ddbd08
--- /dev/null
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -0,0 +1,257 @@
+/*
+ * 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.app.prediction;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.prediction.IPredictionCallback.Stub;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import dalvik.system.CloseGuard;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * TODO (b/111701043) : Add java doc
+ *
+ * <p>
+ * Usage: <pre> {@code
+ *
+ * class MyActivity {
+ *    private AppPredictor mClient
+ *
+ *    void onCreate() {
+ *         mClient = new AppPredictor(...)
+ *    }
+ *
+ *    void onStart() {
+ *        mClient.requestPredictionUpdate();
+ *    }
+ *
+ *    void onDestroy() {
+ *        mClient.close();
+ *    }
+ *
+ * }</pre>
+ *
+ * @hide
+ */
+@SystemApi
+public final class AppPredictor {
+
+    private static final String TAG = AppPredictor.class.getSimpleName();
+
+
+    private final IPredictionManager mPredictionManager;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+    private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
+
+    private final AppPredictionSessionId mSessionId;
+    private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
+
+    /**
+     * Creates a new Prediction client.
+     * <p>
+     * The caller should call {@link AppPredictor#destroy()} to dispose the client once it
+     * no longer used.
+     *
+     * @param predictionContext The prediction context
+     */
+    AppPredictor(@NonNull Context context, @NonNull AppPredictionContext predictionContext) {
+        IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE);
+        mPredictionManager = IPredictionManager.Stub.asInterface(b);
+        mSessionId = new AppPredictionSessionId(
+                context.getPackageName() + ":" + UUID.randomUUID().toString());
+        try {
+            mPredictionManager.createPredictionSession(predictionContext, mSessionId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to create predictor", e);
+            return;
+        }
+
+        mCloseGuard.open("close");
+    }
+
+    /**
+     * Notifies the prediction service of an app target event.
+     */
+    public void notifyAppTargetEvent(@NonNull AppTargetEvent event) {
+        try {
+            mPredictionManager.notifyAppTargetEvent(mSessionId, event);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to notify app target event", e);
+        }
+    }
+
+    /**
+     * Notifies the prediction service when the targets in a launch location are shown to the user.
+     */
+    public void notifyLocationShown(@NonNull String launchLocation,
+            @NonNull List<AppTargetId> targetIds) {
+        try {
+            mPredictionManager.notifyLocationShown(mSessionId, launchLocation,
+                    new ParceledListSlice<>(targetIds));
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to notify location shown event", e);
+        }
+    }
+
+    /**
+     * Requests the prediction service provide continuous updates of App predictions via the
+     * provided callback, until the given callback is unregistered.
+     *
+     * @see Callback#onTargetsAvailable(List)
+     */
+    public void registerPredictionUpdates(@NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull AppPredictor.Callback callback) {
+        if (mRegisteredCallbacks.containsKey(callback)) {
+            // Skip if this callback is already registered
+            return;
+        }
+        try {
+            final CallbackWrapper callbackWrapper = new CallbackWrapper(callbackExecutor,
+                    callback::onTargetsAvailable);
+            mPredictionManager.registerPredictionUpdates(mSessionId, callbackWrapper);
+            mRegisteredCallbacks.put(callback, callbackWrapper);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register for prediction updates", e);
+        }
+    }
+
+    /**
+     * Requests the prediction service to stop providing continuous updates to the provided
+     * callback until the callback is re-registered.
+     */
+    public void unregisterPredictionUpdates(@NonNull AppPredictor.Callback callback) {
+        if (!mRegisteredCallbacks.containsKey(callback)) {
+            // Skip if this callback was never registered
+            return;
+        }
+        try {
+            final CallbackWrapper callbackWrapper = mRegisteredCallbacks.remove(callback);
+            mPredictionManager.unregisterPredictionUpdates(mSessionId, callbackWrapper);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to unregister for prediction updates", e);
+        }
+    }
+
+    /**
+     * Requests the prediction service to dispatch a new set of App predictions via the provided
+     * callback.
+     *
+     * @see Callback#onTargetsAvailable(List)
+     */
+    public void requestPredictionUpdate() {
+        try {
+            mPredictionManager.requestPredictionUpdate(mSessionId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to request prediction update", e);
+        }
+    }
+
+    /**
+     * Returns a new list of AppTargets sorted based on prediction rank or {@code null} if the
+     * ranker is not available.
+     */
+    @Nullable
+    public void sortTargets(@NonNull List<AppTarget> targets,
+            @NonNull Executor callbackExecutor, @NonNull Consumer<List<AppTarget>> callback) {
+        try {
+            mPredictionManager.sortAppTargets(mSessionId, new ParceledListSlice(targets),
+                    new CallbackWrapper(callbackExecutor, callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to sort targets", e);
+        }
+    }
+
+    /**
+     * Destroys the client and unregisters the callback. Any method on this class after this call
+     * with throw {@link IllegalStateException}.
+     *
+     * TODO(b/111701043): Add state check in other methods.
+     */
+    public void destroy() {
+        if (!mIsClosed.getAndSet(true)) {
+            mCloseGuard.close();
+
+            // Do destroy;
+            try {
+                mPredictionManager.onDestroyPredictionSession(mSessionId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify app target event", e);
+            }
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            destroy();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Callback for receiving prediction updates.
+     */
+    public interface Callback {
+
+        /**
+         * Called when a new set of predicted app targets are available.
+         * @param targets Sorted list of predicted targets
+         */
+        void onTargetsAvailable(@NonNull List<AppTarget> targets);
+    }
+
+    static class CallbackWrapper extends Stub {
+
+        private final Consumer<List<AppTarget>> mCallback;
+        private final Executor mExecutor;
+
+        CallbackWrapper(@NonNull Executor callbackExecutor,
+                @NonNull Consumer<List<AppTarget>> callback) {
+            mCallback = callback;
+            mExecutor = callbackExecutor;
+        }
+
+        @Override
+        public void onResult(ParceledListSlice result) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.accept(result.getList()));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/prediction/AppTarget.aidl b/core/java/android/app/prediction/AppTarget.aidl
new file mode 100644
index 0000000..e4e2bc2
--- /dev/null
+++ b/core/java/android/app/prediction/AppTarget.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.prediction;
+
+parcelable AppTarget;
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
new file mode 100644
index 0000000..99c1c44
--- /dev/null
+++ b/core/java/android/app/prediction/AppTarget.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.app.prediction;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.pm.ShortcutInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A representation of a launchable target.
+ * @hide
+ */
+@SystemApi
+public final class AppTarget implements Parcelable {
+
+    private final AppTargetId mId;
+    private final String mPackageName;
+    private final String mClassName;
+    private final UserHandle mUser;
+
+    private final ShortcutInfo mShortcutInfo;
+
+    private int mRank;
+
+    /**
+     * @hide
+     */
+    public AppTarget(@NonNull AppTargetId id, @NonNull String packageName,
+            @Nullable String className, @NonNull UserHandle user) {
+        mId = id;
+        mShortcutInfo = null;
+
+        mPackageName = Preconditions.checkNotNull(packageName);
+        mClassName = className;
+        mUser = Preconditions.checkNotNull(user);
+    }
+
+    /**
+     * @hide
+     */
+    public AppTarget(@NonNull AppTargetId id, @NonNull ShortcutInfo shortcutInfo) {
+        mId = id;
+        mShortcutInfo = Preconditions.checkNotNull(shortcutInfo);
+
+        mPackageName = mShortcutInfo.getPackage();
+        mUser = mShortcutInfo.getUserHandle();
+        mClassName = null;
+    }
+
+    private AppTarget(Parcel parcel) {
+        mId = parcel.readTypedObject(AppTargetId.CREATOR);
+        mShortcutInfo = parcel.readTypedObject(ShortcutInfo.CREATOR);
+        if (mShortcutInfo == null) {
+            mPackageName = parcel.readString();
+            mClassName = parcel.readString();
+            mUser = UserHandle.of(parcel.readInt());
+        } else {
+            mPackageName = mShortcutInfo.getPackage();
+            mUser = mShortcutInfo.getUserHandle();
+            mClassName = null;
+        }
+        mRank = parcel.readInt();
+    }
+
+    /**
+     * Returns the target id.
+     */
+    @NonNull
+    public AppTargetId getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the class name for the app target.
+     */
+    @Nullable
+    public String getClassName() {
+        return mClassName;
+    }
+
+    /**
+     * Returns the package name for the app target.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the user for the app target.
+     */
+    @NonNull
+    public UserHandle getUser() {
+        return mUser;
+    }
+
+    /**
+     * Returns the shortcut info for the target.
+     */
+    @Nullable
+    public ShortcutInfo getShortcutInfo() {
+        return mShortcutInfo;
+    }
+
+    /**
+     * Sets the rank of the for the target.
+     * @hide
+     */
+    public void setRank(int rank) {
+        mRank = rank;
+    }
+
+    public int getRank() {
+        return mRank;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedObject(mId, flags);
+        dest.writeTypedObject(mShortcutInfo, flags);
+        if (mShortcutInfo == null) {
+            dest.writeString(mPackageName);
+            dest.writeString(mClassName);
+            dest.writeInt(mUser.getIdentifier());
+        }
+        dest.writeInt(mRank);
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    public static final Parcelable.Creator<AppTarget> CREATOR =
+            new Parcelable.Creator<AppTarget>() {
+                public AppTarget createFromParcel(Parcel parcel) {
+                    return new AppTarget(parcel);
+                }
+
+                public AppTarget[] newArray(int size) {
+                    return new AppTarget[size];
+                }
+            };
+}
diff --git a/core/java/android/app/prediction/AppTargetEvent.aidl b/core/java/android/app/prediction/AppTargetEvent.aidl
new file mode 100644
index 0000000..ebed2da
--- /dev/null
+++ b/core/java/android/app/prediction/AppTargetEvent.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.prediction;
+
+parcelable AppTargetEvent;
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
new file mode 100644
index 0000000..18317e1
--- /dev/null
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -0,0 +1,154 @@
+/*
+ * 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.app.prediction;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A representation of an app target event.
+ * @hide
+ */
+@SystemApi
+public final class AppTargetEvent implements Parcelable {
+
+    /**
+     * @hide
+     */
+    @IntDef({ACTION_LAUNCH, ACTION_DISMISS, ACTION_PIN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ActionType {}
+
+    /**
+     * Event type constant indicating an app target has been launched.
+     */
+    public static final int ACTION_LAUNCH = 1;
+
+    /**
+     * Event type constant indicating an app target has been dismissed.
+     */
+    public static final int ACTION_DISMISS = 2;
+
+    /**
+     * Event type constant indicating an app target has been pinned.
+     */
+    public static final int ACTION_PIN = 3;
+
+    private final AppTarget mTarget;
+    private final String mLocation;
+    private final int mAction;
+
+    private AppTargetEvent(@Nullable AppTarget target, @Nullable String location,
+            @ActionType int actionType) {
+        mTarget = target;
+        mLocation = location;
+        mAction = actionType;
+    }
+
+    private AppTargetEvent(Parcel parcel) {
+        mTarget = parcel.readParcelable(null);
+        mLocation = parcel.readString();
+        mAction = parcel.readInt();
+    }
+
+    /**
+     * Returns the app target.
+     */
+    @Nullable
+    public AppTarget getTarget() {
+        return mTarget;
+    }
+
+    /**
+     * Returns the launch location.
+     */
+    @NonNull
+    public String getLaunchLocation() {
+        return mLocation;
+    }
+
+    /**
+     * Returns the action type.
+     */
+    @NonNull
+    public int getAction() {
+        return mAction;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mTarget, 0);
+        dest.writeString(mLocation);
+        dest.writeInt(mAction);
+    }
+
+    /**
+     * @see Creator
+     */
+    public static final Creator<AppTargetEvent> CREATOR =
+            new Creator<AppTargetEvent>() {
+                public AppTargetEvent createFromParcel(Parcel parcel) {
+                    return new AppTargetEvent(parcel);
+                }
+
+                public AppTargetEvent[] newArray(int size) {
+                    return new AppTargetEvent[size];
+                }
+            };
+
+    /**
+     * A builder for app target events.
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private AppTarget mTarget;
+        private String mLocation;
+        private @ActionType int mAction;
+
+        public Builder(@Nullable AppTarget target, @ActionType int actionType) {
+            mTarget = target;
+            mAction = actionType;
+        }
+
+        /**
+         * Sets the launch location.
+         */
+        public Builder setLaunchLocation(String location) {
+            mLocation = location;
+            return this;
+        }
+
+        /**
+         * Builds a new event instance.
+         */
+        public AppTargetEvent build() {
+            return new AppTargetEvent(mTarget, mLocation, mAction);
+        }
+    }
+}
diff --git a/core/java/android/app/prediction/AppTargetId.aidl b/core/java/android/app/prediction/AppTargetId.aidl
new file mode 100644
index 0000000..bf69eea
--- /dev/null
+++ b/core/java/android/app/prediction/AppTargetId.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.prediction;
+
+parcelable AppTargetId;
diff --git a/core/java/android/app/prediction/AppTargetId.java b/core/java/android/app/prediction/AppTargetId.java
new file mode 100644
index 0000000..0b8fb47
--- /dev/null
+++ b/core/java/android/app/prediction/AppTargetId.java
@@ -0,0 +1,90 @@
+/*
+ * 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.app.prediction;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The id for a prediction target.
+ * @hide
+ */
+@SystemApi
+public final class AppTargetId implements Parcelable {
+
+    @NonNull
+    private final String mId;
+
+    /**
+     * @hide
+     */
+    public AppTargetId(@NonNull String id) {
+        mId = id;
+    }
+
+    private AppTargetId(Parcel parcel) {
+        mId = parcel.readString();
+    }
+
+    /**
+     * Returns the id.
+     * @hide
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+        AppTargetId other = (AppTargetId) o;
+        return mId.equals(other.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        // Ensure that the id has a consistent hash
+        return mId.hashCode();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+    }
+
+    /**
+     * @see Creator
+     */
+    public static final Creator<AppTargetId> CREATOR =
+            new Creator<AppTargetId>() {
+                public AppTargetId createFromParcel(Parcel parcel) {
+                    return new AppTargetId(parcel);
+                }
+
+                public AppTargetId[] newArray(int size) {
+                    return new AppTargetId[size];
+                }
+            };
+}
diff --git a/core/java/android/app/prediction/IPredictionCallback.aidl b/core/java/android/app/prediction/IPredictionCallback.aidl
new file mode 100644
index 0000000..f6f241e
--- /dev/null
+++ b/core/java/android/app/prediction/IPredictionCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.app.prediction;
+
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+oneway interface IPredictionCallback {
+
+    void onResult(in ParceledListSlice result);
+}
diff --git a/core/java/android/app/prediction/IPredictionManager.aidl b/core/java/android/app/prediction/IPredictionManager.aidl
new file mode 100644
index 0000000..114a1ff
--- /dev/null
+++ b/core/java/android/app/prediction/IPredictionManager.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.prediction;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.IPredictionCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+interface IPredictionManager {
+
+    void createPredictionSession(in AppPredictionContext context,
+            in AppPredictionSessionId sessionId);
+
+    void notifyAppTargetEvent(in AppPredictionSessionId sessionId, in AppTargetEvent event);
+
+    void notifyLocationShown(in AppPredictionSessionId sessionId, in String launchLocation,
+            in ParceledListSlice targetIds);
+
+    void sortAppTargets(in AppPredictionSessionId sessionId, in ParceledListSlice targets,
+            in IPredictionCallback callback);
+
+    void registerPredictionUpdates(in AppPredictionSessionId sessionId,
+            in IPredictionCallback callback);
+
+    void unregisterPredictionUpdates(in AppPredictionSessionId sessionId,
+            in IPredictionCallback callback);
+
+    void requestPredictionUpdate(in AppPredictionSessionId sessionId);
+
+    void onDestroyPredictionSession(in AppPredictionSessionId sessionId);
+}
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 0c9b41bf..2964fbc 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -50,4 +50,6 @@
     boolean removeRoleHolderFromController(in String roleName, in String packageName);
 
     List<String> getHeldRolesFromController(in String packageName);
+
+    String getDefaultSmsPackage(int userId);
 }
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 2d630a6..a6abe0b 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -68,6 +69,8 @@
 
     /**
      * The name of the dialer role.
+     *
+     * @see Intent#ACTION_DIAL
      */
     public static final String ROLE_DIALER = "android.app.role.DIALER";
 
@@ -100,6 +103,75 @@
     public static final String ROLE_MUSIC = "android.app.role.MUSIC";
 
     /**
+     * The name of the home role.
+     *
+     * @see Intent#CATEGORY_HOME
+     */
+    public static final String ROLE_HOME = "android.app.role.HOME";
+
+    /**
+     * The name of the emergency role
+     *
+     * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
+     */
+    public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
+
+    /**
+     * The name of the car mode dialer app role.
+     * <p>
+     * Similar to the {@link #ROLE_DIALER dialer} role, this role determines which app is
+     * responsible for showing the user interface for ongoing calls on the device. It is only used
+     * when the device is in car mode.
+     *
+     * @see #ROLE_DIALER
+     * @see android.app.UiModeManager#ACTION_ENTER_CAR_MODE
+     * @see android.telecom.InCallService
+     *
+     * TODO: STOPSHIP: Make name of required roles public API
+     * @hide
+     */
+    public static final String ROLE_CAR_MODE_DIALER_APP = "android.app.role.CAR_MODE_DIALER_APP";
+
+    /**
+     * The name of the proxy calling role.
+     * <p>
+     * A proxy calling app provides a means to re-write the phone number for an outgoing call to
+     * place the call through a proxy calling service.
+     *
+     * @see android.telecom.CallRedirectionService
+     *
+     * TODO: STOPSHIP: Make name of required roles public API
+     * @hide
+     */
+    public static final String ROLE_PROXY_CALLING_APP = "android.app.role.PROXY_CALLING_APP";
+
+    /**
+     * The name of the call screening and caller id role.
+     *
+     * @see android.telecom.CallScreeningService
+     *
+     * TODO: STOPSHIP: Make name of required roles public API
+     * @hide
+     */
+    public static final String ROLE_CALL_SCREENING_APP = "android.app.role.CALL_SCREENING_APP";
+
+    /**
+     * The name of the call companion app role.
+     * <p>
+     * A call companion app provides no user interface for calls, but will be bound to by Telecom
+     * when there are active calls on the device. Companion apps for wearable devices are an
+     * acceptable use-case.
+     * <p>
+     * Multiple apps may hold this role at the same time.
+     *
+     * @see android.telecom.InCallService
+     *
+     * TODO: STOPSHIP: Make name of required roles public API
+     * @hide
+     */
+    public static final String ROLE_CALL_COMPANION_APP = "android.app.role.CALL_COMPANION_APP";
+
+    /**
      * The action used to request user approval of a role for an application.
      *
      * @hide
@@ -107,16 +179,6 @@
     public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
 
     /**
-     * The name of the requested role.
-     * <p>
-     * <strong>Type:</strong> String
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
-
-    /**
      * The permission required to manage records of role holders in {@link RoleManager} directly.
      *
      * @hide
@@ -164,7 +226,7 @@
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
         Intent intent = new Intent(ACTION_REQUEST_ROLE);
         intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
-        intent.putExtra(EXTRA_REQUEST_ROLE_NAME, roleName);
+        intent.putExtra(Intent.EXTRA_ROLE_NAME, roleName);
         return intent;
     }
 
@@ -542,7 +604,6 @@
         }
     }
 
-
     /**
      * Returns the list of all roles that the given package is currently holding
      *
@@ -563,6 +624,22 @@
         }
     }
 
+    /**
+     * Allows getting the role holder for {@link #ROLE_SMS} without
+     * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by
+     * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}
+     *
+     * @hide
+     */
+    @Nullable
+    public String getDefaultSmsPackage(@UserIdInt int userId) {
+        try {
+            return mService.getDefaultSmsPackage(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private static class RoleManagerCallbackDelegate extends IRoleManagerCallback.Stub {
 
         @NonNull
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index a2b7d58..8ee9e53 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -36,6 +36,11 @@
     private Configuration mConfiguration;
 
     @Override
+    public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
+        client.updatePendingActivityConfiguration(token, mConfiguration);
+    }
+
+    @Override
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         // TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
index a79ad2f..aaae57e5 100644
--- a/core/java/android/app/usage/EventList.java
+++ b/core/java/android/app/usage/EventList.java
@@ -103,21 +103,4 @@
         }
         return result;
     }
-
-    /**
-     * Remove events of certain type on or after a timestamp.
-     * @param type The type of event to remove.
-     * @param timeStamp the timeStamp on or after which to remove the event.
-     */
-    public void removeOnOrAfter(int type, long timeStamp) {
-        for (int i = mEvents.size() - 1; i >= 0; i--) {
-            UsageEvents.Event event = mEvents.get(i);
-            if (event.mTimeStamp < timeStamp) {
-                break;
-            }
-            if (event.mEventType == type) {
-                mEvents.remove(i);
-            }
-        }
-    }
 }
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4d52263..bbae7d3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -55,4 +55,8 @@
             long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
             in PendingIntent sessionEndCallbackIntent, String callingPackage);
     void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
+    void reportUsageStart(in IBinder activity, String token, String callingPackage);
+    void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
+            String callingPackage);
+    void reportUsageStop(in IBinder activity, String token, String callingPackage);
 }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index d7a5328..2c5fe04 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -245,10 +245,18 @@
         public static final int FLUSH_TO_DISK = 25;
 
         /**
+         * An event type denoting that the device underwent a shutdown process.
+         * A DEVICE_SHUTDOWN event should be treated as if all started activities and foreground
+         * services are now stopped and no explicit {@link #ACTIVITY_STOPPED} and
+         * {@link #FOREGROUND_SERVICE_STOP} events will be generated for them.
+         */
+        public static final int DEVICE_SHUTDOWN = 26;
+
+        /**
          * Keep in sync with the greatest event type value.
          * @hide
          */
-        public static final int MAX_EVENT_TYPE = 25;
+        public static final int MAX_EVENT_TYPE = 26;
 
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 308180b..94a2a3e 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -21,6 +21,7 @@
 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
 import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.END_OF_DAY;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
@@ -119,12 +120,9 @@
     public int mLastEvent;
 
     /**
-     * If an activity is visible(onStart(), onPause() states) or in foreground (onResume() state),
-     * it has one entry in this map. When an activity becomes invisible (onStop() or onDestroy()),
-     * it is removed from this map.
      * Key is instanceId of the activity (ActivityRecode appToken hashCode)..
-     * Value is this activity's last event, one of ACTIVITY_RESUMED or
-     * ACTIVITY_PAUSED.
+     * Value is this activity's last event, one of ACTIVITY_RESUMED, ACTIVITY_PAUSED or
+     * ACTIVITY_STOPPED.
      * {@hide}
      */
     public SparseIntArray mActivities = new SparseIntArray();
@@ -560,6 +558,7 @@
                 mLastTimeForegroundServiceUsed = timeStamp;
                 mForegroundServices.put(className, eventType);
                 break;
+            case DEVICE_SHUTDOWN:
             case FLUSH_TO_DISK:
                 // update usage of all active activities/services.
                 if (hasForegroundActivity()) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 26beb45..605deac 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -579,15 +580,18 @@
     /**
      * @hide
      * Register an app usage limit observer that receives a callback on the provided intent when
-     * the sum of usages of apps in the packages array exceeds the {@code timeLimit} specified. The
-     * observer will automatically be unregistered when the time limit is reached and the intent
-     * is delivered. Registering an {@code observerId} that was already registered will override
-     * the previous one. No more than 1000 unique {@code observerId} may be registered by a single
-     * uid at any one time.
+     * the sum of usages of apps and tokens in the {@code observed} array exceeds the
+     * {@code timeLimit} specified. The structure of a token is a String with the reporting
+     * package's name and a token the reporting app will use, separated by the forward slash
+     * character. Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+     * The observer will automatically be unregistered when the time limit is reached and the
+     * intent is delivered. Registering an {@code observerId} that was already registered will
+     * override the previous one. No more than 1000 unique {@code observerId} may be registered by
+     * a single uid at any one time.
      * @param observerId A unique id associated with the group of apps to be monitored. There can
      *                  be multiple groups with common packages and different time limits.
-     * @param packages The list of packages to observe for foreground activity time. Cannot be null
-     *                 and must include at least one package.
+     * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+     *                         null and must include at least one package or token.
      * @param timeLimit The total time the set of apps can be in the foreground before the
      *                  callbackIntent is delivered. Must be at least one minute.
      * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -600,11 +604,11 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
-    public void registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit,
-            @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
+    public void registerAppUsageObserver(int observerId, @NonNull String[] observedEntities,
+            long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
         try {
-            mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
-                    callbackIntent, mContext.getOpPackageName());
+            mService.registerAppUsageObserver(observerId, observedEntities,
+                    timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -631,18 +635,21 @@
 
     /**
      * Register a usage session observer that receives a callback on the provided {@code
-     * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds
-     * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has
-     * been reached, the usage session observer will receive a callback on the provided {@code
-     * sessionEndCallbackIntent} when the usage session ends. Registering another session
-     * observer against a {@code sessionObserverId} that has already been registered will
-     * override the previous session observer.
+     * limitReachedCallbackIntent} when the sum of usages of apps and tokens in the {@code
+     * observed} array exceeds the {@code timeLimit} specified within a usage session. The
+     * structure of a token is a String with the reporting packages' name and a token the
+     * reporting app will use, separated by the forward slash character.
+     * Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+     * After the {@code timeLimit} has been reached, the usage session observer will receive a
+     * callback on the provided {@code sessionEndCallbackIntent} when the usage session ends.
+     * Registering another session observer against a {@code sessionObserverId} that has already
+     * been registered will override the previous session observer.
      *
      * @param sessionObserverId A unique id associated with the group of apps to be
      *                          monitored. There can be multiple groups with common
      *                          packages and different time limits.
-     * @param packages The list of packages to observe for foreground activity time. Cannot be null
-     *                 and must include at least one package.
+     * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+     *                         null and must include at least one package or token.
      * @param timeLimit The total time the set of apps can be used continuously before the {@code
      *                  limitReachedCallbackIntent} is delivered. Must be at least one minute.
      * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -668,13 +675,13 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
-    public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages,
-            long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime,
-            @NonNull TimeUnit sessionThresholdTimeUnit,
+    public void registerUsageSessionObserver(int sessionObserverId,
+            @NonNull String[] observedEntities, long timeLimit, @NonNull TimeUnit timeUnit,
+            long sessionThresholdTime,  @NonNull TimeUnit sessionThresholdTimeUnit,
             @NonNull PendingIntent limitReachedCallbackIntent,
             @Nullable PendingIntent sessionEndCallbackIntent) {
         try {
-            mService.registerUsageSessionObserver(sessionObserverId, packages,
+            mService.registerUsageSessionObserver(sessionObserverId, observedEntities,
                     timeUnit.toMillis(timeLimit),
                     sessionThresholdTimeUnit.toMillis(sessionThresholdTime),
                     limitReachedCallbackIntent, sessionEndCallbackIntent,
@@ -704,6 +711,71 @@
         }
     }
 
+    /**
+     * Report usage associated with a particular {@code token} has started. Tokens are app defined
+     * strings used to represent usage of in-app features. Apps with the {@link
+     * android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
+     * to monitor the usage of a token. In app usage can only associated with an {@code activity}
+     * and usage will be considered stopped if the activity stops or crashes.
+     * @see #registerAppUsageObserver
+     * @see #registerUsageSessionObserver
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStart(@NonNull Activity activity, @NonNull String token) {
+        try {
+            mService.reportUsageStart(activity.getActivityToken(), token,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report usage associated with a particular {@code token} had started some amount of time in
+     * the past. Tokens are app defined strings used to represent usage of in-app features. Apps
+     * with the {@link android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time
+     * limit observers to monitor the usage of a token. In app usage can only associated with an
+     * {@code activity} and usage will be considered stopped if the activity stops or crashes.
+     * @see #registerAppUsageObserver
+     * @see #registerUsageSessionObserver
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @param timeAgoMs How long ago the start of usage took place
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStart(@NonNull Activity activity, @NonNull String token,
+                                 long timeAgoMs) {
+        try {
+            mService.reportPastUsageStart(activity.getActivityToken(), token, timeAgoMs,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report the usage associated with a particular {@code token} has stopped.
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStop(@NonNull Activity activity, @NonNull String token) {
+        try {
+            mService.reportUsageStop(activity.getActivityToken(), token,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public static String reasonToString(int standbyReason) {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 2edad35..cc3ab00 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -98,6 +98,12 @@
     public abstract void prepareShutdown();
 
     /**
+     * When the device power button is long pressed for 3.5 seconds, prepareForPossibleShutdown()
+     * is called.
+     */
+    public abstract void prepareForPossibleShutdown();
+
+    /**
      * Returns true if the app has not been used for a certain amount of time. How much time?
      * Could be hours, could be days, who knows?
      *
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index c740c42..3e9dd28 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -375,12 +375,12 @@
     }
 
     /**
-     * Sets whether the widget should is being displayed on a light/white background and use an
+     * Sets whether the widget is being displayed on a light/white background and use an
      * alternate UI if available.
      * @see RemoteViews#setLightBackgroundLayoutId(int)
      */
-    public void setOnLightBackground(boolean useDarkTextLayout) {
-        mOnLightBackground = useDarkTextLayout;
+    public void setOnLightBackground(boolean onLightBackground) {
+        mOnLightBackground = onLightBackground;
     }
 
     /**
diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java
new file mode 100644
index 0000000..6b7f10e
--- /dev/null
+++ b/core/java/android/attention/AttentionManagerInternal.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.attention;
+
+/**
+ * Attention manager local system server interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class AttentionManagerInternal {
+    /**
+     * Returns {@code true} if attention service is supported on this device.
+     */
+    public abstract boolean isAttentionServiceSupported();
+
+    /**
+     * Checks whether user attention is at the screen and calls in the provided callback.
+     *
+     * @param requestCode   a code associated with the attention check request; this code would be
+     *                      used to call back in {@link AttentionCallbackInternal#onSuccess} and
+     *                      {@link AttentionCallbackInternal#onFailure}
+     * @param timeoutMillis a budget for the attention check; if it takes longer - {@link
+     *                      AttentionCallbackInternal#onFailure} would be called with the {@link
+     *                      android.service.attention.AttentionService#ATTENTION_FAILURE_TIMED_OUT}
+     *                      code
+     * @param callback      a callback for when the attention check has completed
+     * @return {@code true} if the attention check should succeed; {@false} otherwise.
+     */
+    public abstract boolean checkAttention(int requestCode,
+            long timeoutMillis, AttentionCallbackInternal callback);
+
+    /**
+     * Cancels the specified attention check in case it's no longer needed.
+     *
+     * @param requestCode a code provided during {@link #checkAttention}
+     */
+    public abstract void cancelAttentionCheck(int requestCode);
+
+    /** Internal interface for attention callback. */
+    public abstract static class AttentionCallbackInternal {
+        /**
+         * Provides the result of the attention check, if the check was successful.
+         *
+         * @param requestCode a code provided in {@link #checkAttention}
+         * @param result      an int with the result of the check
+         * @param timestamp   a {@code SystemClock.uptimeMillis()} timestamp associated with the
+         *                    attention check
+         */
+        public abstract void onSuccess(int requestCode, int result, long timestamp);
+
+        /**
+         * Provides the explanation for why the attention check had failed.
+         *
+         * @param requestCode a code provided in {@link #checkAttention}
+         * @param error       an int with the reason for failure
+         */
+        public abstract void onFailure(int requestCode, int error);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 87b64797..1945b2f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2066,8 +2066,7 @@
      * Get the current connection state of a profile.
      * This function can be used to check whether the local Bluetooth adapter
      * is connected to any remote device for a specific profile.
-     * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
-     * {@link BluetoothProfile#A2DP}.
+     * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
      *
      * <p> Return value can be one of
      * {@link BluetoothProfile#STATE_DISCONNECTED},
@@ -2441,16 +2440,15 @@
     /**
      * Get the profile proxy object associated with the profile.
      *
-     * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
-     * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or
-     * {@link BluetoothProfile#GATT_SERVER}. Clients must implement
-     * {@link BluetoothProfile.ServiceListener} to get notified of
-     * the connection status and to get the proxy object.
+     * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP},
+     * {@link BluetoothProfile#GATT}, or {@link BluetoothProfile#GATT_SERVER}. Clients must
+     * implement {@link BluetoothProfile.ServiceListener} to get notified of the connection status
+     * and to get the proxy object.
      *
      * @param context Context of the application
      * @param listener The service Listener for connection callbacks.
-     * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, {@link
-     * BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or
+     * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET},
+     * {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or
      * {@link BluetoothProfile#GATT_SERVER}.
      * @return true on success, false on error
      */
@@ -2479,8 +2477,8 @@
             BluetoothPan pan = new BluetoothPan(context, listener);
             return true;
         } else if (profile == BluetoothProfile.HEALTH) {
-            BluetoothHealth health = new BluetoothHealth(context, listener);
-            return true;
+            Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
+            return false;
         } else if (profile == BluetoothProfile.MAP) {
             BluetoothMap map = new BluetoothMap(context, listener);
             return true;
@@ -2512,8 +2510,7 @@
      *
      * <p> Clients should call this when they are no longer using
      * the proxy obtained from {@link #getProfileProxy}.
-     * Profile can be one of  {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or
-     * {@link BluetoothProfile#A2DP}
+     * Profile can be one of  {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}
      *
      * @param profile
      * @param proxy Profile proxy object
@@ -2548,10 +2545,6 @@
                 BluetoothPan pan = (BluetoothPan) proxy;
                 pan.close();
                 break;
-            case BluetoothProfile.HEALTH:
-                BluetoothHealth health = (BluetoothHealth) proxy;
-                health.close();
-                break;
             case BluetoothProfile.GATT:
                 BluetoothGatt gatt = (BluetoothGatt) proxy;
                 gatt.close();
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 22d41d9..e2e56fd 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -16,15 +16,7 @@
 
 package android.bluetooth;
 
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Binder;
-import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -54,79 +46,59 @@
  * <li> When done, close the health channel by calling {@link #disconnectChannel}
  * and unregister the application configuration calling
  * {@link #unregisterAppConfiguration}
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps
+ * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
  */
+@Deprecated
 public final class BluetoothHealth implements BluetoothProfile {
     private static final String TAG = "BluetoothHealth";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
     /**
      * Health Profile Source Role - the health device.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public static final int SOURCE_ROLE = 1 << 0;
 
     /**
      * Health Profile Sink Role the device talking to the health device.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public static final int SINK_ROLE = 1 << 1;
 
     /**
      * Health Profile - Channel Type used - Reliable
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public static final int CHANNEL_TYPE_RELIABLE = 10;
 
     /**
      * Health Profile - Channel Type used - Streaming
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public static final int CHANNEL_TYPE_STREAMING = 11;
 
-    /**
-     * @hide
-     */
-    public static final int CHANNEL_TYPE_ANY = 12;
-
-    /** @hide */
-    public static final int HEALTH_OPERATION_SUCCESS = 6000;
-    /** @hide */
-    public static final int HEALTH_OPERATION_ERROR = 6001;
-    /** @hide */
-    public static final int HEALTH_OPERATION_INVALID_ARGS = 6002;
-    /** @hide */
-    public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003;
-    /** @hide */
-    public static final int HEALTH_OPERATION_NOT_FOUND = 6004;
-    /** @hide */
-    public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
-                }
-            };
-
 
     /**
      * Register an application configuration that acts as a Health SINK.
@@ -142,53 +114,17 @@
      * @param callback A callback to indicate success or failure of the registration and all
      * operations done on this application configuration.
      * @return If true, callback will be called.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public boolean registerSinkAppConfiguration(String name, int dataType,
             BluetoothHealthCallback callback) {
-        if (!isEnabled() || name == null) return false;
-
-        if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
-        return registerAppConfiguration(name, dataType, SINK_ROLE,
-                CHANNEL_TYPE_ANY, callback);
-    }
-
-    /**
-     * Register an application configuration that acts as a Health SINK or in a Health
-     * SOURCE role.This is an asynchronous call and so
-     * the callback is used to notify success or failure if the function returns true.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param name The friendly name associated with the application or configuration.
-     * @param dataType The dataType of the Source role of Health Profile.
-     * @param channelType The channel type. Will be one of {@link #CHANNEL_TYPE_RELIABLE}  or {@link
-     * #CHANNEL_TYPE_STREAMING}
-     * @param callback - A callback to indicate success or failure.
-     * @return If true, callback will be called.
-     * @hide
-     */
-    public boolean registerAppConfiguration(String name, int dataType, int role,
-            int channelType, BluetoothHealthCallback callback) {
-        boolean result = false;
-        if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
-
-        if (VDBG) log("registerApplication(" + name + ":" + dataType + ")");
-        BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
-        BluetoothHealthAppConfiguration config =
-                new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
-
-        final IBluetoothHealth service = mService;
-        if (service != null) {
-            try {
-                result = service.registerAppConfiguration(config, wrapper);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return result;
+        Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated");
+        return false;
     }
 
     /**
@@ -199,22 +135,16 @@
      *
      * @param config The health app configuration
      * @return Success or failure.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
-        boolean result = false;
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled() && config != null) {
-            try {
-                result = service.unregisterAppConfiguration(config);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-
-        return result;
+        Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated");
+        return false;
     }
 
     /**
@@ -228,49 +158,16 @@
      * @param config The application configuration which has been registered using {@link
      * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
      * @return If true, the callback associated with the application config will be called.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public boolean connectChannelToSource(BluetoothDevice device,
             BluetoothHealthAppConfiguration config) {
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled() && isValidDevice(device) && config != null) {
-            try {
-                return service.connectChannelToSource(device, config);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return false;
-    }
-
-    /**
-     * Connect to a health device which has the {@link #SINK_ROLE}.
-     * This is an asynchronous call. If this function returns true, the callback
-     * associated with the application configuration will be called.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param device The remote Bluetooth device.
-     * @param config The application configuration which has been registered using {@link
-     * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
-     * @return If true, the callback associated with the application config will be called.
-     * @hide
-     */
-    public boolean connectChannelToSink(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config, int channelType) {
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled() && isValidDevice(device) && config != null) {
-            try {
-                return service.connectChannelToSink(device, config, channelType);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
+        Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated");
         return false;
     }
 
@@ -286,20 +183,16 @@
      * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
      * @param channelId The channel id associated with the channel
      * @return If true, the callback associated with the application config will be called.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public boolean disconnectChannel(BluetoothDevice device,
             BluetoothHealthAppConfiguration config, int channelId) {
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled() && isValidDevice(device) && config != null) {
-            try {
-                return service.disconnectChannel(device, config, channelId);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
+        Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated");
         return false;
     }
 
@@ -315,20 +208,16 @@
      * @param device The remote Bluetooth health device
      * @param config The application configuration
      * @return null on failure, ParcelFileDescriptor on success.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
             BluetoothHealthAppConfiguration config) {
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled() && isValidDevice(device) && config != null) {
-            try {
-                return service.getMainChannelFd(device, config);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
+        Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated");
         return null;
     }
 
@@ -348,17 +237,7 @@
      */
     @Override
     public int getConnectionState(BluetoothDevice device) {
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled() && isValidDevice(device)) {
-            try {
-                return service.getHealthDeviceConnectionState(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
+        Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated");
         return STATE_DISCONNECTED;
     }
 
@@ -378,17 +257,8 @@
      */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled()) {
-            try {
-                return service.getConnectedHealthDevices();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return new ArrayList<BluetoothDevice>();
-            }
-        }
-        if (service == null) Log.w(TAG, "Proxy not attached to service");
-        return new ArrayList<BluetoothDevice>();
+        Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated");
+        return new ArrayList<>();
     }
 
     /**
@@ -410,163 +280,81 @@
      */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        final IBluetoothHealth service = mService;
-        if (service != null && isEnabled()) {
-            try {
-                return service.getHealthDevicesMatchingConnectionStates(states);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return new ArrayList<BluetoothDevice>();
-            }
-        }
-        if (service == null) Log.w(TAG, "Proxy not attached to service");
-        return new ArrayList<BluetoothDevice>();
+        Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated");
+        return new ArrayList<>();
     }
 
-    private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub {
-        private BluetoothHealthCallback mCallback;
-
-        public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
-                int status) {
-            mCallback.onHealthAppConfigurationStatusChange(config, status);
-        }
-
-        @Override
-        public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
-                BluetoothDevice device, int prevState, int newState,
-                ParcelFileDescriptor fd, int channelId) {
-            mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd,
-                    channelId);
-        }
-    }
-
-    /** Health Channel Connection State - Disconnected */
+    /** Health Channel Connection State - Disconnected
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
+     */
+    @Deprecated
     public static final int STATE_CHANNEL_DISCONNECTED = 0;
-    /** Health Channel Connection State - Connecting */
+    /** Health Channel Connection State - Connecting
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
+     */
+    @Deprecated
     public static final int STATE_CHANNEL_CONNECTING = 1;
-    /** Health Channel Connection State - Connected */
+    /** Health Channel Connection State - Connected
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
+     */
+    @Deprecated
     public static final int STATE_CHANNEL_CONNECTED = 2;
-    /** Health Channel Connection State - Disconnecting */
+    /** Health Channel Connection State - Disconnecting
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
+     */
+    @Deprecated
     public static final int STATE_CHANNEL_DISCONNECTING = 3;
 
-    /** Health App Configuration registration success */
-    public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
-    /** Health App Configuration registration failure */
-    public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
-    /** Health App Configuration un-registration success */
-    public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
-    /** Health App Configuration un-registration failure */
-    public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
-
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothHealth mService;
-    BluetoothAdapter mAdapter;
-
-    /**
-     * Create a BluetoothHealth proxy object.
+    /** Health App Configuration registration success
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
-    /*package*/ BluetoothHealth(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothHealth.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
-            return false;
-        }
-        return true;
-    }
-
-    /*package*/ void close() {
-        if (VDBG) log("close()");
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
-    }
-
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service));
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH);
-            }
-        }
-    };
-
-    private boolean isEnabled() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        log("Bluetooth is Not enabled");
-        return false;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private boolean checkAppParam(String name, int role, int channelType,
-            BluetoothHealthCallback callback) {
-        if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE)
-                || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING
-                    && channelType != CHANNEL_TYPE_ANY)
-                || callback == null) {
-            return false;
-        }
-        if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false;
-        return true;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
+    @Deprecated
+    public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
+    /** Health App Configuration registration failure
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
+     */
+    @Deprecated
+    public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
+    /** Health App Configuration un-registration success
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
+     */
+    @Deprecated
+    public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
+    /** Health App Configuration un-registration failure
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
+     */
+    @Deprecated
+    public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
 }
diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
index 7c9db6f..9788bbf 100644
--- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
+++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
@@ -25,72 +25,14 @@
  * the {@link BluetoothHealth} class. This class represents an application configuration
  * that the Bluetooth Health third party application will register to communicate with the
  * remote Bluetooth health device.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
  */
+@Deprecated
 public final class BluetoothHealthAppConfiguration implements Parcelable {
-    private final String mName;
-    private final int mDataType;
-    private final int mRole;
-    private final int mChannelType;
-
-    /**
-     * Constructor to register the SINK role
-     *
-     * @param name Friendly name associated with the application configuration
-     * @param dataType Data Type of the remote Bluetooth Health device
-     * @hide
-     */
-    BluetoothHealthAppConfiguration(String name, int dataType) {
-        mName = name;
-        mDataType = dataType;
-        mRole = BluetoothHealth.SINK_ROLE;
-        mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY;
-    }
-
-    /**
-     * Constructor to register the application configuration.
-     *
-     * @param name Friendly name associated with the application configuration
-     * @param dataType Data Type of the remote Bluetooth Health device
-     * @param role {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
-     * @hide
-     */
-    BluetoothHealthAppConfiguration(String name, int dataType, int role, int
-            channelType) {
-        mName = name;
-        mDataType = dataType;
-        mRole = role;
-        mChannelType = channelType;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof BluetoothHealthAppConfiguration) {
-            BluetoothHealthAppConfiguration config = (BluetoothHealthAppConfiguration) o;
-
-            if (mName == null) return false;
-
-            return mName.equals(config.getName()) && mDataType == config.getDataType()
-                    && mRole == config.getRole() && mChannelType == config.getChannelType();
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-        result = 31 * result + (mName != null ? mName.hashCode() : 0);
-        result = 31 * result + mDataType;
-        result = 31 * result + mRole;
-        result = 31 * result + mChannelType;
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType
-                + ", mRole = " + mRole + ",mChannelType = " + mChannelType + "]";
-    }
-
     @Override
     public int describeContents() {
         return 0;
@@ -100,50 +42,59 @@
      * Return the data type associated with this application configuration.
      *
      * @return dataType
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public int getDataType() {
-        return mDataType;
+        return 0;
     }
 
     /**
      * Return the name of the application configuration.
      *
      * @return String name
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public String getName() {
-        return mName;
+        return null;
     }
 
     /**
      * Return the role associated with this application configuration.
      *
      * @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     public int getRole() {
-        return mRole;
+        return 0;
     }
 
     /**
-     * Return the channel type associated with this application configuration.
-     *
-     * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or {@link
-     * BluetoothHealth#CHANNEL_TYPE_STREAMING} or {@link BluetoothHealth#CHANNEL_TYPE_ANY}.
-     * @hide
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
-    public int getChannelType() {
-        return mChannelType;
-    }
-
+    @Deprecated
     public static final Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR =
             new Parcelable.Creator<BluetoothHealthAppConfiguration>() {
                 @Override
                 public BluetoothHealthAppConfiguration createFromParcel(Parcel in) {
-                    String name = in.readString();
-                    int type = in.readInt();
-                    int role = in.readInt();
-                    int channelType = in.readInt();
-                    return new BluetoothHealthAppConfiguration(name, type, role,
-                            channelType);
+                    return new BluetoothHealthAppConfiguration();
                 }
 
                 @Override
@@ -153,10 +104,5 @@
             };
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mName);
-        out.writeInt(mDataType);
-        out.writeInt(mRole);
-        out.writeInt(mChannelType);
-    }
+    public void writeToParcel(Parcel out, int flags) {}
 }
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
index 4023485..4769212 100644
--- a/core/java/android/bluetooth/BluetoothHealthCallback.java
+++ b/core/java/android/bluetooth/BluetoothHealthCallback.java
@@ -23,7 +23,13 @@
 
 /**
  * This abstract class is used to implement {@link BluetoothHealth} callbacks.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
  */
+@Deprecated
 public abstract class BluetoothHealthCallback {
     private static final String TAG = "BluetoothHealthCallback";
 
@@ -38,8 +44,14 @@
      * BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or
      * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS}
      * or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
     @BinderThread
+    @Deprecated
     public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
             int status) {
         Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
@@ -58,8 +70,14 @@
      * @param fd The Parcel File Descriptor when the channel state is connected.
      * @param channelId The id associated with the channel. This id will be used in future calls
      * like when disconnecting the channel.
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
     @BinderThread
+    @Deprecated
     public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
             BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
             int channelId) {
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 3c3a01b..3c87c73 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -72,7 +72,13 @@
 
     /**
      * Health Profile
+     *
+     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+     * {@link BluetoothAdapter#listenUsingL2capChannel()}, or
+     * {@link BluetoothDevice#createL2capChannel(int)}
      */
+    @Deprecated
     int HEALTH = 3;
 
     /**
@@ -269,9 +275,8 @@
          * Called to notify the client when the proxy object has been
          * connected to the service.
          *
-         * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP}
-         * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or {@link
-         * BluetoothA2dp}
+         * @param profile - One of {@link #HEADSET} or {@link #A2DP}
+         * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp}
          */
         public void onServiceConnected(int profile, BluetoothProfile proxy);
 
@@ -279,7 +284,7 @@
          * Called to notify the client that this proxy object has been
          * disconnected from the service.
          *
-         * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP}
+         * @param profile - One of {@link #HEADSET} or {@link #A2DP}
          */
         public void onServiceDisconnected(int profile);
     }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index eb7be6f..cefc700 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -337,6 +337,15 @@
     public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x0100;
 
     /**
+     * @hide Flag for {@link #bindService}: the service being bound to represents a
+     * protected system component, so must have association restrictions applied to it.
+     * That is, a system config must have one or more allow-association tags limiting
+     * which packages it can interact with.  If it does not have any such association
+     * restrictions, a default empty set will be created.
+     */
+    public static final int BIND_RESTRICT_ASSOCIATIONS = 0x00200000;
+
+    /**
      * @hide Flag for {@link #bindService}: allows binding to a service provided
      * by an instant app. Note that the caller may not have access to the instant
      * app providing the service which is a violation of the instant app sandbox.
@@ -3093,6 +3102,7 @@
             VIBRATOR_SERVICE,
             //@hide: STATUS_BAR_SERVICE,
             CONNECTIVITY_SERVICE,
+            //@hide: IP_MEMORY_STORE_SERVICE,
             IPSEC_SERVICE,
             //@hide: UPDATE_LOCK_SERVICE,
             //@hide: NETWORKMANAGEMENT_SERVICE,
@@ -3630,6 +3640,14 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.net.IpMemoryStore} to store and read information about
+     * known networks.
+     * @hide
+     */
+    public static final String IP_MEMORY_STORE_SERVICE = "ipmemorystore";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
      * IPSec.
      *
@@ -3975,6 +3993,24 @@
     public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
 
     /**
+     * Used for getting content selections and classifications for task snapshots.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
+
+    /**
+     * Official published name of the app prediction service.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String APP_PREDICTION_SERVICE = "app_prediction";
+
+    /**
      * Use with {@link #getSystemService(String)} to access the
      * {@link com.android.server.voiceinteraction.SoundTriggerService}.
      *
@@ -4427,6 +4463,16 @@
     public static final String STATS_MANAGER = "stats";
 
     /**
+     * Service to capture a bugreport.
+     * @see #getSystemService(String)
+     * @see android.os.BugreportManager
+     * @hide
+     */
+    // TODO: Expose API when the implementation is more complete.
+    // @SystemApi
+    public static final String BUGREPORT_SERVICE = "bugreport";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.content.om.OverlayManager} for managing overlay packages.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7b3497b..8497656 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -33,6 +33,7 @@
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -816,6 +817,28 @@
             = "android.intent.action.SHOW_APP_INFO";
 
     /**
+     * Activity Action: Start an activity to show the app's detailed usage information for
+     * permission protected data.
+     *
+     * The Intent contains an extra {@link #EXTRA_PERMISSION_USAGE_PERMISSIONS} that is of
+     * type {@code String[]} and contains the specific permissions to show information for.
+     *
+     * Apps should handle this intent if they want to provide more information about permission
+     * usage to users beyond the information provided in the manifest.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PERMISSION_USAGE_DETAILS =
+            "android.intent.action.PERMISSION_USAGE_DETAILS";
+
+    /**
+     * The name of the extra used to contain the permissions in
+     * {@link #ACTION_PERMISSION_USAGE_DETAILS}.
+     * @see #ACTION_PERMISSION_USAGE_DETAILS
+     */
+    public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS =
+            "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
+
+    /**
      * Represents a shortcut/live folder icon resource.
      *
      * @see Intent#ACTION_CREATE_SHORTCUT
@@ -1811,6 +1834,54 @@
             "android.intent.action.REVIEW_PERMISSIONS";
 
     /**
+     * Activity action: Launch UI to manage a default app.
+     * <p>
+     * Input: {@link #EXTRA_ROLE_NAME} specifies the role of the default app which will be managed
+     * by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_MANAGE_DEFAULT_APP =
+            "android.intent.action.MANAGE_DEFAULT_APP";
+
+    /**
+     * Intent extra: A role name.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     * @see android.app.role.RoleManager
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
+
+    /**
+     * Activity action: Launch UI to manage special app accesses.
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_MANAGE_SPECIAL_APP_ACCESSES =
+            "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
+
+    /**
      * Intent extra: A callback for reporting remote result as a bundle.
      * <p>
      * Type: IRemoteCallback
@@ -1884,6 +1955,17 @@
     public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS";
 
     /**
+     * Intent extra: ID of the shortcut used to send the share intent.
+     *
+     * @see ShortcutInfo#getId()
+     *
+     * <p>
+     * Type: String
+     * </p>
+     */
+    public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+
+    /**
      * Activity action: Launch UI to manage which apps have a given permission.
      * <p>
      * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission group
@@ -1914,16 +1996,35 @@
     public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
 
     /**
+     * Intent extra: The name of a permission group.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PERMISSION_GROUP_NAME =
+            "android.intent.extra.PERMISSION_GROUP_NAME";
+
+    /**
      * Activity action: Launch UI to review app uses of permissions.
      * <p>
      * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission name
-     * that will be displayed by the launched UI.
+     * that will be displayed by the launched UI.  Do not pass both this and
+     * {@link #EXTRA_PERMISSION_GROUP_NAME} .
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group name
+     * that will be displayed by the launched UI.  Do not pass both this and
+     * {@link #EXTRA_PERMISSION_NAME}.
      * </p>
      * <p>
      * Output: Nothing.
      * </p>
      *
      * @see #EXTRA_PERMISSION_NAME
+     * @see #EXTRA_PERMISSION_GROUP_NAME
      *
      * @hide
      */
@@ -2353,6 +2454,25 @@
     public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
 
     /**
+     * Broadcast Action: Distracting packages have been changed.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been changed.
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been changed.
+     * <li> {@link #EXTRA_DISTRACTION_RESTRICTIONS} the new restrictions set on these packages.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. It is only sent to registered receivers.
+     *
+     * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DISTRACTING_PACKAGES_CHANGED =
+            "android.intent.action.DISTRACTING_PACKAGES_CHANGED";
+
+    /**
      * Broadcast Action: Sent to a package that has been suspended by the system. This is sent
      * whenever a package is put into a suspended state or any of its app extras change while in the
      * suspended state.
@@ -5062,6 +5182,17 @@
             "android.intent.extra.changed_uid_list";
 
     /**
+     * An integer denoting a bitwise combination of restrictions set on distracting packages via
+     * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+     *
+     * @hide
+     * @see PackageManager.DistractionRestriction
+     * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+     */
+    public static final String EXTRA_DISTRACTION_RESTRICTIONS =
+            "android.intent.extra.distraction_restrictions";
+
+    /**
      * @hide
      * Magic extra system code can use when binding, to give a label for
      * who it is that has bound to a service.  This is an integer giving
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index dd55003..1989f06 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -18,8 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,8 +31,10 @@
  *
  * @hide
  */
+@SystemApi
 public final class OverlayInfo implements Parcelable {
 
+    /** @hide */
     @IntDef(prefix = "STATE_", value = {
             STATE_UNKNOWN,
             STATE_MISSING_TARGET,
@@ -44,6 +45,7 @@
             STATE_TARGET_UPGRADING,
             STATE_OVERLAY_UPGRADING,
     })
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {}
 
@@ -52,17 +54,23 @@
      * objects exposed outside the {@link
      * com.android.server.om.OverlayManagerService} should never have this
      * state.
+     *
+     * @hide
      */
     public static final int STATE_UNKNOWN = -1;
 
     /**
      * The target package of the overlay is not installed. The overlay cannot be enabled.
+     *
+     * @hide
      */
     public static final int STATE_MISSING_TARGET = 0;
 
     /**
      * Creation of idmap file failed (e.g. no matching resources). The overlay
      * cannot be enabled.
+     *
+     * @hide
      */
     public static final int STATE_NO_IDMAP = 1;
 
@@ -70,6 +78,7 @@
      * The overlay is currently disabled. It can be enabled.
      *
      * @see IOverlayManager#setEnabled
+     * @hide
      */
     public static final int STATE_DISABLED = 2;
 
@@ -77,18 +86,21 @@
      * The overlay is currently enabled. It can be disabled.
      *
      * @see IOverlayManager#setEnabled
+     * @hide
      */
     public static final int STATE_ENABLED = 3;
 
     /**
      * The target package is currently being upgraded; the state will change
      * once the package installation has finished.
+     * @hide
      */
     public static final int STATE_TARGET_UPGRADING = 4;
 
     /**
      * The overlay package is currently being upgraded; the state will change
      * once the package installation has finished.
+     * @hide
      */
     public static final int STATE_OVERLAY_UPGRADING = 5;
 
@@ -96,6 +108,7 @@
      * The overlay package is currently enabled because it is marked as
      * 'static'. It cannot be disabled but will change state if for instance
      * its target is uninstalled.
+     * @hide
      */
     public static final int STATE_ENABLED_STATIC = 6;
 
@@ -103,40 +116,52 @@
      * Overlay category: theme.
      * <p>
      * Change how Android (including the status bar, dialogs, ...) looks.
+     *
+     * @hide
      */
     public static final String CATEGORY_THEME = "android.theme";
 
     /**
      * Package name of the overlay package
+     *
+     * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public final String packageName;
 
     /**
      * Package name of the target package
+     *
+     * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @SystemApi
     public final String targetPackageName;
 
     /**
      * Category of the overlay package
+     *
+     * @hide
      */
+    @SystemApi
     public final String category;
 
     /**
      * Full path to the base APK for this overlay package
+     * @hide
      */
     public final String baseCodePath;
 
     /**
      * The state of this OverlayInfo as defined by the STATE_* constants in this class.
+     * @hide
      */
-    @UnsupportedAppUsage
     public final @State int state;
 
     /**
      * User handle for which this overlay applies
+     * @hide
      */
+    @SystemApi
     public final int userId;
 
     /**
@@ -161,12 +186,15 @@
      *
      * @param source the source OverlayInfo to base the new instance on
      * @param state the new state for the source OverlayInfo
+     *
+     * @hide
      */
     public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
         this(source.packageName, source.targetPackageName, source.category, source.baseCodePath,
                 state, source.userId, source.priority, source.isStatic);
     }
 
+    /** @hide */
     public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
             @NonNull String category, @NonNull String baseCodePath, int state, int userId,
             int priority, boolean isStatic) {
@@ -181,6 +209,7 @@
         ensureValidState();
     }
 
+    /** @hide */
     public OverlayInfo(Parcel source) {
         packageName = source.readString();
         targetPackageName = source.readString();
@@ -255,8 +284,9 @@
      * Disabled overlay packages are installed but are currently not in use.
      *
      * @return true if the overlay is enabled, else false.
+     * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public boolean isEnabled() {
         switch (state) {
             case STATE_ENABLED:
@@ -272,6 +302,7 @@
      * debugging purposes.
      *
      * @return a human readable String representing the state.
+     * @hide
      */
     public static String stateToString(@State int state) {
         switch (state) {
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
new file mode 100644
index 0000000..7a2220bf
--- /dev/null
+++ b/core/java/android/content/om/OverlayManager.java
@@ -0,0 +1,98 @@
+/*
+ * 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.om;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import java.util.List;
+
+/**
+ * Updates OverlayManager state; gets information about installed overlay packages.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.OVERLAY_SERVICE)
+public class OverlayManager {
+
+    private final IOverlayManager mService;
+    private final Context mContext;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param context The current context in which to operate.
+     * @param service The backing system service.
+     *
+     * @hide
+     */
+    public OverlayManager(Context context, IOverlayManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /** @hide */
+    public OverlayManager(Context context) {
+        this(context, IOverlayManager.Stub.asInterface(
+            ServiceManager.getService(Context.OVERLAY_SERVICE)));
+    }
+    /**
+     * Request that an overlay package is enabled and any other overlay packages with the same
+     * target package and category are disabled.
+     *
+     * @param packageName the name of the overlay package to enable.
+     * @param userId The user for which to change the overlay.
+     * @return true if the system successfully registered the request, false otherwise.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean setEnabledExclusiveInCategory(@Nullable final String packageName,
+            int userId) {
+        try {
+            return mService.setEnabledExclusiveInCategory(packageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns information about all overlays for the given target package for
+     * the specified user. The returned list is ordered according to the
+     * overlay priority with the highest priority at the end of the list.
+     *
+     * @param targetPackageName The name of the target package.
+     * @param userId The user to get the OverlayInfos for.
+     * @return A list of OverlayInfo objects; if no overlays exist for the
+     *         requested package, an empty list is returned.
+     *
+     * @hide
+     */
+    @SystemApi
+    public List<OverlayInfo> getOverlayInfosForTarget(@Nullable final String targetPackageName,
+            int userId) {
+        try {
+            return mService.getOverlayInfosForTarget(targetPackageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0edb36c..3cfbe0c 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -505,6 +505,22 @@
      */
     public int flags;
 
+    /**
+     * Bit in {@link #privateFlags} indicating if the activity should be shown when locked in case
+     * an activity behind this can also be shown when locked.
+     * See android.R.attr#inheritShowWhenLocked
+     * @hide
+     */
+    public static final int FLAG_INHERIT_SHOW_WHEN_LOCKED = 0x1;
+
+    /**
+     * Options that have been set in the activity declaration in the manifest.
+     * These include:
+     * {@link #FLAG_INHERIT_SHOW_WHEN_LOCKED}.
+     * @hide
+     */
+    public int privateFlags;
+
     /** @hide */
     @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
             SCREEN_ORIENTATION_UNSET,
@@ -975,6 +991,7 @@
         taskAffinity = orig.taskAffinity;
         targetActivity = orig.targetActivity;
         flags = orig.flags;
+        privateFlags = orig.privateFlags;
         screenOrientation = orig.screenOrientation;
         configChanges = orig.configChanges;
         softInputMode = orig.softInputMode;
@@ -1122,9 +1139,10 @@
                     + " targetActivity=" + targetActivity
                     + " persistableMode=" + persistableModeToString());
         }
-        if (launchMode != 0 || flags != 0 || theme != 0) {
+        if (launchMode != 0 || flags != 0 || privateFlags != 0 || theme != 0) {
             pw.println(prefix + "launchMode=" + launchMode
                     + " flags=0x" + Integer.toHexString(flags)
+                    + " privateFlags=0x" + Integer.toHexString(privateFlags)
                     + " theme=0x" + Integer.toHexString(theme));
         }
         if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED
@@ -1178,6 +1196,7 @@
         dest.writeString(targetActivity);
         dest.writeString(launchToken);
         dest.writeInt(flags);
+        dest.writeInt(privateFlags);
         dest.writeInt(screenOrientation);
         dest.writeInt(configChanges);
         dest.writeInt(softInputMode);
@@ -1307,6 +1326,7 @@
         targetActivity = source.readString();
         launchToken = source.readString();
         flags = source.readInt();
+        privateFlags = source.readInt();
         screenOrientation = source.readInt();
         configChanges = source.readInt();
         softInputMode = source.readInt();
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2978058..576466f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1866,7 +1866,15 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) != 0;
     }
 
-    /** @hide */
+    /**
+     * Check whether the application is encryption aware.
+     *
+     * @see #isDirectBootAware()
+     * @see #isPartiallyDirectBootAware()
+     *
+     * @hide
+     */
+    @SystemApi
     public boolean isEncryptionAware() {
         return isDirectBootAware() || isPartiallyDirectBootAware();
     }
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 740fd7f..b7366f1 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
@@ -76,9 +77,23 @@
     }
 
     /**
-     * Starts the specified activity of the caller package in the specified profile if the caller
-     * has {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} permission and
-     * both the caller and target user profiles are in the same user group.
+     * @deprecated use {@link #startActivity(ComponentName, UserHandle)} instead.
+     *
+     * @removed
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+        startActivity(component, targetUser);
+    }
+
+    /**
+     * Starts the specified activity of the caller package in the specified profile. Unlike
+     * {@link #startMainActivity}, this can start any activity of the caller package, not just
+     * the main activity.
+     * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
+     * permission and both the caller and target user profiles must be in the same profile group.
      *
      * @param component The ComponentName of the activity to launch. It must be exported.
      * @param targetUser The UserHandle of the profile, must be one of the users returned by
@@ -88,7 +103,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
-    public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+    public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
                     mContext.getPackageName(), component, targetUser.getIdentifier(), false);
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index ba7710b..db2b6fd 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -69,6 +69,7 @@
             int userId);
 
     boolean hasShortcutHostPermission(String callingPackage);
+    boolean shouldHideFromSuggestions(String packageName, in UserHandle user);
 
     ParceledListSlice getShortcutConfigActivities(
             String callingPackage, String packageName, in UserHandle user);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 64a4479b..d5c3b26 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -273,6 +273,9 @@
 
     void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
 
+    String[] setDistractingPackageRestrictionsAsUser(in String[] packageNames, int restrictionFlags,
+            int userId);
+
     String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
             in PersistableBundle appExtras, in PersistableBundle launcherExtras,
             in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 03124be..c702b16 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -16,6 +16,7 @@
 package android.content.pm;
 
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
@@ -72,4 +73,7 @@
     void applyRestore(in byte[] payload, int user);
 
     boolean isRequestPinItemSupported(int user, int requestType);
+
+    // System API used by framework's ShareSheet (ChooserActivity)
+    ParceledListSlice getShareTargets(String packageName, in IntentFilter filter, int userId);
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 44e652f..983ea9f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -697,6 +697,26 @@
     }
 
     /**
+     * Returns whether a package should be hidden from suggestions to the user. Currently, this
+     * could be done because the package was marked as distracting to the user via
+     * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
+     *
+     * @param packageName The package for which to check.
+     * @param user the {@link UserHandle} of the profile.
+     * @return
+     */
+    public boolean shouldHideFromSuggestions(@NonNull String packageName,
+            @NonNull UserHandle user) {
+        Preconditions.checkNotNull(packageName, "packageName");
+        Preconditions.checkNotNull(user, "user");
+        try {
+            return mService.shouldHideFromSuggestions(packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
      *
      * @param packageName The package name of the application
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2aeb68d..b845673 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5555,26 +5555,24 @@
     }
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void addPackageToPreferred(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void removePackageFromPreferred(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Retrieve the list of all currently configured preferred packages. The
      * first package on the list is the most preferred, the last is the least
      * preferred.
@@ -5582,15 +5580,16 @@
      * @param flags Additional option flags to modify the data returned.
      * @return A List of PackageInfo objects, one for each preferred
      *         application, in order of preference.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Add a new preferred activity mapping to the system.  This will be used
      * to automatically select the given activity component when
      * {@link Context#startActivity(Intent) Context.startActivity()} finds
@@ -5604,20 +5603,26 @@
      * this preference was made.
      * @param activity The component name of the activity that is to be
      * preferred.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void addPreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Same as {@link #addPreferredActivity(IntentFilter, int,
             ComponentName[], ComponentName)}, but with a specific userId to apply the preference
             to.
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5627,10 +5632,6 @@
     }
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used
      * to automatically select the given activity component when
@@ -5645,7 +5646,13 @@
      * this preference was made.
      * @param activity The component name of the activity that is to be
      * preferred.
+     *
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5653,10 +5660,6 @@
             ComponentName[] set, ComponentName activity);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used to automatically select the given activity
      * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
@@ -5671,6 +5674,11 @@
      * @param activity The component name of the activity that is to be preferred.
      *
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @SystemApi
@@ -5681,6 +5689,11 @@
 
     /**
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5690,10 +5703,6 @@
     }
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Remove all preferred activity mappings, previously added with
      * {@link #addPreferredActivity}, from the
      * system whose activities are implemented in the given package name.
@@ -5701,15 +5710,16 @@
      *
      * @param packageName The name of the package whose preferred activity
      * mappings are to be removed.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void clearPackagePreferredActivities(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Retrieve all preferred activities, previously added with
      * {@link #addPreferredActivity}, that are
      * currently registered with the system.
@@ -5725,6 +5735,11 @@
      * @return Returns the total number of registered preferred activities
      * (the number of distinct IntentFilter records, not the number of unique
      * activity components) that were found.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
@@ -5889,6 +5904,74 @@
     public abstract boolean isSignedByExactly(String packageName, KeySet ks);
 
     /**
+     * Flag to denote no restrictions. This should be used to clear any restrictions that may have
+     * been previously set for the package.
+     * @see PackageManager.DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    public static final int RESTRICTION_NONE = 0x0;
+
+    /**
+     * Flag to denote that a package should be hidden from any suggestions to the user.
+     * @see PackageManager.DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001;
+
+    /**
+     * Flag to denote that a package's notifications should be hidden.
+     * @see PackageManager.DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002;
+
+    /**
+     * Restriction flags to set on a package that is considered as distracting to the user.
+     * These should help the user to restrict their usage of these apps.
+     *
+     * @see #setDistractingPackageRestrictions(String[], int)
+     * @hide
+     */
+    @SystemApi
+    @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
+            RESTRICTION_NONE,
+            RESTRICTION_HIDE_FROM_SUGGESTIONS,
+            RESTRICTION_HIDE_NOTIFICATIONS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DistractionRestriction {}
+
+    /**
+     * Mark or unmark the given packages as distracting to the user.
+     * These packages can have certain restrictions set that should discourage the user to launch
+     * them often. For example, notifications from such an app can be hidden, or the app can be
+     * removed from launcher suggestions, so the user is able to restrict their use of these apps.
+     *
+     * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API.
+     *
+     * @param packages Packages to mark as distracting.
+     * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to
+     *                         impose on the given packages. {@link #RESTRICTION_NONE} can be used
+     *                         to clear any existing restrictions.
+     * @return A list of packages that could not have the {@code restrictionFlags} set. The system
+     * may prevent restricting critical packages to preserve normal device function.
+     *
+     * @see DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SUSPEND_APPS)
+    @NonNull
+    public String[] setDistractingPackageRestrictions(@NonNull String[] packages,
+            @DistractionRestriction int restrictionFlags) {
+        throw new UnsupportedOperationException(
+                "setDistractingPackageRestrictions not implemented");
+    }
+
+    /**
      * Puts the package in a suspended state, where attempts at starting activities are denied.
      *
      * <p>It doesn't remove the data or the actual package file. The application's notifications
@@ -5911,8 +5994,7 @@
      * {@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} or
-     * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
+     * <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
@@ -5955,7 +6037,7 @@
      * <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
+     * 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.
      *
@@ -5966,7 +6048,7 @@
      * {@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.
+     * <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
@@ -6005,7 +6087,7 @@
      * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
      * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
      * packages to keep the device in a functioning state, e.g. the default dialer.
-     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api.
+     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
      *
      * <p>
      * Note that this set of critical packages can change with time, so even though a package name
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 83979e9..359adbd 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -55,6 +55,7 @@
     public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
     public static final int PACKAGE_WELLBEING = 7;
     public static final int PACKAGE_DOCUMENTER = 8;
+    public static final int PACKAGE_CONFIGURATOR = 9;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -65,6 +66,7 @@
         PACKAGE_PERMISSION_CONTROLLER,
         PACKAGE_WELLBEING,
         PACKAGE_DOCUMENTER,
+        PACKAGE_CONFIGURATOR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
@@ -137,6 +139,12 @@
     public abstract void setLocationPackagesProvider(PackagesProvider provider);
 
     /**
+     * Set the location extra packages provider.
+     * @param provider The packages provider.
+     */
+    public abstract  void setLocationExtraPackagesProvider(PackagesProvider provider);
+
+    /**
      * Sets the voice interaction packages provider.
      * @param provider The packages provider.
      */
@@ -284,6 +292,17 @@
     public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
 
     /**
+     * Gets any distraction flags set via
+     * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+     *
+     * @param packageName
+     * @param userId
+     * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction}
+     */
+    public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions(
+            String packageName, int userId);
+
+    /**
      * Do a straight uid lookup for the given package/application in the given user.
      * @see PackageManager#getPackageUidAsUser(String, int, int)
      * @return The app's uid, or < 0 if the package was not found in that user
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2b266b7..dbf3574 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1621,7 +1621,7 @@
             }
 
             final AttributeSet attrs = parser;
-            return parseApkLite(apkPath, parser, attrs, signingDetails);
+            return parseApkLite(apkPath, parser, attrs, signingDetails, flags);
 
         } catch (XmlPullParserException | IOException | RuntimeException e) {
             Slog.w(TAG, "Failed to parse " + apkPath, e);
@@ -1708,7 +1708,7 @@
     }
 
     private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
-            SigningDetails signingDetails)
+            SigningDetails signingDetails, int flags)
             throws IOException, XmlPullParserException, PackageParserException {
         final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
 
@@ -1716,11 +1716,12 @@
         int versionCode = 0;
         int versionCodeMajor = 0;
         int revisionCode = 0;
+        int targetSdkVersion = 0;
         boolean coreApp = false;
         boolean debuggable = false;
         boolean multiArch = false;
         boolean use32bitAbi = false;
-        boolean extractNativeLibs = true;
+        Boolean extractNativeLibsProvided = null;
         boolean isolatedSplits = false;
         boolean isFeatureSplit = false;
         boolean isSplitRequired = false;
@@ -1785,7 +1786,8 @@
                         use32bitAbi = attrs.getAttributeBooleanValue(i, false);
                     }
                     if ("extractNativeLibs".equals(attr)) {
-                        extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
+                        extractNativeLibsProvided = Boolean.valueOf(
+                                attrs.getAttributeBooleanValue(i, true));
                     }
                     if ("preferCodeIntegrity".equals(attr)) {
                         preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false);
@@ -1803,9 +1805,51 @@
                             PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                             "<uses-split> tag requires 'android:name' attribute");
                 }
+            } else if (TAG_USES_SDK.equals(parser.getName())) {
+                final String[] errorMsg = new String[1];
+                Pair<Integer, Integer> versions = deriveSdkVersions(new AbstractVersionsAccessor() {
+                    @Override public String getMinSdkVersionCode() {
+                        return getAttributeAsString("minSdkVersion");
+                    }
+
+                    @Override public int getMinSdkVersion() {
+                        return getAttributeAsInt("minSdkVersion");
+                    }
+
+                    @Override public String getTargetSdkVersionCode() {
+                        return getAttributeAsString("targetSdkVersion");
+                    }
+
+                    @Override public int getTargetSdkVersion() {
+                        return getAttributeAsInt("targetSdkVersion");
+                    }
+
+                    private String getAttributeAsString(String name) {
+                        return attrs.getAttributeValue(ANDROID_RESOURCES, name);
+                    }
+
+                    private int getAttributeAsInt(String name) {
+                        try {
+                            return attrs.getAttributeIntValue(ANDROID_RESOURCES, name, -1);
+                        } catch (NumberFormatException e) {
+                            return -1;
+                        }
+                    }
+                }, flags, errorMsg);
+
+                if (versions == null) {
+                    throw new PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, errorMsg[0]);
+                }
+
+                targetSdkVersion = versions.second;
             }
         }
 
+        final boolean extractNativeLibsDefault = targetSdkVersion < Build.VERSION_CODES.Q;
+        final boolean extractNativeLibs = (extractNativeLibsProvided != null)
+                ? extractNativeLibsProvided : extractNativeLibsDefault;
+
         if (preferCodeIntegrity && extractNativeLibs) {
             throw new PackageParserException(
                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -2215,65 +2259,60 @@
 
             } else if (tagName.equals(TAG_USES_SDK)) {
                 if (SDK_VERSION > 0) {
-                    sa = res.obtainAttributes(parser,
-                            com.android.internal.R.styleable.AndroidManifestUsesSdk);
+                    sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
+                    final TypedArray saFinal = sa;
+                    Pair<Integer, Integer> versions = deriveSdkVersions(
+                            new AbstractVersionsAccessor() {
+                                @Override public String getMinSdkVersionCode() {
+                                    return getAttributeAsString(
+                                            R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+                                }
 
-                    int minVers = 1;
-                    String minCode = null;
-                    int targetVers = 0;
-                    String targetCode = null;
+                                @Override public int getMinSdkVersion() {
+                                    return getAttributeAsInt(
+                                            R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+                                }
 
-                    TypedValue val = sa.peekValue(
-                            com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
-                    if (val != null) {
-                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
-                            minCode = val.string.toString();
-                        } else {
-                            // If it's not a string, it's an integer.
-                            minVers = val.data;
-                        }
+                                @Override public String getTargetSdkVersionCode() {
+                                    return getAttributeAsString(
+                                            R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+                                }
+
+                                @Override public int getTargetSdkVersion() {
+                                    return getAttributeAsInt(
+                                            R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+                                }
+
+                                private String getAttributeAsString(int index) {
+                                    TypedValue val = saFinal.peekValue(index);
+                                    if (val != null && val.type == TypedValue.TYPE_STRING
+                                            && val.string != null) {
+                                        return val.string.toString();
+                                    }
+                                    return null;
+                                }
+
+                                private int getAttributeAsInt(int index) {
+                                    TypedValue val = saFinal.peekValue(index);
+                                    if (val != null && val.type != TypedValue.TYPE_STRING) {
+                                        // If it's not a string, it's an integer.
+                                        return val.data;
+                                    }
+                                    return -1;
+                                }
+                            }, flags, outError);
+
+                    if (versions == null) {
+                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+                        return null;
                     }
 
-                    val = sa.peekValue(
-                            com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
-                    if (val != null) {
-                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
-                            targetCode = val.string.toString();
-                            if (minCode == null) {
-                                minCode = targetCode;
-                            }
-                        } else {
-                            // If it's not a string, it's an integer.
-                            targetVers = val.data;
-                        }
-                    } else {
-                        targetVers = minVers;
-                        targetCode = minCode;
-                    }
+                    pkg.applicationInfo.minSdkVersion = versions.first;
+                    pkg.applicationInfo.targetSdkVersion = versions.second;
 
                     sa.recycle();
-
-                    final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode,
-                            SDK_VERSION, SDK_CODENAMES, outError);
-                    if (minSdkVersion < 0) {
-                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
-                        return null;
-                    }
-
-                    boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0;
-                    final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,
-                            targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch);
-                    if (targetSdkVersion < 0) {
-                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
-                        return null;
-                    }
-
-                    pkg.applicationInfo.minSdkVersion = minSdkVersion;
-                    pkg.applicationInfo.targetSdkVersion = targetSdkVersion;
                 }
-
                 XmlUtils.skipCurrentTag(parser);
-
             } else if (tagName.equals(TAG_SUPPORT_SCREENS)) {
                 sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestSupportsScreens);
@@ -2617,6 +2656,23 @@
     }
 
     /**
+     * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
+     * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
+     * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
+     */
+    private static boolean matchTargetCode(@NonNull String[] codeNames,
+            @NonNull String targetCode) {
+        final String targetCodeName;
+        final int targetCodeIdx = targetCode.indexOf('.');
+        if (targetCodeIdx == -1) {
+            targetCodeName = targetCode;
+        } else {
+            targetCodeName = targetCode.substring(0, targetCodeIdx);
+        }
+        return ArrayUtils.contains(codeNames, targetCodeName);
+    }
+
+    /**
      * Computes the targetSdkVersion to use at runtime. If the package is not
      * compatible with this platform, populates {@code outError[0]} with an
      * error message.
@@ -2659,7 +2715,7 @@
 
         // If it's a pre-release SDK and the codename matches this platform, it
         // definitely targets this SDK.
-        if (ArrayUtils.contains(platformSdkCodenames, targetCode) || forceCurrentDev) {
+        if (matchTargetCode(platformSdkCodenames, targetCode) || forceCurrentDev) {
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
@@ -2675,6 +2731,67 @@
         return -1;
     }
 
+    private interface AbstractVersionsAccessor {
+        /** Returns minimum SDK version code string, or null if absent. */
+        String getMinSdkVersionCode();
+
+        /** Returns minimum SDK version code, or -1 if absent. */
+        int getMinSdkVersion();
+
+        /** Returns target SDK version code string, or null if absent. */
+        String getTargetSdkVersionCode();
+
+        /** Returns target SDK version code, or -1 if absent. */
+        int getTargetSdkVersion();
+    }
+
+    private static @Nullable Pair<Integer, Integer> deriveSdkVersions(
+            @NonNull AbstractVersionsAccessor accessor, int flags, String[] outError) {
+        int minVers = 1;
+        String minCode = null;
+        int targetVers = 0;
+        String targetCode = null;
+
+        String code = accessor.getMinSdkVersionCode();
+        int version = accessor.getMinSdkVersion();
+        // Check integer first since code is almost never a null string (e.g. "28").
+        if (version >= 0) {
+            minVers = version;
+        } else if (code != null) {
+            minCode = code;
+        }
+
+        code = accessor.getTargetSdkVersionCode();
+        version = accessor.getTargetSdkVersion();
+        // Check integer first since code is almost never a null string (e.g. "28").
+        if (version >= 0) {
+            targetVers = version;
+        } else if (code != null) {
+            targetCode = code;
+            if (minCode == null) {
+                minCode = targetCode;
+            }
+        } else {
+            targetVers = minVers;
+            targetCode = minCode;
+        }
+
+        final int minSdkVersion = computeMinSdkVersion(minVers, minCode,
+                SDK_VERSION, SDK_CODENAMES, outError);
+        if (minSdkVersion < 0) {
+            return null;
+        }
+
+        boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0;
+        final int targetSdkVersion = computeTargetSdkVersion(targetVers,
+                targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch);
+        if (targetSdkVersion < 0) {
+            return null;
+        }
+
+        return Pair.create(minSdkVersion, targetSdkVersion);
+    }
+
     /**
      * Computes the minSdkVersion to use at runtime. If the package is not
      * compatible with this platform, populates {@code outError[0]} with an
@@ -2731,7 +2848,7 @@
 
         // If it's a pre-release SDK and the codename matches this platform, we
         // definitely meet the minimum SDK requirement.
-        if (ArrayUtils.contains(platformSdkCodenames, minCode)) {
+        if (matchTargetCode(platformSdkCodenames, minCode)) {
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
@@ -3660,9 +3777,11 @@
             ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
         }
 
+        final boolean extractNativeLibsDefault =
+                owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q;
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
-                true)) {
+                extractNativeLibsDefault)) {
             ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
         }
 
@@ -4546,6 +4665,9 @@
                 a.info.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON;
             }
 
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_inheritShowWhenLocked, false)) {
+                a.info.privateFlags |= ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
+            }
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
@@ -4925,6 +5047,7 @@
         info.targetActivity = targetActivity;
         info.configChanges = target.info.configChanges;
         info.flags = target.info.flags;
+        info.privateFlags = target.info.privateFlags;
         info.icon = target.info.icon;
         info.logo = target.info.logo;
         info.banner = target.info.banner;
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index be6ed51..249b691 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -29,8 +29,11 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.os.BaseBundle;
+import android.os.Debug;
 import android.os.PersistableBundle;
 import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -43,11 +46,15 @@
  * @hide
  */
 public class PackageUserState {
+    private static final boolean DEBUG = false;
+    private static final String LOG_TAG = "PackageUserState";
+
     public long ceDataInode;
     public boolean installed;
     public boolean stopped;
     public boolean notLaunched;
     public boolean hidden; // Is the app restricted by owner / admin
+    public int distractionFlags;
     public boolean suspended;
     public String suspendingPackage;
     public SuspendDialogInfo dialogInfo;
@@ -86,6 +93,7 @@
         stopped = o.stopped;
         notLaunched = o.notLaunched;
         hidden = o.hidden;
+        distractionFlags = o.distractionFlags;
         suspended = o.suspended;
         suspendingPackage = o.suspendingPackage;
         dialogInfo = o.dialogInfo;
@@ -132,12 +140,12 @@
         final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();
         final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
         if (!isAvailable(flags)
-                && !(isSystemApp && matchUninstalled)) return false;
-        if (!isEnabled(componentInfo, flags)) return false;
+                && !(isSystemApp && matchUninstalled)) return reportIfDebug(false, flags);
+        if (!isEnabled(componentInfo, flags)) return reportIfDebug(false, flags);
 
         if ((flags & MATCH_SYSTEM_ONLY) != 0) {
             if (!isSystemApp) {
-                return false;
+                return reportIfDebug(false, flags);
             }
         }
 
@@ -145,7 +153,16 @@
                 && !componentInfo.directBootAware;
         final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
                 && componentInfo.directBootAware;
-        return matchesUnaware || matchesAware;
+        return reportIfDebug(matchesUnaware || matchesAware, flags);
+    }
+
+    private boolean reportIfDebug(boolean result, int flags) {
+        if (DEBUG && !result) {
+            Slog.i(LOG_TAG, "No match!; flags: "
+                    + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
+                    + Debug.getCaller());
+        }
+        return result;
     }
 
     /**
@@ -207,6 +224,9 @@
         if (hidden != oldState.hidden) {
             return false;
         }
+        if (distractionFlags != oldState.distractionFlags) {
+            return false;
+        }
         if (suspended != oldState.suspended) {
             return false;
         }
diff --git a/core/java/android/content/pm/PermissionGroupInfo.java b/core/java/android/content/pm/PermissionGroupInfo.java
index 8cf66d8..f21612a 100644
--- a/core/java/android/content/pm/PermissionGroupInfo.java
+++ b/core/java/android/content/pm/PermissionGroupInfo.java
@@ -49,7 +49,7 @@
      * only access while in the foreground.
      *
      * From the "requestDetail" attribute or, if not set, {@link
-     * android.content.res.ResourceId#ID_NULL}.
+     * android.content.res.Resources#ID_NULL}.
      *
      * @hide
      */
@@ -61,7 +61,7 @@
      * access. Also used when requesting both foreground and background access.
      *
      * From the "backgroundRequest" attribute or, if not set, {@link
-     * android.content.res.ResourceId#ID_NULL}.
+     * android.content.res.Resources#ID_NULL}.
      *
      * @hide
      */
@@ -73,7 +73,7 @@
      * background access.
      *
      * From the "backgroundRequestDetail" attribute or, if not set, {@link
-     * android.content.res.ResourceId#ID_NULL}.
+     * android.content.res.Resources#ID_NULL}.
      *
      * @hide
      */
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index bb8c92d..5d2cf0a 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -158,6 +158,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int PROTECTION_FLAG_OEM = 0x4000;
 
     /**
@@ -202,6 +203,16 @@
     @TestApi
     public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000;
 
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding to the
+     * {@code configurator} value of {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_CONFIGURATOR = 0x80000;
+
 
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
@@ -221,6 +232,7 @@
             PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
             PROTECTION_FLAG_WELLBEING,
             PROTECTION_FLAG_DOCUMENTER,
+            PROTECTION_FLAG_CONFIGURATOR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -416,6 +428,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
             protLevel += "|documenter";
         }
+        if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
+            protLevel += "|configurator";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index a8c3b88..6e519c1 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -475,6 +475,14 @@
         final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
         for (ResolveInfo resolveInfo : resolveInfos) {
             try {
+                // if this package is not one of those changedUids, we don't need to scan it,
+                // since nothing in it changed, so save a call to parseServiceInfo, which
+                // can cause a large amount of the package apk to be loaded into memory.
+                // if this is the initial scan, changedUids will be null, and containsUid will
+                // trivially return true, and will call parseServiceInfo
+                if (!containsUid(changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) {
+                    continue;
+                }
                 ServiceInfo<V> info = parseServiceInfo(resolveInfo);
                 if (info == null) {
                     Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index ec2e2fd..fe68b8a 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -21,6 +21,7 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.UserIdInt;
+import android.app.Person;
 import android.app.TaskStackBuilder;
 import android.content.ComponentName;
 import android.content.Context;
@@ -111,6 +112,9 @@
     public static final int FLAG_SHADOW = 1 << 12;
 
     /** @hide */
+    public static final int FLAG_LONG_LIVED = 1 << 13;
+
+    /** @hide */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_DYNAMIC,
             FLAG_PINNED,
@@ -124,6 +128,8 @@
             FLAG_ADAPTIVE_BITMAP,
             FLAG_RETURNED_BY_SERVICE,
             FLAG_ICON_FILE_PENDING_SAVE,
+            FLAG_SHADOW,
+            FLAG_LONG_LIVED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ShortcutFlags {}
@@ -344,6 +350,9 @@
     @Nullable
     private PersistableBundle[] mIntentPersistableExtrases;
 
+    @Nullable
+    private Person[] mPersons;
+
     private int mRank;
 
     /**
@@ -399,6 +408,10 @@
         mCategories = cloneCategories(b.mCategories);
         mIntents = cloneIntents(b.mIntents);
         fixUpIntentExtras();
+        mPersons = clonePersons(b.mPersons);
+        if (b.mIsLongLived) {
+            setLongLived();
+        }
         mRank = b.mRank;
         mExtras = b.mExtras;
         updateTimestamp();
@@ -465,6 +478,20 @@
         return ret;
     }
 
+    private static Person[] clonePersons(Person[] persons) {
+        if (persons == null) {
+            return null;
+        }
+        final Person[] ret = new Person[persons.length];
+        for (int i = 0; i < ret.length; i++) {
+            if (persons[i] != null) {
+                // Don't need to keep the icon, remove it to save space
+                ret[i] = persons[i].toBuilder().setIcon(null).build();
+            }
+        }
+        return ret;
+    }
+
     /**
      * Throws if any of the mandatory fields is not set.
      *
@@ -511,6 +538,7 @@
             mDisabledMessage = source.mDisabledMessage;
             mDisabledMessageResId = source.mDisabledMessageResId;
             mCategories = cloneCategories(source.mCategories);
+            mPersons = clonePersons(source.mPersons);
             if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
                 mIntents = cloneIntents(source.mIntents);
                 mIntentPersistableExtrases =
@@ -833,6 +861,9 @@
         if (source.mCategories != null) {
             mCategories = cloneCategories(source.mCategories);
         }
+        if (source.mPersons != null) {
+            mPersons = clonePersons(source.mPersons);
+        }
         if (source.mIntents != null) {
             mIntents = cloneIntents(source.mIntents);
             mIntentPersistableExtrases =
@@ -901,6 +932,10 @@
 
         private Intent[] mIntents;
 
+        private Person[] mPersons;
+
+        private boolean mIsLongLived;
+
         private int mRank = RANK_NOT_SET;
 
         private PersistableBundle mExtras;
@@ -1165,6 +1200,53 @@
         }
 
         /**
+         * Add a person that is relevant to this shortcut. Alternatively,
+         * {@link #setPersons(Person[])} can be used to add multiple persons to a shortcut.
+         *
+         * <p> This is an optional field, but the addition of person may cause this shortcut to
+         * appear more prominently in the user interface (e.g. ShareSheet).
+         *
+         * <p> A person should usually contain a uri in order to benefit from the ranking boost.
+         * However, even if no uri is provided, it's beneficial to provide people in the shortcut,
+         * such that listeners and voice only devices can announce and handle them properly.
+         *
+         * @see Person
+         * @see #setPersons(Person[])
+         */
+        @NonNull
+        public Builder setPerson(@NonNull Person person) {
+            return setPersons(new Person[]{person});
+        }
+
+        /**
+         * Sets multiple persons instead of a single person.
+         *
+         * @see Person
+         * @see #setPerson(Person)
+         */
+        @NonNull
+        public Builder setPersons(@NonNull Person[] persons) {
+            Preconditions.checkNotNull(persons, "persons cannot be null");
+            Preconditions.checkNotNull(persons.length, "persons cannot be empty");
+            for (Person person : persons) {
+                Preconditions.checkNotNull(person, "persons cannot contain null");
+            }
+            mPersons = clonePersons(persons);
+            return this;
+        }
+
+        /**
+         * Sets if a shortcut would be valid even if it has been unpublished/invisible by the app
+         * (as a dynamic or pinned shortcut). If it is long lived, it can be cached by various
+         * system services even after it has been unpublished as a dynamic shortcut.
+         */
+        @NonNull
+        public Builder setLongLived() {
+            mIsLongLived = true;
+            return this;
+        }
+
+        /**
          * "Rank" of a shortcut, which is a non-negative value that's used by the launcher app
          * to sort shortcuts.
          *
@@ -1395,6 +1477,16 @@
     }
 
     /**
+     * Return the Persons set with {@link Builder#setPersons(Person[])}.
+     *
+     * @hide
+     */
+    @Nullable
+    public Person[] getPersons() {
+        return clonePersons(mPersons);
+    }
+
+    /**
      * The extras in the intents.  We convert extras into {@link PersistableBundle} so we can
      * persist them.
      * @hide
@@ -1525,6 +1617,16 @@
         addFlags(FLAG_RETURNED_BY_SERVICE);
     }
 
+    /** @hide */
+    public boolean isLongLived() {
+        return hasFlags(FLAG_LONG_LIVED);
+    }
+
+    /** @hide */
+    public void setLongLived() {
+        addFlags(FLAG_LONG_LIVED);
+    }
+
     /** Return whether a shortcut is dynamic. */
     public boolean isDynamic() {
         return hasFlags(FLAG_DYNAMIC);
@@ -1893,6 +1995,8 @@
                 mCategories.add(source.readString().intern());
             }
         }
+
+        mPersons = source.readParcelableArray(cl, Person.class);
     }
 
     @Override
@@ -1940,6 +2044,8 @@
         } else {
             dest.writeInt(0);
         }
+
+        dest.writeParcelableArray(mPersons, flags);
     }
 
     public static final Creator<ShortcutInfo> CREATOR =
@@ -2040,6 +2146,9 @@
         if (isReturnedByServer()) {
             sb.append("Rets");
         }
+        if (isLongLived()) {
+            sb.append("Liv");
+        }
         sb.append("]");
 
         addIndentOrComma(sb, indent);
@@ -2094,6 +2203,11 @@
 
         addIndentOrComma(sb, indent);
 
+        sb.append("persons=");
+        sb.append(mPersons);
+
+        addIndentOrComma(sb, indent);
+
         sb.append("icon=");
         sb.append(mIcon);
 
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 2d59003..4f7acd9 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -17,17 +17,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.UserIdInt;
 import android.app.usage.UsageStatsManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
@@ -549,4 +554,85 @@
     protected int injectMyUserId() {
         return mContext.getUserId();
     }
+
+    /**
+     * Used by framework's ShareSheet (ChooserActivity.java) to retrieve all of the direct share
+     * targets that match the given IntentFilter.
+     *
+     * @param filter IntentFilter that will be used to retrieve the matching {@link ShortcutInfo}s.
+     * @return List of {@link ShareShortcutInfo}s that match the given IntentFilter.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) {
+        try {
+            return mService.getShareTargets(mContext.getPackageName(), filter,
+                    injectMyUserId()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Represents the result of a query return by {@link #getShareTargets(IntentFilter)}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class ShareShortcutInfo implements Parcelable {
+        private final ShortcutInfo mShortcutInfo;
+        private final ComponentName mTargetComponent;
+
+        /**
+         * @hide
+         */
+        public ShareShortcutInfo(@NonNull ShortcutInfo shortcutInfo,
+                @NonNull ComponentName targetComponent) {
+            if (shortcutInfo == null) {
+                throw new NullPointerException("shortcut info is null");
+            }
+            if (targetComponent == null) {
+                throw new NullPointerException("target component is null");
+            }
+
+            mShortcutInfo = shortcutInfo;
+            mTargetComponent = targetComponent;
+        }
+
+        private ShareShortcutInfo(Parcel in) {
+            mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+            mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader());
+        }
+
+        public ShortcutInfo getShortcutInfo() {
+            return mShortcutInfo;
+        }
+
+        public ComponentName getTargetComponent() {
+            return mTargetComponent;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeParcelable(mShortcutInfo, flags);
+            dest.writeParcelable(mTargetComponent, flags);
+        }
+
+        public static final Parcelable.Creator<ShareShortcutInfo> CREATOR =
+                new Parcelable.Creator<ShareShortcutInfo>() {
+                    public ShareShortcutInfo createFromParcel(Parcel in) {
+                        return new ShareShortcutInfo(in);
+                    }
+
+                    public ShareShortcutInfo[] newArray(int size) {
+                        return new ShareShortcutInfo[size];
+                    }
+                };
+    }
 }
diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java
index c798c99..53b52f5 100644
--- a/core/java/android/content/pm/SuspendDialogInfo.java
+++ b/core/java/android/content/pm/SuspendDialogInfo.java
@@ -16,7 +16,7 @@
 
 package android.content.pm;
 
-import static android.content.res.ResourceId.ID_NULL;
+import static android.content.res.Resources.ID_NULL;
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index f1a4db2..9e0a9ba 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -732,6 +732,38 @@
         }
     }
 
+    /**
+     * Enable resource resolution logging to track the steps taken to resolve the last resource
+     * entry retrieved. Stores the configuration and package names for each step.
+     *
+     * Default disabled.
+     *
+     * @param enabled Boolean indicating whether to enable or disable logging.
+     *
+     * @hide
+     */
+    public void setResourceResolutionLoggingEnabled(boolean enabled) {
+        synchronized (this) {
+            ensureValidLocked();
+            nativeSetResourceResolutionLoggingEnabled(mObject, enabled);
+        }
+    }
+
+    /**
+     * Retrieve the last resource resolution path logged.
+     *
+     * @return Formatted string containing last resource ID/name and steps taken to resolve final
+     * entry, including configuration and package names.
+     *
+     * @hide
+     */
+    public @Nullable String getLastResourceResolution() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetLastResourceResolution(mObject);
+        }
+    }
+
     CharSequence getPooledStringForCookie(int cookie, int id) {
         // Cookies map to ApkAssets starting at 1.
         return getApkAssets()[cookie - 1].getStringFromPool(id);
@@ -1383,6 +1415,8 @@
     private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
     private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
     private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+    private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled);
+    private static native @Nullable String nativeGetLastResourceResolution(long ptr);
 
     // Style attribute retrieval native methods.
     private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 799f8e5..536a1b7 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -794,6 +794,7 @@
      * {@link ActivityInfo#CONFIG_ASSETS_PATHS}.
      * @hide
      */
+    @TestApi
     public int assetsSeq;
 
     /**
diff --git a/core/java/android/content/res/ResourceId.java b/core/java/android/content/res/ResourceId.java
index adb9cf1..3c7b5fc 100644
--- a/core/java/android/content/res/ResourceId.java
+++ b/core/java/android/content/res/ResourceId.java
@@ -22,12 +22,6 @@
  * @hide
  */
 public final class ResourceId {
-
-    /**
-     * The {@code null} resource ID.
-     */
-    public static final @AnyRes int ID_NULL = 0;
-
     /**
      * Checks whether the integer {@code id} is a valid resource ID, as generated by AAPT.
      * <p>Note that a negative integer is not necessarily an invalid resource ID, and custom
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 365ceac..baf64ad 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -98,6 +98,12 @@
  * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p>
  */
 public class Resources {
+    /**
+     * The {@code null} resource ID. This denotes an invalid resource ID that is returned by the
+     * system when a resource is not found or the value is set to {@code @null} in XML.
+     */
+    public static final @AnyRes int ID_NULL = 0;
+
     static final String TAG = "Resources";
 
     private static final Object sSync = new Object();
@@ -168,7 +174,7 @@
     /** @hide */
     public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
             int dark, int deviceDefault) {
-        if (curTheme != ResourceId.ID_NULL) {
+        if (curTheme != ID_NULL) {
             return curTheme;
         }
         if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
@@ -2010,22 +2016,36 @@
     public String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
         return mResourcesImpl.getResourceTypeName(resid);
     }
-    
+
     /**
      * Return the entry name for a given resource identifier.
-     * 
+     *
      * @param resid The resource identifier whose entry name is to be
      * retrieved.
-     * 
+     *
      * @return A string holding the entry name of the resource.
-     * 
+     *
      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
-     * 
+     *
      * @see #getResourceName
      */
     public String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
         return mResourcesImpl.getResourceEntryName(resid);
     }
+
+    /**
+     * Return formatted log of the last retrieved resource's resolution path.
+     *
+     * @return A string holding a formatted log of the steps taken to resolve the last resource.
+     *
+     * @throws NotFoundException Throws NotFoundException if there hasn't been a resource
+     * resolved yet.
+     *
+     * @hide
+     */
+    public String getLastResourceResolution() throws NotFoundException {
+        return mResourcesImpl.getLastResourceResolution();
+    }
     
     /**
      * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 2ad4f62..d8564d5 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -299,6 +299,13 @@
     }
 
     @NonNull
+    String getLastResourceResolution() throws NotFoundException {
+        String str = mAssets.getLastResourceResolution();
+        if (str != null) return str;
+        throw new NotFoundException("Associated AssetManager hasn't resolved a resource");
+    }
+
+    @NonNull
     CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException {
         PluralRules rule = getPluralRule();
         CharSequence res = mAssets.getResourceBagText(id,
@@ -848,13 +855,9 @@
             try {
                 if (file.endsWith(".xml")) {
                     if (file.startsWith("res/color/")) {
-                        ColorStateList csl = loadColorStateList(wrapper, value, id, null);
-                        dr = (csl != null ? new ColorStateListDrawable(csl) : null);
+                        dr = loadColorOrXmlDrawable(wrapper, value, id, density, file);
                     } else {
-                        final XmlResourceParser rp = loadXmlResourceParser(
-                                file, id, value.assetCookie, "drawable");
-                        dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
-                        rp.close();
+                        dr = loadXmlDrawable(wrapper, value, id, density, file);
                     }
                 } else {
                     final InputStream is = mAssets.openNonAsset(
@@ -908,6 +911,33 @@
         return dr;
     }
 
+    private Drawable loadColorOrXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
+            int id, int density, String file) {
+        try {
+            ColorStateList csl = loadColorStateList(wrapper, value, id, null);
+            return new ColorStateListDrawable(csl);
+        } catch (NotFoundException originalException) {
+            // If we fail to load as color, try as normal XML drawable
+            try {
+                return loadXmlDrawable(wrapper, value, id, density, file);
+            } catch (Exception ignored) {
+                // If fallback also fails, throw the original exception
+                throw originalException;
+            }
+        }
+    }
+
+    private Drawable loadXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
+            int id, int density, String file)
+            throws IOException, XmlPullParserException {
+        try (
+                XmlResourceParser rp =
+                        loadXmlResourceParser(file, id, value.assetCookie, "drawable")
+        ) {
+            return Drawable.createFromXmlForDensity(wrapper, rp, density, null);
+        }
+    }
+
     /**
      * Loads a font from XML or resources stream.
      */
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 508626b..d53834c 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -19,6 +19,7 @@
 import android.annotation.AnyRes;
 import android.annotation.ColorInt;
 import android.annotation.Nullable;
+import android.annotation.StyleRes;
 import android.annotation.StyleableRes;
 import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
@@ -63,13 +64,15 @@
     }
 
     // STYLE_ prefixed constants are offsets within the typed data array.
-    static final int STYLE_NUM_ENTRIES = 6;
+    // Keep this in sync with libs/androidfw/include/androidfw/AttributeResolution.h
+    static final int STYLE_NUM_ENTRIES = 7;
     static final int STYLE_TYPE = 0;
     static final int STYLE_DATA = 1;
     static final int STYLE_ASSET_COOKIE = 2;
     static final int STYLE_RESOURCE_ID = 3;
     static final int STYLE_CHANGING_CONFIGURATIONS = 4;
     static final int STYLE_DENSITY = 5;
+    static final int SYTLE_SOURCE_STYLE_RESOURCE_ID = 6;
 
     @UnsupportedAppUsage
     private final Resources mResources;
@@ -1098,6 +1101,31 @@
     }
 
     /**
+     * Returns the resource ID of the style against which the specified attribute was resolved,
+     * otherwise returns defValue.
+     *
+     * @param index Index of attribute whose source style to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute source style resource ID or defValue if it was not resolved in any style.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    @StyleRes
+    public int getSourceStyleResourceId(@StyleableRes int index, @StyleRes int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int resid = mData[index + SYTLE_SOURCE_STYLE_RESOURCE_ID];
+        if (resid != 0) {
+            return resid;
+        }
+        return defValue;
+    }
+
+    /**
      * Determines whether there is an attribute at <var>index</var>.
      * <p>
      * <strong>Note:</strong> If the attribute was set to {@code @empty} or
@@ -1309,6 +1337,7 @@
                 data[index + STYLE_CHANGING_CONFIGURATIONS]);
         outValue.density = data[index + STYLE_DENSITY];
         outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
+        outValue.sourceStyleResourceId = data[index + SYTLE_SOURCE_STYLE_RESOURCE_ID];
         return true;
     }
 
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index eb3414d..8aac1bf 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -42,13 +42,13 @@
     /**
      * The hardware is unavailable. Try again later.
      */
-    public static final int BIOMETRIC_ERROR_UNAVAILABLE =
+    public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE =
             BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
 
     /**
      * The user does not have any biometrics enrolled.
      */
-    public static final int BIOMETRIC_ERROR_NO_BIOMETRICS =
+    public static final int BIOMETRIC_ERROR_NONE_ENROLLED =
             BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
 
     /**
@@ -58,8 +58,8 @@
             BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
 
     @IntDef({BIOMETRIC_SUCCESS,
-            BIOMETRIC_ERROR_UNAVAILABLE,
-            BIOMETRIC_ERROR_NO_BIOMETRICS,
+            BIOMETRIC_ERROR_HW_UNAVAILABLE,
+            BIOMETRIC_ERROR_NONE_ENROLLED,
             BIOMETRIC_ERROR_NO_HARDWARE})
     @interface BiometricError {}
 
@@ -95,8 +95,8 @@
      * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt}
      * can be expected to be shown (hardware available, templates enrolled, user-enabled).
      *
-     * @return Returns {@link #BIOMETRIC_ERROR_NO_BIOMETRICS} if the user does not have any
-     *     enrolled, or {@link #BIOMETRIC_ERROR_UNAVAILABLE} if none are currently
+     * @return Returns {@link #BIOMETRIC_ERROR_NONE_ENROLLED} if the user does not have any
+     *     enrolled, or {@link #BIOMETRIC_ERROR_HW_UNAVAILABLE} if none are currently
      *     supported/enabled. Returns {@link #BIOMETRIC_SUCCESS} if a biometric can currently be
      *     used (enrolled and available).
      */
@@ -113,7 +113,7 @@
                 return BIOMETRIC_ERROR_NO_HARDWARE;
             } else {
                 Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
-                return BIOMETRIC_ERROR_UNAVAILABLE;
+                return BIOMETRIC_ERROR_HW_UNAVAILABLE;
             }
         }
     }
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index b238d77..c69b68e4 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -73,6 +73,10 @@
      * @hide
      */
     public static final String KEY_NEGATIVE_TEXT = "negative_text";
+    /**
+     * @hide
+     */
+    public static final String KEY_REQUIRE_CONFIRMATION = "require_confirmation";
 
     /**
      * Error/help message will show for this amount of time.
@@ -215,6 +219,29 @@
         }
 
         /**
+         * Optional: A hint to the system to require user confirmation after a biometric has been
+         * authenticated. For example, implicit modalities like Face and Iris authentication are
+         * passive, meaning they don't require an explicit user action to complete. When set to
+         * 'false', the user action (e.g. pressing a button) will not be required. BiometricPrompt
+         * will require confirmation by default.
+         *
+         * A typical use case for not requiring confirmation would be for low-risk transactions,
+         * such as re-authenticating a recently authenticated application. A typical use case for
+         * requiring confirmation would be for authorizing a purchase.
+         *
+         * Note that this is a hint to the system. The system may choose to ignore the flag. For
+         * example, if the user disables implicit authentication in Settings, or if it does not
+         * apply to a modality (e.g. Fingerprint). When ignored, the system will default to
+         * requiring confirmation.
+         *
+         * @param requireConfirmation
+         */
+        public Builder setRequireConfirmation(boolean requireConfirmation) {
+            mBundle.putBoolean(KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+            return this;
+        }
+
+        /**
          * Creates a {@link BiometricPrompt}.
          * @return a {@link BiometricPrompt}
          * @throws IllegalArgumentException if any of the required fields are not set.
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index be054297..8c74ddc 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -91,9 +91,7 @@
      *
      * @return The matching brightness correction, or null.
      *
-     * @hide
      */
-    @SystemApi
     @Nullable
     public BrightnessCorrection getCorrectionByPackageName(String packageName) {
         return mCorrectionsByPackageName.get(packageName);
@@ -106,10 +104,7 @@
      *      The app category.
      *
      * @return The matching brightness correction, or null.
-     *
-     * @hide
      */
-    @SystemApi
     @Nullable
     public BrightnessCorrection getCorrectionByCategory(int category) {
         return mCorrectionsByCategory.get(category);
@@ -416,9 +411,7 @@
          *
          * @return The maximum number of corrections by package name allowed.
          *
-         * @hide
          */
-        @SystemApi
         public int getMaxCorrectionsByPackageName() {
             return MAX_CORRECTIONS_BY_PACKAGE_NAME;
         }
@@ -428,9 +421,7 @@
          *
          * @return The maximum number of corrections by category allowed.
          *
-         * @hide
          */
-        @SystemApi
         public int getMaxCorrectionsByCategory() {
             return MAX_CORRECTIONS_BY_CATEGORY;
         }
@@ -451,9 +442,7 @@
          *      Maximum number of corrections by package name exceeded (see
          *      {@link #getMaxCorrectionsByPackageName}).
          *
-         * @hide
          */
-        @SystemApi
         public Builder addCorrectionByPackageName(String packageName,
                 BrightnessCorrection correction) {
             if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
@@ -479,9 +468,7 @@
          *      Maximum number of corrections by category exceeded (see
          *      {@link #getMaxCorrectionsByCategory}).
          *
-         * @hide
          */
-        @SystemApi
         public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
                 BrightnessCorrection correction) {
             if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
@@ -504,8 +491,6 @@
 
         /**
          * Builds the {@link BrightnessConfiguration}.
-         *
-         * A brightness curve <b>must</b> be set before calling this.
          */
         public BrightnessConfiguration build() {
             if (mCurveLux == null || mCurveNits == null) {
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
index c4e0e3b..6a073ff 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.java
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.MathUtils;
@@ -41,6 +42,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public final class BrightnessCorrection implements Parcelable {
 
     private static final int SCALE_AND_TRANSLATE_LOG = 1;
@@ -98,6 +100,24 @@
         return mImplementation.toString();
     }
 
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof BrightnessCorrection)) {
+            return false;
+        }
+        BrightnessCorrection other = (BrightnessCorrection) o;
+        return other.mImplementation.equals(mImplementation);
+    }
+
+    @Override
+    public int hashCode() {
+        return mImplementation.hashCode();
+    }
+
     public static final Creator<BrightnessCorrection> CREATOR =
             new Creator<BrightnessCorrection>() {
                 public BrightnessCorrection createFromParcel(Parcel in) {
@@ -215,6 +235,26 @@
         }
 
         @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (!(o instanceof ScaleAndTranslateLog)) {
+                return false;
+            }
+            ScaleAndTranslateLog other = (ScaleAndTranslateLog) o;
+            return other.mScale == mScale && other.mTranslate == mTranslate;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 1;
+            result = result * 31 + Float.hashCode(mScale);
+            result = result * 31 + Float.hashCode(mTranslate);
+            return result;
+        }
+
+        @Override
         public void writeToParcel(Parcel dest) {
             dest.writeInt(SCALE_AND_TRANSLATE_LOG);
             dest.writeFloat(mScale);
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index dd782ec..70a9f08 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -16,7 +16,11 @@
 
 package android.hardware.display;
 
+import android.Manifest;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.IBinder;
@@ -31,6 +35,7 @@
  *
  * @hide
  */
+@SystemApi
 @SystemService(Context.COLOR_DISPLAY_SERVICE)
 public final class ColorDisplayManager {
 
@@ -48,13 +53,43 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
     public boolean isDeviceColorManaged() {
         return mManager.isDeviceColorManaged();
     }
 
     /**
+     * Set the level of color saturation to apply to the display.
+     *
+     * @param saturationLevel 0-100 (inclusive), where 100 is full saturation
+     * @return whether the saturation level change was applied successfully
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    public boolean setSaturationLevel(@IntRange(from = 0, to = 100) int saturationLevel) {
+        return mManager.setSaturationLevel(saturationLevel);
+    }
+
+    /**
+     * Set the level of color saturation to apply to a specific app.
+     *
+     * @param packageName the package name of the app whose windows should be desaturated
+     * @param saturationLevel 0-100 (inclusive), where 100 is full saturation
+     * @return whether the saturation level change was applied successfully
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    public boolean setAppSaturationLevel(@NonNull String packageName,
+            @IntRange(from = 0, to = 100) int saturationLevel) {
+        return mManager.setAppSaturationLevel(packageName, saturationLevel);
+    }
+
+    /**
      * Returns {@code true} if Night Display is supported by the device.
+     *
+     * @hide
      */
     public static boolean isNightDisplayAvailable(Context context) {
         return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
@@ -62,11 +97,23 @@
 
     /**
      * Returns {@code true} if display white balance is supported by the device.
+     *
+     * @hide
      */
     public static boolean isDisplayWhiteBalanceAvailable(Context context) {
         return context.getResources().getBoolean(R.bool.config_displayWhiteBalanceAvailable);
     }
 
+    /**
+     * Check if the color transforms are color accelerated. Some transforms are experimental only
+     * on non-accelerated platforms due to the performance implications.
+     *
+     * @hide
+     */
+    public static boolean isColorTransformAccelerated(Context context) {
+        return context.getResources().getBoolean(R.bool.config_setColorTransformAccelerated);
+    }
+
     private static class ColorDisplayManagerInternal {
 
         private static ColorDisplayManagerInternal sInstance;
@@ -99,5 +146,21 @@
                 throw e.rethrowFromSystemServer();
             }
         }
+
+        boolean setSaturationLevel(int saturationLevel) {
+            try {
+                return mCdm.setSaturationLevel(saturationLevel);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        boolean setAppSaturationLevel(String packageName, int saturationLevel) {
+            try {
+                return mCdm.setAppSaturationLevel(packageName, saturationLevel);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 82e765d..44b653c 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -563,11 +563,16 @@
      * 0 produces a grayscale image, 1 is normal.
      *
      * @hide
+     * @deprecated use {@link ColorDisplayManager#setSaturationLevel(int)}.
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
     public void setSaturationLevel(float level) {
-        mGlobal.setSaturationLevel(level);
+        if (level < 0f || level > 1f) {
+            throw new IllegalArgumentException("Saturation level must be between 0 and 1");
+        }
+        final ColorDisplayManager cdm = mContext.getSystemService(ColorDisplayManager.class);
+        cdm.setSaturationLevel(Math.round(level * 100f));
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 7304ab4..7e45441 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -394,17 +394,6 @@
         }
     }
 
-    /**
-     * Set the level of color saturation to apply to the display.
-     */
-    public void setSaturationLevel(float level) {
-        try {
-            mDm.setSaturationLevel(level);
-        } catch (RemoteException ex) {
-            throw ex.rethrowFromSystemServer();
-        }
-    }
-
     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
             String name, int width, int height, int densityDpi, Surface surface, int flags,
             VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
@@ -447,6 +436,7 @@
     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
         try {
             mDm.setVirtualDisplaySurface(token, surface);
+            setVirtualDisplayState(token, surface != null);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -469,6 +459,14 @@
         }
     }
 
+    void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {
+        try {
+            mDm.setVirtualDisplayState(token, isOn);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Gets the stable device display size, in pixels.
      */
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index f786589..644f510 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -19,4 +19,7 @@
 /** @hide */
 interface IColorDisplayManager {
     boolean isDeviceColorManaged();
+
+    boolean setSaturationLevel(int saturationLevel);
+    boolean setAppSaturationLevel(String packageName, int saturationLevel);
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index b575997..aae8afb 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -66,9 +66,6 @@
     // Requires CONFIGURE_DISPLAY_COLOR_MODE
     void requestColorMode(int displayId, int colorMode);
 
-    // Requires CONTROL_DISPLAY_SATURATION
-    void setSaturationLevel(float level);
-
     // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
     // MediaProjection token for certain combinations of flags.
     int createVirtualDisplay(in IVirtualDisplayCallback callback,
@@ -85,6 +82,9 @@
     // No permissions required but must be same Uid as the creator.
     void releaseVirtualDisplay(in IVirtualDisplayCallback token);
 
+    // No permissions required but must be same Uid as the creator.
+    void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn);
+
     // Get a stable metric for the device's display size. No permissions required.
     Point getStableDisplaySize();
 
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index d354666..bf62c95 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -104,6 +104,18 @@
         }
     }
 
+    /**
+     * Sets the on/off state for a virtual display.
+     *
+     * @param isOn Whether the display should be on or off.
+     * @hide
+     */
+    public void setDisplayState(boolean isOn) {
+        if (mToken != null) {
+            mGlobal.setVirtualDisplayState(mToken, isOn);
+        }
+    }
+
     @Override
     public String toString() {
         return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 1630b06..c9a7830 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -163,8 +163,8 @@
                 mAuthenticationCallback = callback;
                 mCryptoObject = crypto;
                 long sessionId = crypto != null ? crypto.getOpId() : 0;
-                mService.authenticate(mToken, sessionId, mServiceReceiver, flags,
-                        mContext.getOpPackageName());
+                mService.authenticate(mToken, sessionId, mContext.getUserId(), mServiceReceiver,
+                        flags, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Log.w(TAG, "Remote exception while authenticating: ", e);
                 if (callback != null) {
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index a1c88f8..f67760a 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -27,7 +27,7 @@
  */
 interface IFaceService {
     // Authenticate the given sessionId with a face
-    void authenticate(IBinder token, long sessionId,
+    void authenticate(IBinder token, long sessionId, int userid,
             IFaceServiceReceiver receiver, int flags, String opPackageName);
 
     // This method prepares the service to start authenticating, but doesn't start authentication.
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index 9bebbd2..bdd5ab6 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -15,9 +15,12 @@
  */
 package android.hardware.hdmi;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -52,7 +55,22 @@
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler) {
         super(service);
-        mHandler = handler == null ? new Handler() : handler;
+        mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
+    }
+
+    /**
+     * Callback interface used to get the set System Audio Mode result.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public interface SetSystemAudioModeCallback {
+        /**
+         * Called when the input was changed.
+         *
+         * @param result the result of the set System Audio Mode
+         */
+        void onComplete(int result);
     }
 
     /** @hide */
@@ -116,4 +134,34 @@
             mPendingReportAudioStatus = true;
         }
     }
+
+    /**
+     * Set System Audio Mode on/off with audio system device.
+     *
+     * @param state true to set System Audio Mode on. False to set off.
+     * @param callback callback offer the setting result.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
+        // TODO(amyjojo): implement this when needed.
+    }
+
+    /**
+     * When device is switching to an audio only source, this method is called to broadcast
+     * a setSystemAudioMode on message to the HDMI CEC system without querying Active Source or
+     * TV supporting System Audio Control or not. This is to get volume control passthrough
+     * from STB even if TV does not support it.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public void setSystemAudioModeOnForAudioOnlySource() {
+        try {
+            mService.setSystemAudioModeOnForAudioOnlySource();
+        } catch (RemoteException e) {
+            Log.d(TAG, "Failed to set System Audio Mode on for Audio Only source");
+        }
+    }
 }
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 72a6ffe..a98b31a 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.hdmi;
 
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -27,9 +29,12 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * The {@link HdmiControlManager} class is used to send HDMI control messages
  * to attached CEC devices.
@@ -50,6 +55,10 @@
 
     @Nullable private final IHdmiControlService mService;
 
+    private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+
+    private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+
     /**
      * Broadcast Action: Display OSD message.
      * <p>Send when the service has a message to display on screen for events
@@ -264,6 +273,10 @@
     private final boolean mHasTvDevice;
     // True if we have a logical device of type audio system hosted in the system.
     private final boolean mHasAudioSystemDevice;
+    // True if we have a logical device of type audio system hosted in the system.
+    private final boolean mHasSwitchDevice;
+    // True if it's a switch device.
+    private final boolean mIsSwitchDevice;
 
     /**
      * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService,
@@ -283,6 +296,9 @@
         mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV);
         mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
         mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+        mIsSwitchDevice = SystemProperties.getBoolean(
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
     }
 
     private static boolean hasDeviceType(int[] types, int type) {
@@ -319,6 +335,9 @@
                 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
             case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
                 return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null;
+            case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
+                return (mHasSwitchDevice || mIsSwitchDevice)
+                    ? new HdmiSwitchClient(mService) : null;
             default:
                 return null;
         }
@@ -373,6 +392,90 @@
     }
 
     /**
+     * Gets an object that represents an HDMI-CEC logical device of type switch on the system.
+     *
+     * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
+     * possible to communicate with other logical devices hosted in the same system if the system is
+     * configured to host more than one type of HDMI-CEC logical devices.
+     *
+     * @return {@link HdmiSwitchClient} instance. {@code null} on failure.
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    @Nullable
+    @SuppressLint("Doclava125")
+    public HdmiSwitchClient getSwitchClient() {
+        return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+    }
+
+    /**
+     * Get a snapshot of the real-time status of the remote devices.
+     *
+     * @return a list of {@link HdmiDeviceInfo} of the devices connected to the current device.
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public List<HdmiDeviceInfo> getConnectedDevicesList() {
+        try {
+            return mService.getDeviceList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Power off the target device.
+     *
+     * @param deviceInfo HdmiDeviceInfo of the device to be powered off
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public void powerOffRemoteDevice(HdmiDeviceInfo deviceInfo) {
+        try {
+            mService.powerOffRemoteDevice(
+                    deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Power on the target device.
+     *
+     * @param deviceInfo HdmiDeviceInfo of the device to be powered on
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) {
+        try {
+            mService.powerOnRemoteDevice(
+                    deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Ask the target device to be the new Active Source.
+     *
+     * @param deviceInfo HdmiDeviceInfo of the target device
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public void askRemoteDeviceToBecomeActiveSource(HdmiDeviceInfo deviceInfo) {
+        try {
+            mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Controls standby mode of the system. It will also try to turn on/off the connected devices if
      * necessary.
      *
@@ -388,6 +491,59 @@
     }
 
     /**
+     * Gets whether the system is in system audio mode.
+     *
+     * @hide
+     */
+    public boolean getSystemAudioMode() {
+        try {
+            return mService.getSystemAudioMode();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the physical address of the device.
+     *
+     * @hide
+     */
+    public int getPhysicalAddress() {
+        if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
+            return mPhysicalAddress;
+        }
+        try {
+            mPhysicalAddress = mService.getPhysicalAddress();
+            return mPhysicalAddress;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check if the target device is connected to the current device. The
+     * API also returns true if the current device is the target.
+     *
+     * @param targetDevice {@link HdmiDeviceInfo} of the target device.
+     * @return true if {@code device} is directly or indirectly connected to the
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public boolean isTargetDeviceConnected(HdmiDeviceInfo targetDevice) {
+        mPhysicalAddress = getPhysicalAddress();
+        if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+            return false;
+        }
+        int targetPhysicalAddress = targetDevice.getPhysicalAddress();
+        if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+            return false;
+        }
+        return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress)
+            != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE;
+    }
+
+    /**
      * Listener used to get hotplug event from HDMI port.
      */
     public interface HotplugEventListener {
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
new file mode 100644
index 0000000..1ac2973
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -0,0 +1,137 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * HdmiSwitchClient represents HDMI-CEC logical device of type Switch in the Android system which
+ * acts as switch.
+ *
+ * <p>HdmiSwitchClient has a CEC device type of HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ * but it is used by all Android TV devices that have multiple HDMI inputs,
+ * even if it is not a "pure" swicth and has another device type like TV or Player.
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+public class HdmiSwitchClient extends HdmiClient {
+
+    private static final String TAG = "HdmiSwitchClient";
+
+    /* package */ HdmiSwitchClient(IHdmiControlService service) {
+        super(service);
+    }
+
+    private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+        return new IHdmiControlCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                callback.onComplete(result);
+            }
+        };
+    }
+
+    /** @hide */
+    // TODO(b/110094868): unhide for Q
+    @Override
+    public int getDeviceType() {
+        return HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
+    }
+
+    /**
+     * Selects a CEC logical device to be a new active source.
+     *
+     * @param logicalAddress logical address of the device to select
+     * @param callback callback to get the result with
+     * @throws {@link IllegalArgumentException} if the {@code callback} is null
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null.");
+        }
+        try {
+            mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to select device: ", e);
+        }
+    }
+
+    /**
+     * Selects a HDMI port to be a new route path.
+     *
+     * @param portId HDMI port to select
+     * @param callback callback to get the result with
+     * @throws {@link IllegalArgumentException} if the {@code callback} is null
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public void portSelect(int portId, @NonNull SelectCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback must not be null");
+        }
+        try {
+            mService.portSelect(portId, getCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to select port: ", e);
+        }
+    }
+
+    /**
+     * Returns all the CEC devices connected to the device.
+     *
+     * <p>This only applies to device with multiple HDMI inputs
+     *
+     * @return list of {@link HdmiDeviceInfo} for connected CEC devices. Empty list is returned if
+     * there is none.
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public List<HdmiDeviceInfo> getDeviceList() {
+        try {
+            return mService.getDeviceList();
+        } catch (RemoteException e) {
+            Log.e("TAG", "Failed to call getDeviceList():", e);
+            return Collections.<HdmiDeviceInfo>emptyList();
+        }
+    }
+
+    /**
+     * Callback interface used to get the result of {@link #deviceSelect} or {@link #portSelect}.
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public interface SelectCallback {
+
+        /**
+         * Called when the operation is finished.
+         *
+         * @param result the result value of {@link #deviceSelect} or {@link #portSelect}.
+         */
+        void onComplete(int result);
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiUtils.java b/core/java/android/hardware/hdmi/HdmiUtils.java
new file mode 100644
index 0000000..3081738
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiUtils.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+package android.hardware.hdmi;
+
+/**
+ * Various utilities to handle HDMI CEC messages.
+ *
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+public class HdmiUtils {
+
+    /**
+     * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)}
+     */
+    static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
+    static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
+
+    private HdmiUtils() { /* cannot be instantiated */ }
+
+    /**
+     * Method to parse target physical address to the port number on the current device.
+     *
+     * <p>This check assumes target address is valid.
+     *
+     * @param targetPhysicalAddress is the physical address of the target device
+     * @param myPhysicalAddress is the physical address of the current device
+     * @return
+     * If the target device is under the current device, return the port number of current device
+     * that the target device is connected to. This also applies to the devices that are indirectly
+     * connected to the current device.
+     *
+     * <p>If the target device has the same physical address as the current device, return
+     * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
+     *
+     * <p>If the target device is not under the current device, return
+     * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
+     */
+    public static int getLocalPortFromPhysicalAddress(
+            int targetPhysicalAddress, int myPhysicalAddress) {
+        if (myPhysicalAddress == targetPhysicalAddress) {
+            return TARGET_SAME_PHYSICAL_ADDRESS;
+        }
+
+        int mask = 0xF000;
+        int finalMask = 0xF000;
+        int maskedAddress = myPhysicalAddress;
+
+        while (maskedAddress != 0) {
+            maskedAddress = myPhysicalAddress & mask;
+            finalMask |= mask;
+            mask >>= 4;
+        }
+
+        int portAddress = targetPhysicalAddress & finalMask;
+        if ((portAddress & (finalMask << 4)) != myPhysicalAddress) {
+            return TARGET_NOT_UNDER_LOCAL_DEVICE;
+        }
+
+        mask <<= 4;
+        int port = portAddress & mask;
+        while ((port >> 4) != 0) {
+            port >>= 4;
+        }
+        return port;
+    }
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 2b8d00b..1cd9920 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -50,6 +50,7 @@
     List<HdmiPortInfo> getPortInfo();
     boolean canChangeSystemAudioMode();
     boolean getSystemAudioMode();
+    int getPhysicalAddress();
     void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback);
     void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
     void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
@@ -60,6 +61,9 @@
     void setInputChangeListener(IHdmiInputChangeListener listener);
     List<HdmiDeviceInfo> getInputDevices();
     List<HdmiDeviceInfo> getDeviceList();
+    void powerOffRemoteDevice(int logicalAddress, int powerStatus);
+    void powerOnRemoteDevice(int logicalAddress, int powerStatus);
+    void askRemoteDeviceToBecomeActiveSource(int physicalAddress);
     void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
             boolean hasVendorId);
     void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
@@ -73,4 +77,5 @@
     void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
     void setStandbyMode(boolean isStandbyModeOn);
     void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
+    void setSystemAudioModeOnForAudioOnlySource();
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 61d5a91..abc00fe 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -909,6 +910,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getActiveNetworkInfo() {
         try {
             return mService.getActiveNetworkInfo();
@@ -928,6 +930,7 @@
      *        {@code null} if no default network is currently active
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public Network getActiveNetwork() {
         try {
             return mService.getActiveNetwork();
@@ -949,6 +952,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @Nullable
     public Network getActiveNetworkForUid(int uid) {
         return getActiveNetworkForUid(uid, false);
     }
@@ -1074,6 +1078,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
             return mService.getNetworkInfo(networkType);
@@ -1095,7 +1100,8 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkInfo getNetworkInfo(Network network) {
+    @Nullable
+    public NetworkInfo getNetworkInfo(@Nullable Network network) {
         return getNetworkInfoForUid(network, Process.myUid(), false);
     }
 
@@ -1121,6 +1127,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public NetworkInfo[] getAllNetworkInfo() {
         try {
             return mService.getAllNetworkInfo();
@@ -1156,6 +1163,7 @@
      * @return an array of {@link Network} objects.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public Network[] getAllNetworks() {
         try {
             return mService.getAllNetworks();
@@ -1230,7 +1238,8 @@
      * @return The {@link LinkProperties} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public LinkProperties getLinkProperties(Network network) {
+    @Nullable
+    public LinkProperties getLinkProperties(@Nullable Network network) {
         try {
             return mService.getLinkProperties(network);
         } catch (RemoteException e) {
@@ -1246,7 +1255,8 @@
      * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkCapabilities getNetworkCapabilities(Network network) {
+    @Nullable
+    public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
             return mService.getNetworkCapabilities(network);
         } catch (RemoteException e) {
@@ -2000,7 +2010,7 @@
      *
      * @param l Previously registered listener.
      */
-    public void removeDefaultNetworkActiveListener(OnNetworkActiveListener l) {
+    public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
         INetworkActivityListener rl = mNetworkActivityListeners.get(l);
         Preconditions.checkArgument(rl != null, "Listener was not registered.");
         try {
@@ -2041,6 +2051,16 @@
         return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
     }
 
+    /** @hide */
+    public NetworkRequest getDefaultRequest() {
+        try {
+            // This is not racy as the default request is final in ConnectivityService.
+            return mService.getDefaultRequest();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /* TODO: These permissions checks don't belong in client-side code. Move them to
      * services.jar, possibly in com.android.server.net. */
 
@@ -2475,6 +2495,8 @@
     public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
     /** {@hide} */
     public static final int TETHER_ERROR_PROVISION_FAILED     = 11;
+    /** {@hide} */
+    public static final int TETHER_ERROR_DHCPSERVER_ERROR     = 12;
 
     /**
      * Get a more detailed error code after a Tethering or Untethering
@@ -2528,7 +2550,7 @@
      *             working and non-working connectivity.
      */
     @Deprecated
-    public void reportBadNetwork(Network network) {
+    public void reportBadNetwork(@Nullable Network network) {
         printStackTrace();
         try {
             // One of these will be ignored because it matches system's current state.
@@ -2551,7 +2573,7 @@
      * @param hasConnectivity {@code true} if the application was able to successfully access the
      *                        Internet using {@code network} or {@code false} if not.
      */
-    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+    public void reportNetworkConnectivity(@Nullable Network network, boolean hasConnectivity) {
         printStackTrace();
         try {
             mService.reportNetworkConnectivity(network, hasConnectivity);
@@ -2625,6 +2647,7 @@
      * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
      *        HTTP proxy is active.
      */
+    @Nullable
     public ProxyInfo getDefaultProxy() {
         return getProxyForNetwork(getBoundNetworkForProcess());
     }
@@ -3156,8 +3179,9 @@
      *
      * @hide
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs, int legacyType, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
+            @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3194,7 +3218,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         requestNetwork(request, networkCallback, getDefaultHandler());
     }
 
@@ -3229,8 +3254,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
         requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
@@ -3264,8 +3289,8 @@
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
@@ -3298,8 +3323,8 @@
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable} is called.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            Handler handler, int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
@@ -3371,7 +3396,8 @@
      *         {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
      *         {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
      */
-    public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3395,7 +3421,7 @@
      *                  {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
      *                  corresponding NetworkRequest you'd like to remove. Cannot be null.
      */
-    public void releaseNetworkRequest(PendingIntent operation) {
+    public void releaseNetworkRequest(@NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3428,7 +3454,8 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         registerNetworkCallback(request, networkCallback, getDefaultHandler());
     }
 
@@ -3443,8 +3470,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler);
@@ -3480,7 +3507,8 @@
      *                  comes from {@link PendingIntent#getBroadcast}. Cannot be null.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3502,7 +3530,7 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) {
         registerDefaultNetworkCallback(networkCallback, getDefaultHandler());
     }
 
@@ -3516,7 +3544,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+            @NonNull Handler handler) {
         // This works because if the NetworkCapabilities are null,
         // ConnectivityService takes them from the default request.
         //
@@ -3541,7 +3570,7 @@
      * @param network {@link Network} specifying which network you're interested.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean requestBandwidthUpdate(Network network) {
+    public boolean requestBandwidthUpdate(@NonNull Network network) {
         try {
             return mService.requestBandwidthUpdate(network);
         } catch (RemoteException e) {
@@ -3562,7 +3591,7 @@
      *
      * @param networkCallback The {@link NetworkCallback} used when making the request.
      */
-    public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+    public void unregisterNetworkCallback(@NonNull NetworkCallback networkCallback) {
         printStackTrace();
         checkCallbackNotNull(networkCallback);
         final List<NetworkRequest> reqs = new ArrayList<>();
@@ -3601,7 +3630,7 @@
      *                  {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}.
      *                  Cannot be null.
      */
-    public void unregisterNetworkCallback(PendingIntent operation) {
+    public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
         checkPendingIntentNotNull(operation);
         releaseNetworkRequest(operation);
     }
@@ -3723,7 +3752,7 @@
      * @return a bitwise OR of zero or more of the  {@code MULTIPATH_PREFERENCE_*} constants.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public @MultipathPreference int getMultipathPreference(Network network) {
+    public @MultipathPreference int getMultipathPreference(@Nullable Network network) {
         try {
             return mService.getMultipathPreference(network);
         } catch (RemoteException e) {
@@ -3761,7 +3790,7 @@
      *                the current binding.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean bindProcessToNetwork(Network network) {
+    public boolean bindProcessToNetwork(@Nullable Network network) {
         // Forcing callers to call through non-static function ensures ConnectivityManager
         // instantiated.
         return setProcessDefaultNetwork(network);
@@ -3789,7 +3818,7 @@
      *             is a direct replacement.
      */
     @Deprecated
-    public static boolean setProcessDefaultNetwork(Network network) {
+    public static boolean setProcessDefaultNetwork(@Nullable Network network) {
         int netId = (network == null) ? NETID_UNSET : network.netId;
         if (netId == NetworkUtils.getBoundNetworkForProcess()) {
             return true;
@@ -3820,6 +3849,7 @@
      *
      * @return {@code Network} to which this process is bound, or {@code null}.
      */
+    @Nullable
     public Network getBoundNetworkForProcess() {
         // Forcing callers to call thru non-static function ensures ConnectivityManager
         // instantiated.
@@ -3836,6 +3866,7 @@
      *             {@code getBoundNetworkForProcess} is a direct replacement.
      */
     @Deprecated
+    @Nullable
     public static Network getProcessDefaultNetwork() {
         int netId = NetworkUtils.getBoundNetworkForProcess();
         if (netId == NETID_UNSET) return null;
@@ -3962,6 +3993,7 @@
      *
      * @return Hash of network watchlist config file. Null if config does not exist.
      */
+    @Nullable
     public byte[] getNetworkWatchlistConfigHash() {
         try {
             return mService.getNetworkWatchlistConfigHash();
@@ -3983,8 +4015,8 @@
      * (e.g., if it is associated with the calling VPN app's tunnel) or
      * {@link android.os.Process#INVALID_UID} if the connection is not found.
      */
-    public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
-                                     InetSocketAddress remote) {
+    public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
+            @NonNull InetSocketAddress remote) {
         ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
         try {
             return mService.getConnectionOwnerUid(connectionInfo);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e7d441d..da5d96e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -167,6 +167,8 @@
 
     int getMultipathPreference(in Network Network);
 
+    NetworkRequest getDefaultRequest();
+
     int getRestoreDefaultNetworkDelay(int networkType);
 
     boolean addVpnAddress(String address, int prefixLength);
diff --git a/core/java/android/net/IIpMemoryStore.aidl b/core/java/android/net/IIpMemoryStore.aidl
new file mode 100644
index 0000000..6f88dec
--- /dev/null
+++ b/core/java/android/net/IIpMemoryStore.aidl
@@ -0,0 +1,113 @@
+/*
+ * 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.net;
+
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+
+/** {@hide} */
+oneway interface IIpMemoryStore {
+    /**
+     * Store network attributes for a given L2 key.
+     * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
+     * calling findL2Key with the attributes and storing in the returned value.
+     *
+     * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+     *              key and only care about grouping can pass a unique ID here like the ones
+     *              generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+     *              relevance of such a network will lead to it being evicted soon if it's not
+     *              refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+     * @param attributes The attributes for this network.
+     * @param listener A listener that will be invoked to inform of the completion of this call,
+     *                 or null if the client is not interested in learning about success/failure.
+     * @return (through the listener) The L2 key. This is useful if the L2 key was not specified.
+     *         If the call failed, the L2 key will be null.
+     */
+    void storeNetworkAttributes(String l2Key, in NetworkAttributesParcelable attributes,
+            IOnStatusListener listener);
+
+    /**
+     * Store a binary blob associated with an L2 key and a name.
+     *
+     * @param l2Key The L2 key for this network.
+     * @param clientId The ID of the client.
+     * @param name The name of this data.
+     * @param data The data to store.
+     * @param listener A listener to inform of the completion of this call, or null if the client
+     *        is not interested in learning about success/failure.
+     * @return (through the listener) A status to indicate success or failure.
+     */
+    void storeBlob(String l2Key, String clientId, String name, in Blob data,
+            IOnStatusListener listener);
+
+    /**
+     * Returns the best L2 key associated with the attributes.
+     *
+     * This will find a record that would be in the same group as the passed attributes. This is
+     * useful to choose the key for storing a sample or private data when the L2 key is not known.
+     * If multiple records are group-close to these attributes, the closest match is returned.
+     * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+     * order) L2 key is returned.
+     * If no record matches these attributes, null is returned.
+     *
+     * @param attributes The attributes of the network to find.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) The L2 key if one matched, or null.
+     */
+    void findL2Key(in NetworkAttributesParcelable attributes, IOnL2KeyResponseListener listener);
+
+    /**
+     * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+     * to the same L3 network. Group-closeness is used to determine this.
+     *
+     * @param l2Key1 The key for the first network.
+     * @param l2Key2 The key for the second network.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) A SameL3NetworkResponse containing the answer and confidence.
+     */
+    void isSameNetwork(String l2Key1, String l2Key2, IOnSameNetworkResponseListener listener);
+
+    /**
+     * Retrieve the network attributes for a key.
+     * If no record is present for this key, this will return null attributes.
+     *
+     * @param l2Key The key of the network to query.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) The network attributes and the L2 key associated with
+     *         the query.
+     */
+    void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrieved listener);
+
+    /**
+     * Retrieve previously stored private data.
+     * If no data was stored for this L2 key and name this will return null.
+     *
+     * @param l2Key The L2 key.
+     * @param clientId The id of the client that stored this data.
+     * @param name The name of the data.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) The private data (or null), with the L2 key
+     *         and the name of the data associated with the query.
+     */
+    void retrieveBlob(String l2Key, String clientId, String name,
+            IOnBlobRetrievedListener listener);
+}
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
new file mode 100644
index 0000000..41f969a
--- /dev/null
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -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 perNmissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.net.PrivateDnsConfigParcel;
+
+/** @hide */
+oneway interface INetworkMonitor {
+    // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
+    // The network should be used as a default internet connection.  It was found to be:
+    // 1. a functioning network providing internet access, or
+    // 2. a captive portal and the user decided to use it as is.
+    const int NETWORK_TEST_RESULT_VALID = 0;
+
+    // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
+    // The network should not be used as a default internet connection.  It was found to be:
+    // 1. a captive portal and the user is prompted to sign-in, or
+    // 2. a captive portal and the user did not want to use it, or
+    // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed).
+    const int NETWORK_TEST_RESULT_INVALID = 1;
+
+    void start();
+    void launchCaptivePortalApp();
+    void forceReevaluation(int uid);
+    void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
+    void notifyDnsResponse(int returnCode);
+    void notifySystemReady();
+    void notifyNetworkConnected();
+    void notifyNetworkDisconnected();
+    void notifyLinkPropertiesChanged();
+    void notifyNetworkCapabilitiesChanged();
+}
\ No newline at end of file
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
new file mode 100644
index 0000000..0bc2575
--- /dev/null
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -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.
+ */
+
+package android.net;
+
+import android.net.INetworkMonitor;
+import android.net.PrivateDnsConfigParcel;
+
+/** @hide */
+oneway interface INetworkMonitorCallbacks {
+    void onNetworkMonitorCreated(in INetworkMonitor networkMonitor);
+    void notifyNetworkTested(int testResult, @nullable String redirectUrl);
+    void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
+    void showProvisioningNotification(String action);
+    void hideProvisioningNotification();
+}
\ No newline at end of file
diff --git a/core/java/android/net/INetworkStackConnector.aidl b/core/java/android/net/INetworkStackConnector.aidl
index 29f8828..2df8ab7 100644
--- a/core/java/android/net/INetworkStackConnector.aidl
+++ b/core/java/android/net/INetworkStackConnector.aidl
@@ -15,7 +15,13 @@
  */
 package android.net;
 
+import android.net.INetworkMonitorCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+
 /** @hide */
 oneway interface INetworkStackConnector {
-    // TODO: requestDhcpServer(), etc. will go here
+    void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params,
+        in IDhcpServerCallbacks cb);
+    void makeNetworkMonitor(int netId, String name, in INetworkMonitorCallbacks cb);
 }
\ No newline at end of file
diff --git a/core/java/android/net/INetworkStackStatusCallback.aidl b/core/java/android/net/INetworkStackStatusCallback.aidl
new file mode 100644
index 0000000..51032d8
--- /dev/null
+++ b/core/java/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.net;
+
+/** @hide */
+oneway interface INetworkStackStatusCallback {
+    void onStatusAvailable(int statusCode);
+}
\ No newline at end of file
diff --git a/core/java/android/net/IpMemoryStore.java b/core/java/android/net/IpMemoryStore.java
new file mode 100644
index 0000000..b35f097
--- /dev/null
+++ b/core/java/android/net/IpMemoryStore.java
@@ -0,0 +1,174 @@
+/*
+ * 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.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributes;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * The interface for system components to access the IP memory store.
+ * @see com.android.server.net.ipmemorystore.IpMemoryStoreService
+ * @hide
+ */
+@SystemService(Context.IP_MEMORY_STORE_SERVICE)
+public class IpMemoryStore {
+    @NonNull final Context mContext;
+    @NonNull final IIpMemoryStore mService;
+
+    public IpMemoryStore(@NonNull final Context context, @NonNull final IIpMemoryStore service) {
+        mContext = Preconditions.checkNotNull(context, "missing context");
+        mService = Preconditions.checkNotNull(service, "missing IIpMemoryStore");
+    }
+
+    /**
+     * Store network attributes for a given L2 key.
+     * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
+     * calling findL2Key with the attributes and storing in the returned value.
+     *
+     * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+     *              key and only care about grouping can pass a unique ID here like the ones
+     *              generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+     *              relevance of such a network will lead to it being evicted soon if it's not
+     *              refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+     * @param attributes The attributes for this network.
+     * @param listener A listener that will be invoked to inform of the completion of this call,
+     *                 or null if the client is not interested in learning about success/failure.
+     * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
+     * If the call failed, the L2 key will be null.
+     */
+    public void storeNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final NetworkAttributes attributes,
+            @Nullable final IOnStatusListener listener) {
+        try {
+            mService.storeNetworkAttributes(l2Key, attributes.toParcelable(), listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Store a binary blob associated with an L2 key and a name.
+     *
+     * @param l2Key The L2 key for this network.
+     * @param clientId The ID of the client.
+     * @param name The name of this data.
+     * @param data The data to store.
+     * @param listener A listener to inform of the completion of this call, or null if the client
+     *        is not interested in learning about success/failure.
+     * Through the listener, returns a status to indicate success or failure.
+     */
+    public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final Blob data,
+            @Nullable final IOnStatusListener listener) {
+        try {
+            mService.storeBlob(l2Key, clientId, name, data, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the best L2 key associated with the attributes.
+     *
+     * This will find a record that would be in the same group as the passed attributes. This is
+     * useful to choose the key for storing a sample or private data when the L2 key is not known.
+     * If multiple records are group-close to these attributes, the closest match is returned.
+     * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+     * order) L2 key is returned.
+     * If no record matches these attributes, null is returned.
+     *
+     * @param attributes The attributes of the network to find.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the L2 key if one matched, or null.
+     */
+    public void findL2Key(@NonNull final NetworkAttributes attributes,
+            @NonNull final IOnL2KeyResponseListener listener) {
+        try {
+            mService.findL2Key(attributes.toParcelable(), listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+     * to the same L3 network. Group-closeness is used to determine this.
+     *
+     * @param l2Key1 The key for the first network.
+     * @param l2Key2 The key for the second network.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
+     */
+    public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
+            @NonNull final IOnSameNetworkResponseListener listener) {
+        try {
+            mService.isSameNetwork(l2Key1, l2Key2, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieve the network attributes for a key.
+     * If no record is present for this key, this will return null attributes.
+     *
+     * @param l2Key The key of the network to query.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the network attributes and the L2 key associated with
+     *         the query.
+     */
+    public void retrieveNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final IOnNetworkAttributesRetrieved listener) {
+        try {
+            mService.retrieveNetworkAttributes(l2Key, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieve previously stored private data.
+     * If no data was stored for this L2 key and name this will return null.
+     *
+     * @param l2Key The L2 key.
+     * @param clientId The id of the client that stored this data.
+     * @param name The name of the data.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the private data (or null), with the L2 key
+     *         and the name of the data associated with the query.
+     */
+    public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+        try {
+            mService.retrieveBlob(l2Key, clientId, name, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/net/IpPrefixParcelable.aidl b/core/java/android/net/IpPrefixParcelable.aidl
new file mode 100644
index 0000000..93a8d41
--- /dev/null
+++ b/core/java/android/net/IpPrefixParcelable.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.net;
+
+parcelable IpPrefixParcelable {
+    String address;
+    int prefixLength;
+}
\ No newline at end of file
diff --git a/core/java/android/net/LinkAddressParcelable.aidl b/core/java/android/net/LinkAddressParcelable.aidl
new file mode 100644
index 0000000..af8e79b
--- /dev/null
+++ b/core/java/android/net/LinkAddressParcelable.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.net;
+
+parcelable LinkAddressParcelable {
+    String address;
+    int prefixLength;
+    int flags;
+    int scope;
+}
\ No newline at end of file
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 3a79206..c2963fd 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -55,6 +55,8 @@
     private String mIfaceName;
     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
     private ArrayList<InetAddress> mDnses = new ArrayList<>();
+    // PCSCF addresses are addresses of SIP proxies that only exist for the IMS core service.
+    private ArrayList<InetAddress> mPcscfs = new ArrayList<InetAddress>();
     private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>();
     private boolean mUsePrivateDns;
     private String mPrivateDnsServerName;
@@ -64,6 +66,7 @@
     private int mMtu;
     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
     private String mTcpBufferSizes;
+    private IpPrefix mNat64Prefix;
 
     private static final int MIN_MTU    = 68;
     private static final int MIN_MTU_V6 = 1280;
@@ -179,6 +182,7 @@
             mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
             mUsePrivateDns = source.mUsePrivateDns;
             mPrivateDnsServerName = source.mPrivateDnsServerName;
+            mPcscfs.addAll(source.mPcscfs);
             mDomains = source.mDomains;
             mRoutes.addAll(source.mRoutes);
             mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
@@ -187,6 +191,7 @@
             }
             setMtu(source.mMtu);
             mTcpBufferSizes = source.mTcpBufferSizes;
+            mNat64Prefix = source.mNat64Prefix;
         }
     }
 
@@ -525,6 +530,60 @@
     }
 
     /**
+     * Adds the given {@link InetAddress} to the list of PCSCF servers, if not present.
+     *
+     * @param pcscfServer The {@link InetAddress} to add to the list of PCSCF servers.
+     * @return true if the PCSCF server was added, false otherwise.
+     * @hide
+     */
+    public boolean addPcscfServer(InetAddress pcscfServer) {
+        if (pcscfServer != null && !mPcscfs.contains(pcscfServer)) {
+            mPcscfs.add(pcscfServer);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Removes the given {@link InetAddress} from the list of PCSCF servers.
+     *
+     * @param pcscf Server The {@link InetAddress} to remove from the list of PCSCF servers.
+     * @return true if the PCSCF server was removed, false otherwise.
+     * @hide
+     */
+    public boolean removePcscfServer(InetAddress pcscfServer) {
+        if (pcscfServer != null) {
+            return mPcscfs.remove(pcscfServer);
+        }
+        return false;
+    }
+
+    /**
+     * Replaces the PCSCF servers in this {@code LinkProperties} with
+     * the given {@link Collection} of {@link InetAddress} objects.
+     *
+     * @param addresses The {@link Collection} of PCSCF servers to set in this object.
+     * @hide
+     */
+    public void setPcscfServers(Collection<InetAddress> pcscfServers) {
+        mPcscfs.clear();
+        for (InetAddress pcscfServer: pcscfServers) {
+            addPcscfServer(pcscfServer);
+        }
+    }
+
+    /**
+     * Returns all the {@link InetAddress} for PCSCF servers on this link.
+     *
+     * @return An unmodifiable {@link List} of {@link InetAddress} for PCSCF servers on
+     *         this link.
+     * @hide
+     */
+    public List<InetAddress> getPcscfServers() {
+        return Collections.unmodifiableList(mPcscfs);
+    }
+
+    /**
      * Sets the DNS domain search path used on this link.
      *
      * @param domains A {@link String} listing in priority order the comma separated
@@ -703,6 +762,32 @@
     }
 
     /**
+     * Returns the NAT64 prefix in use on this link, if any.
+     *
+     * @return the NAT64 prefix.
+     * @hide
+     */
+    public @Nullable IpPrefix getNat64Prefix() {
+        return mNat64Prefix;
+    }
+
+    /**
+     * Sets the NAT64 prefix in use on this link.
+     *
+     * Currently, only 96-bit prefixes (i.e., where the 32-bit IPv4 address is at the end of the
+     * 128-bit IPv6 address) are supported.
+     *
+     * @param prefix the NAT64 prefix.
+     * @hide
+     */
+    public void setNat64Prefix(IpPrefix prefix) {
+        if (prefix != null && prefix.getPrefixLength() != 96) {
+            throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
+        }
+        mNat64Prefix = prefix;  // IpPrefix objects are immutable.
+    }
+
+    /**
      * Adds a stacked link.
      *
      * If there is already a stacked link with the same interface name as link,
@@ -767,12 +852,14 @@
         mDnses.clear();
         mUsePrivateDns = false;
         mPrivateDnsServerName = null;
+        mPcscfs.clear();
         mDomains = null;
         mRoutes.clear();
         mHttpProxy = null;
         mStackedLinks.clear();
         mMtu = 0;
         mTcpBufferSizes = null;
+        mNat64Prefix = null;
     }
 
     /**
@@ -813,6 +900,12 @@
             resultJoiner.add(mPrivateDnsServerName);
         }
 
+        if (!mPcscfs.isEmpty()) {
+            resultJoiner.add("PcscfAddresses: [");
+            resultJoiner.add(TextUtils.join(",", mPcscfs));
+            resultJoiner.add("]");
+        }
+
         if (!mValidatedPrivateDnses.isEmpty()) {
             final StringJoiner validatedPrivateDnsesJoiner =
                     new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]");
@@ -844,6 +937,11 @@
             resultJoiner.add(mHttpProxy.toString());
         }
 
+        if (mNat64Prefix != null) {
+            resultJoiner.add("Nat64Prefix:");
+            resultJoiner.add(mNat64Prefix.toString());
+        }
+
         final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values();
         if (!stackedLinksValues.isEmpty()) {
             final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]");
@@ -965,6 +1063,36 @@
     }
 
     /**
+     * Returns true if this link has an IPv4 PCSCF server.
+     *
+     * @return {@code true} if there is an IPv4 PCSCF server, {@code false} otherwise.
+     * @hide
+     */
+    public boolean hasIPv4PcscfServer() {
+        for (InetAddress ia : mPcscfs) {
+          if (ia instanceof Inet4Address) {
+            return true;
+          }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this link has an IPv6 PCSCF server.
+     *
+     * @return {@code true} if there is an IPv6 PCSCF server, {@code false} otherwise.
+     * @hide
+     */
+    public boolean hasIPv6PcscfServer() {
+        for (InetAddress ia : mPcscfs) {
+          if (ia instanceof Inet6Address) {
+            return true;
+          }
+        }
+        return false;
+    }
+
+    /**
      * Returns true if this link is provisioned for global IPv4 connectivity.
      * This requires an IP address, default route, and DNS server.
      *
@@ -1117,6 +1245,19 @@
     }
 
     /**
+     * Compares this {@code LinkProperties} PCSCF addresses against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalPcscfs(LinkProperties target) {
+        Collection<InetAddress> targetPcscfs = target.getPcscfServers();
+        return (mPcscfs.size() == targetPcscfs.size()) ?
+                    mPcscfs.containsAll(targetPcscfs) : false;
+    }
+
+    /**
      * Compares this {@code LinkProperties} Routes against the target
      *
      * @param target LinkProperties to compare.
@@ -1188,6 +1329,17 @@
     }
 
     /**
+     * Compares this {@code LinkProperties} NAT64 prefix against the target.
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalNat64Prefix(LinkProperties target) {
+        return Objects.equals(mNat64Prefix, target.mNat64Prefix);
+    }
+
+    /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
      * all their fields are equal in values.
@@ -1218,11 +1370,13 @@
                 && isIdenticalDnses(target)
                 && isIdenticalPrivateDns(target)
                 && isIdenticalValidatedPrivateDnses(target)
+                && isIdenticalPcscfs(target)
                 && isIdenticalRoutes(target)
                 && isIdenticalHttpProxy(target)
                 && isIdenticalStackedLinks(target)
                 && isIdenticalMtu(target)
-                && isIdenticalTcpBufferSizes(target);
+                && isIdenticalTcpBufferSizes(target)
+                && isIdenticalNat64Prefix(target);
     }
 
     /**
@@ -1334,7 +1488,9 @@
                 + mMtu * 51
                 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
                 + (mUsePrivateDns ? 57 : 0)
-                + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
+                + mPcscfs.size() * 67
+                + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
+                + Objects.hash(mNat64Prefix);
     }
 
     /**
@@ -1357,6 +1513,10 @@
         }
         dest.writeBoolean(mUsePrivateDns);
         dest.writeString(mPrivateDnsServerName);
+        dest.writeInt(mPcscfs.size());
+        for (InetAddress d : mPcscfs) {
+            dest.writeByteArray(d.getAddress());
+        }
         dest.writeString(mDomains);
         dest.writeInt(mMtu);
         dest.writeString(mTcpBufferSizes);
@@ -1371,6 +1531,8 @@
         } else {
             dest.writeByte((byte)0);
         }
+        dest.writeParcelable(mNat64Prefix, 0);
+
         ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
         dest.writeList(stackedLinks);
     }
@@ -1406,6 +1568,12 @@
                 }
                 netProp.setUsePrivateDns(in.readBoolean());
                 netProp.setPrivateDnsServerName(in.readString());
+                addressCount = in.readInt();
+                for (int i = 0; i < addressCount; i++) {
+                    try {
+                        netProp.addPcscfServer(InetAddress.getByAddress(in.createByteArray()));
+                    } catch (UnknownHostException e) { }
+                }
                 netProp.setDomains(in.readString());
                 netProp.setMtu(in.readInt());
                 netProp.setTcpBufferSizes(in.readString());
@@ -1416,6 +1584,7 @@
                 if (in.readByte() == 1) {
                     netProp.setHttpProxy(in.readParcelable(null));
                 }
+                netProp.setNat64Prefix(in.readParcelable(null));
                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
                 for (LinkProperties stackedLink: stackedLinks) {
diff --git a/core/java/android/net/LinkPropertiesParcelable.aidl b/core/java/android/net/LinkPropertiesParcelable.aidl
new file mode 100644
index 0000000..b153dc7
--- /dev/null
+++ b/core/java/android/net/LinkPropertiesParcelable.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.net;
+
+import android.net.IpPrefixParcelable;
+import android.net.LinkAddressParcelable;
+import android.net.ProxyInfoParcelable;
+import android.net.RouteInfoParcelable;
+
+parcelable LinkPropertiesParcelable {
+    String ifaceName;
+    LinkAddressParcelable[] linkAddresses;
+    String[] dnses;
+    String[] pcscfs;
+    String[] validatedPrivateDnses;
+    boolean usePrivateDns;
+    String privateDnsServerName;
+    String domains;
+    RouteInfoParcelable[] routes;
+    ProxyInfoParcelable httpProxy;
+    int mtu;
+    String tcpBufferSizes;
+    IpPrefixParcelable nat64Prefix;
+    LinkPropertiesParcelable[] stackedLinks;
+}
\ No newline at end of file
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index be2f955..fcfb720 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -17,7 +17,7 @@
 package android.net;
 
 /**
- * Describes specific properties of a network for use in a {@link NetworkRequest}.
+ * Describes specific properties of a requested network for use in a {@link NetworkRequest}.
  *
  * Applications cannot instantiate this class by themselves, but can obtain instances of
  * subclasses of this class via other APIs.
@@ -49,4 +49,29 @@
     public void assertValidFromUid(int requestorUid) {
         // empty
     }
+
+    /**
+     * Optional method which can be overridden by concrete implementations of NetworkSpecifier to
+     * perform any redaction of information from the NetworkSpecifier, e.g. if it contains
+     * sensitive information. The default implementation simply returns the object itself - i.e.
+     * no information is redacted. A concrete implementation may return a modified (copy) of the
+     * NetworkSpecifier, or even return a null to fully remove all information.
+     * <p>
+     * This method is relevant to NetworkSpecifier objects used by agents - those are shared with
+     * apps by default. Some agents may store sensitive matching information in the specifier,
+     * e.g. a Wi-Fi SSID (which should not be shared since it may leak location). Those classes
+     * can redact to a null. Other agents use the Network Specifier to share public information
+     * with apps - those should not be redacted.
+     * <p>
+     * The default implementation redacts no information.
+     *
+     * @return A NetworkSpecifier object to be passed along to the requesting app.
+     *
+     * @hide
+     */
+    public NetworkSpecifier redact() {
+        // TODO (b/122160111): convert default to null once all platform NetworkSpecifiers
+        // implement this method.
+        return this;
+    }
 }
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 82a4e31..2eac6de 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -25,9 +25,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Slog;
@@ -45,19 +48,52 @@
 public class NetworkStack {
     private static final String TAG = NetworkStack.class.getSimpleName();
 
+    public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack";
+
     @NonNull
     @GuardedBy("mPendingNetStackRequests")
-    private final ArrayList<NetworkStackRequest> mPendingNetStackRequests = new ArrayList<>();
+    private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
     @Nullable
     @GuardedBy("mPendingNetStackRequests")
     private INetworkStackConnector mConnector;
 
-    private interface NetworkStackRequest {
+    private interface NetworkStackCallback {
         void onNetworkStackConnected(INetworkStackConnector connector);
     }
 
     public NetworkStack() { }
 
+    /**
+     * Create a DHCP server according to the specified parameters.
+     *
+     * <p>The server will be returned asynchronously through the provided callbacks.
+     */
+    public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+            final IDhcpServerCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeDhcpServer(ifName, params, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    /**
+     * Create a NetworkMonitor.
+     *
+     * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+     */
+    public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeNetworkMonitor(network.netId, name, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
     private class NetworkStackConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -77,14 +113,14 @@
         ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
                 DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
 
-        final ArrayList<NetworkStackRequest> requests;
+        final ArrayList<NetworkStackCallback> requests;
         synchronized (mPendingNetStackRequests) {
             requests = new ArrayList<>(mPendingNetStackRequests);
             mPendingNetStackRequests.clear();
             mConnector = connector;
         }
 
-        for (NetworkStackRequest r : requests) {
+        for (NetworkStackCallback r : requests) {
             r.onNetworkStackConnected(connector);
         }
     }
@@ -105,7 +141,8 @@
                     "com.android.server.NetworkStackService",
                     true /* initialize */,
                     context.getClassLoader());
-            connector = (IBinder) service.getMethod("makeConnector").invoke(null);
+            connector = (IBinder) service.getMethod("makeConnector", Context.class)
+                    .invoke(null, context);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
             Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
             // TODO: crash/reboot system here ?
@@ -134,7 +171,7 @@
     }
 
     // TODO: use this method to obtain the connector when implementing network stack operations
-    private void requestConnector(@NonNull NetworkStackRequest request) {
+    private void requestConnector(@NonNull NetworkStackCallback request) {
         // TODO: PID check.
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
             // Don't even attempt to obtain the connector and give a nice error message
diff --git a/core/java/android/net/PrivateDnsConfigParcel.aidl b/core/java/android/net/PrivateDnsConfigParcel.aidl
new file mode 100644
index 0000000..b52fce6
--- /dev/null
+++ b/core/java/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.net;
+
+parcelable PrivateDnsConfigParcel {
+    String hostname;
+    String[] ips;
+}
diff --git a/core/java/android/net/ProxyInfoParcelable.aidl b/core/java/android/net/ProxyInfoParcelable.aidl
new file mode 100644
index 0000000..59fd846
--- /dev/null
+++ b/core/java/android/net/ProxyInfoParcelable.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.net;
+
+parcelable ProxyInfoParcelable {
+    String host;
+    int port;
+    String[] exclusionList;
+    String pacFileUrl;
+}
diff --git a/core/java/android/net/RouteInfoParcelable.aidl b/core/java/android/net/RouteInfoParcelable.aidl
new file mode 100644
index 0000000..15bcdcf
--- /dev/null
+++ b/core/java/android/net/RouteInfoParcelable.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.net;
+
+import android.net.IpPrefixParcelable;
+
+parcelable RouteInfoParcelable {
+    IpPrefixParcelable destination;
+    String gatewayAddr;
+    String ifaceName;
+    int type;
+}
diff --git a/core/java/android/net/dhcp/DhcpServerCallbacks.java b/core/java/android/net/dhcp/DhcpServerCallbacks.java
new file mode 100644
index 0000000..bb56876
--- /dev/null
+++ b/core/java/android/net/dhcp/DhcpServerCallbacks.java
@@ -0,0 +1,33 @@
+/*
+ * 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.net.dhcp;
+
+/**
+ * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion().
+ * @hide
+ */
+public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
+    // TODO: add @Override here once the API is versioned
+
+    /**
+     * Get the version of the aidl interface implemented by the callbacks.
+     */
+    public int getInterfaceVersion() {
+        // TODO: return IDhcpServerCallbacks.VERSION;
+        return 0;
+    }
+}
diff --git a/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
new file mode 100644
index 0000000..7b8b9ee
--- /dev/null
+++ b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -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 android.net.dhcp;
+
+parcelable DhcpServingParamsParcel {
+    int serverAddr;
+    int serverAddrPrefixLength;
+    int[] defaultRouters;
+    int[] dnsServers;
+    int[] excludedAddrs;
+    long dhcpLeaseTimeSecs;
+    int linkMtu;
+    boolean metered;
+}
+
diff --git a/core/java/android/net/dhcp/IDhcpServer.aidl b/core/java/android/net/dhcp/IDhcpServer.aidl
new file mode 100644
index 0000000..559433b
--- /dev/null
+++ b/core/java/android/net/dhcp/IDhcpServer.aidl
@@ -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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import android.net.INetworkStackStatusCallback;
+import android.net.dhcp.DhcpServingParamsParcel;
+
+/** @hide */
+oneway interface IDhcpServer {
+    const int STATUS_UNKNOWN = 0;
+    const int STATUS_SUCCESS = 1;
+    const int STATUS_INVALID_ARGUMENT = 2;
+    const int STATUS_UNKNOWN_ERROR = 3;
+
+    void start(in INetworkStackStatusCallback cb);
+    void updateParams(in DhcpServingParamsParcel params, in INetworkStackStatusCallback cb);
+    void stop(in INetworkStackStatusCallback cb);
+}
diff --git a/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl b/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
new file mode 100644
index 0000000..7ab4dcd
--- /dev/null
+++ b/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import android.net.dhcp.IDhcpServer;
+
+/** @hide */
+oneway interface IDhcpServerCallbacks {
+    void onDhcpServerCreated(int statusCode, in IDhcpServer server);
+}
diff --git a/core/java/android/net/ipmemorystore/Blob.aidl b/core/java/android/net/ipmemorystore/Blob.aidl
new file mode 100644
index 0000000..9dbef11
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/Blob.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.net.ipmemorystore;
+
+/**
+ * A blob of data opaque to the memory store. The client mutates this at its own risk,
+ * and it is strongly suggested to never do it at all and treat this as immutable.
+ * {@hide}
+ */
+parcelable Blob {
+    byte[] data;
+}
diff --git a/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
new file mode 100644
index 0000000..4926feb
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
@@ -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 android.net.ipmemorystore;
+
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnBlobRetrievedListener {
+    /**
+     * Private data was retrieved for the L2 key and name specified.
+     * Note this does not return the client ID, as clients are expected to only ever use one ID.
+     */
+     void onBlobRetrieved(in StatusParcelable status, in String l2Key, in String name,
+             in Blob data);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
new file mode 100644
index 0000000..dea0cc4
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnL2KeyResponseListener {
+    /**
+     * The operation completed with the specified L2 key.
+     */
+     void onL2KeyResponse(in StatusParcelable status, in String l2Key);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl b/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
new file mode 100644
index 0000000..57f59a1
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
@@ -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 android.net.ipmemorystore;
+
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnNetworkAttributesRetrieved {
+    /**
+     * Network attributes were fetched for the specified L2 key. While the L2 key will never
+     * be null, the attributes may be if no data is stored about this L2 key.
+     */
+     void onL2KeyResponse(in StatusParcelable status, in String l2Key,
+             in NetworkAttributesParcelable attributes);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl b/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
new file mode 100644
index 0000000..294bd3b
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
@@ -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.
+ */
+
+package android.net.ipmemorystore;
+
+import android.net.ipmemorystore.SameL3NetworkResponseParcelable;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnSameNetworkResponseListener {
+    /**
+     * The memory store has come up with the answer to a query that was sent.
+     */
+     void onSameNetworkResponse(in StatusParcelable status,
+             in SameL3NetworkResponseParcelable response);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnStatusListener.aidl b/core/java/android/net/ipmemorystore/IOnStatusListener.aidl
new file mode 100644
index 0000000..5d07504
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnStatusListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnStatusListener {
+    /**
+     * The operation has completed with the specified status.
+     */
+     void onComplete(in StatusParcelable status);
+}
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java
new file mode 100644
index 0000000..b932d21
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/NetworkAttributes.java
@@ -0,0 +1,259 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * A POD object to represent attributes of a single L2 network entry.
+ * @hide
+ */
+public class NetworkAttributes {
+    private static final boolean DBG = true;
+
+    // The v4 address that was assigned to this device the last time it joined this network.
+    // This typically comes from DHCP but could be something else like static configuration.
+    // This does not apply to IPv6.
+    // TODO : add a list of v6 prefixes for the v6 case.
+    @Nullable
+    public final Inet4Address assignedV4Address;
+
+    // Optionally supplied by the client if it has an opinion on L3 network. For example, this
+    // could be a hash of the SSID + security type on WiFi.
+    @Nullable
+    public final String groupHint;
+
+    // The list of DNS server addresses.
+    @Nullable
+    public final List<InetAddress> dnsAddresses;
+
+    // The mtu on this network.
+    @Nullable
+    public final Integer mtu;
+
+    NetworkAttributes(
+            @Nullable final Inet4Address assignedV4Address,
+            @Nullable final String groupHint,
+            @Nullable final List<InetAddress> dnsAddresses,
+            @Nullable final Integer mtu) {
+        if (mtu != null && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
+        this.assignedV4Address = assignedV4Address;
+        this.groupHint = groupHint;
+        this.dnsAddresses = null == dnsAddresses ? null :
+                Collections.unmodifiableList(new ArrayList<>(dnsAddresses));
+        this.mtu = mtu;
+    }
+
+    @VisibleForTesting
+    public NetworkAttributes(@NonNull final NetworkAttributesParcelable parcelable) {
+        // The call to the other constructor must be the first statement of this constructor,
+        // so everything has to be inline
+        this((Inet4Address) getByAddressOrNull(parcelable.assignedV4Address),
+                parcelable.groupHint,
+                blobArrayToInetAddressList(parcelable.dnsAddresses),
+                parcelable.mtu >= 0 ? parcelable.mtu : null);
+    }
+
+    @Nullable
+    private static InetAddress getByAddressOrNull(@Nullable final byte[] address) {
+        try {
+            return InetAddress.getByAddress(address);
+        } catch (UnknownHostException e) {
+            return null;
+        }
+    }
+
+    @Nullable
+    private static List<InetAddress> blobArrayToInetAddressList(@Nullable final Blob[] blobs) {
+        if (null == blobs) return null;
+        final ArrayList<InetAddress> list = new ArrayList<>(blobs.length);
+        for (final Blob b : blobs) {
+            final InetAddress addr = getByAddressOrNull(b.data);
+            if (null != addr) list.add(addr);
+        }
+        return list;
+    }
+
+    @Nullable
+    private static Blob[] inetAddressListToBlobArray(@Nullable final List<InetAddress> addresses) {
+        if (null == addresses) return null;
+        final ArrayList<Blob> blobs = new ArrayList<>();
+        for (int i = 0; i < addresses.size(); ++i) {
+            final InetAddress addr = addresses.get(i);
+            if (null == addr) continue;
+            final Blob b = new Blob();
+            b.data = addr.getAddress();
+            blobs.add(b);
+        }
+        return blobs.toArray(new Blob[0]);
+    }
+
+    /** Converts this NetworkAttributes to a parcelable object */
+    @NonNull
+    public NetworkAttributesParcelable toParcelable() {
+        final NetworkAttributesParcelable parcelable = new NetworkAttributesParcelable();
+        parcelable.assignedV4Address =
+                (null == assignedV4Address) ? null : assignedV4Address.getAddress();
+        parcelable.groupHint = groupHint;
+        parcelable.dnsAddresses = inetAddressListToBlobArray(dnsAddresses);
+        parcelable.mtu = (null == mtu) ? -1 : mtu;
+        return parcelable;
+    }
+
+    /** @hide */
+    public static class Builder {
+        @Nullable
+        private Inet4Address mAssignedAddress;
+        @Nullable
+        private String mGroupHint;
+        @Nullable
+        private List<InetAddress> mDnsAddresses;
+        @Nullable
+        private Integer mMtu;
+
+        /**
+         * Set the assigned address.
+         * @param assignedV4Address The assigned address.
+         * @return This builder.
+         */
+        public Builder setAssignedV4Address(@Nullable final Inet4Address assignedV4Address) {
+            mAssignedAddress = assignedV4Address;
+            return this;
+        }
+
+        /**
+         * Set the group hint.
+         * @param groupHint The group hint.
+         * @return This builder.
+         */
+        public Builder setGroupHint(@Nullable final String groupHint) {
+            mGroupHint = groupHint;
+            return this;
+        }
+
+        /**
+         * Set the DNS addresses.
+         * @param dnsAddresses The DNS addresses.
+         * @return This builder.
+         */
+        public Builder setDnsAddresses(@Nullable final List<InetAddress> dnsAddresses) {
+            if (DBG && null != dnsAddresses) {
+                // Parceling code crashes if one of the addresses is null, therefore validate
+                // them when running in debug.
+                for (final InetAddress address : dnsAddresses) {
+                    if (null == address) throw new IllegalArgumentException("Null DNS address");
+                }
+            }
+            this.mDnsAddresses = dnsAddresses;
+            return this;
+        }
+
+        /**
+         * Set the MTU.
+         * @param mtu The MTU.
+         * @return This builder.
+         */
+        public Builder setMtu(@Nullable final Integer mtu) {
+            if (null != mtu && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
+            mMtu = mtu;
+            return this;
+        }
+
+        /**
+         * Return the built NetworkAttributes object.
+         * @return The built NetworkAttributes object.
+         */
+        public NetworkAttributes build() {
+            return new NetworkAttributes(mAssignedAddress, mGroupHint, mDnsAddresses, mMtu);
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof NetworkAttributes)) return false;
+        final NetworkAttributes other = (NetworkAttributes) o;
+        return Objects.equals(assignedV4Address, other.assignedV4Address)
+                && Objects.equals(groupHint, other.groupHint)
+                && Objects.equals(dnsAddresses, other.dnsAddresses)
+                && Objects.equals(mtu, other.mtu);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(assignedV4Address, groupHint, dnsAddresses, mtu);
+    }
+
+    /** Pretty print */
+    @Override
+    public String toString() {
+        final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
+        final ArrayList<String> nullFields = new ArrayList<>();
+
+        if (null != assignedV4Address) {
+            resultJoiner.add("assignedV4Addr :");
+            resultJoiner.add(assignedV4Address.toString());
+        } else {
+            nullFields.add("assignedV4Addr");
+        }
+
+        if (null != groupHint) {
+            resultJoiner.add("groupHint :");
+            resultJoiner.add(groupHint);
+        } else {
+            nullFields.add("groupHint");
+        }
+
+        if (null != dnsAddresses) {
+            resultJoiner.add("dnsAddr : [");
+            for (final InetAddress addr : dnsAddresses) {
+                resultJoiner.add(addr.getHostAddress());
+            }
+            resultJoiner.add("]");
+        } else {
+            nullFields.add("dnsAddr");
+        }
+
+        if (null != mtu) {
+            resultJoiner.add("mtu :");
+            resultJoiner.add(mtu.toString());
+        } else {
+            nullFields.add("mtu");
+        }
+
+        if (!nullFields.isEmpty()) {
+            resultJoiner.add("; Null fields : [");
+            for (final String field : nullFields) {
+                resultJoiner.add(field);
+            }
+            resultJoiner.add("]");
+        }
+
+        return resultJoiner.toString();
+    }
+}
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
new file mode 100644
index 0000000..0894d72
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.net.ipmemorystore;
+
+// Blob[] is used to represent an array of byte[], as structured AIDL does not support arrays
+// of arrays.
+import android.net.ipmemorystore.Blob;
+
+/**
+ * An object to represent attributes of a single L2 network entry.
+ * See NetworkAttributes.java for a description of each field. The types used in this class
+ * are structured parcelable types instead of the richer types of the NetworkAttributes object,
+ * but they have the same purpose. The NetworkAttributes.java file also contains the code
+ * to convert the richer types to the parcelable types and back.
+ * @hide
+ */
+parcelable NetworkAttributesParcelable {
+    byte[] assignedV4Address;
+    String groupHint;
+    Blob[] dnsAddresses;
+    int mtu;
+}
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
new file mode 100644
index 0000000..d040dcc
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
@@ -0,0 +1,146 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * An object representing the answer to a query whether two given L2 networks represent the
+ * same L3 network. Parcels as a SameL3NetworkResponseParceled object.
+ * @hide
+ */
+public class SameL3NetworkResponse {
+    @IntDef(prefix = "NETWORK_",
+            value = {NETWORK_SAME, NETWORK_DIFFERENT, NETWORK_NEVER_CONNECTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NetworkSameness {}
+
+    /**
+     * Both L2 networks represent the same L3 network.
+     */
+    public static final int NETWORK_SAME = 1;
+
+    /**
+     * The two L2 networks represent a different L3 network.
+     */
+    public static final int NETWORK_DIFFERENT = 2;
+
+    /**
+     * The device has never connected to at least one of these two L2 networks, or data
+     * has been wiped. Therefore the device has never seen the L3 network behind at least
+     * one of these two L2 networks, and can't evaluate whether it's the same as the other.
+     */
+    public static final int NETWORK_NEVER_CONNECTED = 3;
+
+    /**
+     * The first L2 key specified in the query.
+     */
+    @NonNull
+    public final String l2Key1;
+
+    /**
+     * The second L2 key specified in the query.
+     */
+    @NonNull
+    public final String l2Key2;
+
+    /**
+     * A confidence value indicating whether the two L2 networks represent the same L3 network.
+     *
+     * If both L2 networks were known, this value will be between 0.0 and 1.0, with 0.0
+     * representing complete confidence that the given L2 networks represent a different
+     * L3 network, and 1.0 representing complete confidence that the given L2 networks
+     * represent the same L3 network.
+     * If at least one of the L2 networks was not known, this value will be outside of the
+     * 0.0~1.0 range.
+     *
+     * Most apps should not be interested in this, and are encouraged to use the collapsing
+     * {@link #getNetworkSameness()} function below.
+     */
+    public final float confidence;
+
+    /**
+     * @return whether the two L2 networks represent the same L3 network. Either
+     *     {@code NETWORK_SAME}, {@code NETWORK_DIFFERENT} or {@code NETWORK_NEVER_CONNECTED}.
+     */
+    @NetworkSameness
+    public final int getNetworkSameness() {
+        if (confidence > 1.0 || confidence < 0.0) return NETWORK_NEVER_CONNECTED;
+        return confidence > 0.5 ? NETWORK_SAME : NETWORK_DIFFERENT;
+    }
+
+    SameL3NetworkResponse(@NonNull final String l2Key1, @NonNull final String l2Key2,
+            final float confidence) {
+        this.l2Key1 = l2Key1;
+        this.l2Key2 = l2Key2;
+        this.confidence = confidence;
+    }
+
+    /** Builds a SameL3NetworkResponse from a parcelable object */
+    @VisibleForTesting
+    public SameL3NetworkResponse(@NonNull final SameL3NetworkResponseParcelable parceled) {
+        this(parceled.l2Key1, parceled.l2Key2, parceled.confidence);
+    }
+
+    /** Converts this SameL3NetworkResponse to a parcelable object */
+    @NonNull
+    public SameL3NetworkResponseParcelable toParcelable() {
+        final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable();
+        parcelable.l2Key1 = l2Key1;
+        parcelable.l2Key2 = l2Key2;
+        parcelable.confidence = confidence;
+        return parcelable;
+    }
+
+    // Note key1 and key2 have to match each other for this to return true. If
+    // key1 matches o.key2 and the other way around this returns false.
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof SameL3NetworkResponse)) return false;
+        final SameL3NetworkResponse other = (SameL3NetworkResponse) o;
+        return l2Key1.equals(other.l2Key1) && l2Key2.equals(other.l2Key2)
+                && confidence == other.confidence;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(l2Key1, l2Key2, confidence);
+    }
+
+    @Override
+    /** Pretty print */
+    public String toString() {
+        switch (getNetworkSameness()) {
+            case NETWORK_SAME:
+                return "\"" + l2Key1 + "\" same L3 network as \"" + l2Key2 + "\"";
+            case NETWORK_DIFFERENT:
+                return "\"" + l2Key1 + "\" different L3 network from \"" + l2Key2 + "\"";
+            case NETWORK_NEVER_CONNECTED:
+                return "\"" + l2Key1 + "\" can't be tested against \"" + l2Key2 + "\"";
+            default:
+                return "Buggy sameness value ? \"" + l2Key1 + "\", \"" + l2Key2 + "\"";
+        }
+    }
+}
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
new file mode 100644
index 0000000..7196699
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+/** {@hide} */
+parcelable SameL3NetworkResponseParcelable {
+    String l2Key1;
+    String l2Key2;
+    float confidence;
+}
diff --git a/core/java/android/net/ipmemorystore/Status.java b/core/java/android/net/ipmemorystore/Status.java
new file mode 100644
index 0000000..95e5042
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/Status.java
@@ -0,0 +1,62 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A parcelable status representing the result of an operation.
+ * Parcels as StatusParceled.
+ * @hide
+ */
+public class Status {
+    public static final int SUCCESS = 0;
+
+    public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -1;
+
+    public final int resultCode;
+
+    public Status(final int resultCode) {
+        this.resultCode = resultCode;
+    }
+
+    Status(@NonNull final StatusParcelable parcelable) {
+        this(parcelable.resultCode);
+    }
+
+    /** Converts this Status to a parcelable object */
+    @NonNull
+    public StatusParcelable toParcelable() {
+        final StatusParcelable parcelable = new StatusParcelable();
+        parcelable.resultCode = resultCode;
+        return parcelable;
+    }
+
+    public boolean isSuccess() {
+        return SUCCESS == resultCode;
+    }
+
+    /** Pretty print */
+    @Override
+    public String toString() {
+        switch (resultCode) {
+            case SUCCESS: return "SUCCESS";
+            case ERROR_DATABASE_CANNOT_BE_OPENED: return "DATABASE CANNOT BE OPENED";
+            default: return "Unknown value ?!";
+        }
+    }
+}
diff --git a/core/java/android/net/ipmemorystore/StatusParcelable.aidl b/core/java/android/net/ipmemorystore/StatusParcelable.aidl
new file mode 100644
index 0000000..fb36ef4
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/StatusParcelable.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.net.ipmemorystore;
+
+/** {@hide} */
+parcelable StatusParcelable {
+  int resultCode;
+}
diff --git a/core/java/android/net/ipmemorystore/Utils.java b/core/java/android/net/ipmemorystore/Utils.java
new file mode 100644
index 0000000..73d8c83
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/Utils.java
@@ -0,0 +1,44 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/** {@hide} */
+public class Utils {
+    /** Pretty print */
+    public static String blobToString(final Blob blob) {
+        final StringBuilder sb = new StringBuilder("Blob : [");
+        if (blob.data.length <= 24) {
+            appendByteArray(sb, blob.data, 0, blob.data.length);
+        } else {
+            appendByteArray(sb, blob.data, 0, 16);
+            sb.append("...");
+            appendByteArray(sb, blob.data, blob.data.length - 8, blob.data.length);
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    // Adds the hex representation of the array between the specified indices (inclusive, exclusive)
+    private static void appendByteArray(@NonNull final StringBuilder sb, @NonNull final byte[] ar,
+            final int from, final int to) {
+        for (int i = from; i < to; ++i) {
+            sb.append(String.format("%02X", ar[i]));
+        }
+    }
+}
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
new file mode 100644
index 0000000..950f381
--- /dev/null
+++ b/core/java/android/os/AppZygote.java
@@ -0,0 +1,130 @@
+/*
+ * 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.content.pm.ApplicationInfo;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * AppZygote is responsible for interfacing with an application-specific zygote.
+ *
+ * Application zygotes can pre-load app-specific code and data, and this interface can
+ * be used to spawn isolated services from such an application zygote.
+ *
+ * Note that we'll have only one instance of this per application / uid combination.
+ *
+ * @hide
+ */
+public class AppZygote {
+    private static final String LOG_TAG = "AppZygote";
+
+    // UID of the Zygote itself
+    private final int mZygoteUid;
+
+    // First UID/GID of the range the AppZygote can setuid()/setgid() to
+    private final int mZygoteUidGidMin;
+
+    // Last UID/GID of the range the AppZygote can setuid()/setgid() to
+    private final int mZygoteUidGidMax;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Instance that maintains the socket connection to the zygote. This is {@code null} if the
+     * zygote is not running or is not connected.
+     */
+    @GuardedBy("mLock")
+    private ChildZygoteProcess mZygote;
+
+    private final ApplicationInfo mAppInfo;
+
+    public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax) {
+        mAppInfo = appInfo;
+        mZygoteUid = zygoteUid;
+        mZygoteUidGidMin = uidGidMin;
+        mZygoteUidGidMax = uidGidMax;
+    }
+
+    /**
+     * Returns the zygote process associated with this app zygote.
+     * Creates the process if it's not already running.
+     */
+    public ChildZygoteProcess getProcess() {
+        synchronized (mLock) {
+            if (mZygote != null) return mZygote;
+
+            connectToZygoteIfNeededLocked();
+            return mZygote;
+        }
+    }
+
+    /**
+     * Stops the Zygote and kills the zygote process.
+     */
+    public void stopZygote() {
+        synchronized (mLock) {
+            stopZygoteLocked();
+        }
+    }
+
+    public ApplicationInfo getAppInfo() {
+        return mAppInfo;
+    }
+
+    @GuardedBy("mLock")
+    private void stopZygoteLocked() {
+        if (mZygote != null) {
+            // Close the connection and kill the zygote process. This will not cause
+            // child processes to be killed by itself.
+            mZygote.close();
+            Process.killProcess(mZygote.getPid());
+            mZygote = null;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void connectToZygoteIfNeededLocked() {
+        String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi :
+                Build.SUPPORTED_ABIS[0];
+        try {
+            mZygote = Process.zygoteProcess.startChildZygote(
+                    "com.android.internal.os.AppZygoteInit",
+                    mAppInfo.processName + "_zygote",
+                    mZygoteUid,
+                    mZygoteUid,
+                    null,  // gids
+                    0,  // runtimeFlags
+                    "app_zygote",  // seInfo
+                    abi,  // abi
+                    abi, // acceptedAbiList
+                    null, // instructionSet
+                    mZygoteUidGidMin,
+                    mZygoteUidGidMax);
+
+            ZygoteProcess.waitForConnectionToZygote(mZygote.getPrimarySocketAddress());
+            // preload application code in the zygote
+            Log.i(LOG_TAG, "Starting application preload.");
+            mZygote.preloadApp(mAppInfo, abi);
+            Log.i(LOG_TAG, "Application preload done.");
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Error connecting to app zygote", e);
+            stopZygoteLocked();
+        }
+    }
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 9939a3c..1ebb551 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -273,6 +273,30 @@
     public static final native int getCallingUid();
 
     /**
+     * Returns {@code true} if the current thread is currently executing an
+     * incoming transaction.
+     *
+     * @hide
+     */
+    @CriticalNative
+    public static final native boolean isHandlingTransaction();
+
+    /**
+     * Return the Linux uid assigned to the process that sent the transaction
+     * currently being processed.
+     *
+     * @throws IllegalStateException if the current thread is not currently
+     *        executing an incoming transaction.
+     */
+    public static final int getCallingUidOrThrow() {
+        if (!isHandlingTransaction()) {
+            throw new IllegalStateException(
+                  "Thread is not in a binder transcation");
+        }
+        return getCallingUid();
+    }
+
+    /**
      * Return the UserHandle assigned to the process that sent you the
      * current transaction that is being processed.  This is the user
      * of the caller.  It is distinct from {@link #getCallingUid()} in that a
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
new file mode 100644
index 0000000..cbb3909
--- /dev/null
+++ b/core/java/android/os/BugreportManager.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2019 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.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.IBinder.DeathRecipient;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class that provides a privileged API to capture and consume bugreports.
+ *
+ * @hide
+ */
+// TODO: Expose API when the implementation is more complete.
+// @SystemApi
+@SystemService(Context.BUGREPORT_SERVICE)
+public class BugreportManager {
+    private final Context mContext;
+    private final IDumpstate mBinder;
+
+    /** @hide */
+    public BugreportManager(@NonNull Context context, IDumpstate binder) {
+        mContext = context;
+        mBinder = binder;
+    }
+
+    /**
+     * An interface describing the listener for bugreport progress and status.
+     */
+    public interface BugreportListener {
+        /**
+         * Called when there is a progress update.
+         * @param progress the progress in [0.0, 100.0]
+         */
+        void onProgress(float progress);
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = { "BUGREPORT_ERROR_" }, value = {
+                BUGREPORT_ERROR_INVALID_INPUT,
+                BUGREPORT_ERROR_RUNTIME
+        })
+
+        /** Possible error codes taking a bugreport can encounter */
+        @interface BugreportErrorCode {}
+
+        /** The input options were invalid */
+        int BUGREPORT_ERROR_INVALID_INPUT = 1;
+
+        /** A runtime error occured */
+        int BUGREPORT_ERROR_RUNTIME = 2;
+
+        /**
+         * Called when taking bugreport resulted in an error.
+         *
+         * @param errorCode the error that occurred. Possible values are
+         *     {@code BUGREPORT_ERROR_INVALID_INPUT}, {@code BUGREPORT_ERROR_RUNTIME}.
+         */
+        void onError(@BugreportErrorCode int errorCode);
+
+        /**
+         * Called when taking bugreport finishes successfully
+         *
+         * @param durationMs time capturing bugreport took in milliseconds
+         * @param title title for the bugreport; helpful in reminding the user why they took it
+         * @param description detailed description for the bugreport
+         */
+        void onFinished(long durationMs, @NonNull String title,
+                @NonNull String description);
+    }
+
+    /**
+     * Starts a bugreport asynchronously.
+     *
+     * @param bugreportFd file to write the bugreport. This should be opened in write-only,
+     *     append mode.
+     * @param screenshotFd file to write the screenshot, if necessary. This should be opened
+     *     in write-only, append mode.
+     * @param params options that specify what kind of a bugreport should be taken
+     * @param listener callback for progress and status updates
+     */
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    public void startBugreport(@NonNull FileDescriptor bugreportFd,
+            @Nullable FileDescriptor screenshotFd,
+            @NonNull BugreportParams params, @Nullable BugreportListener listener) {
+        // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
+        DumpstateListener dsListener = new DumpstateListener(listener);
+
+        try {
+            mBinder.startBugreport(bugreportFd, screenshotFd, params.getMode(), dsListener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private final class DumpstateListener extends IDumpstateListener.Stub
+            implements DeathRecipient {
+        private final BugreportListener mListener;
+
+        DumpstateListener(@Nullable BugreportListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            // TODO(b/111441001): implement
+        }
+
+        @Override
+        public void onProgress(int progress) throws RemoteException {
+            mListener.onProgress(progress);
+        }
+
+        @Override
+        public void onError(int errorCode) throws RemoteException {
+            mListener.onError(errorCode);
+        }
+
+        @Override
+        public void onFinished(long durationMs, String title, String description)
+                throws RemoteException {
+            mListener.onFinished(durationMs, title, description);
+        }
+
+        // Old methods; should go away
+        @Override
+        public void onProgressUpdated(int progress) throws RemoteException {
+            // TODO(b/111441001): remove from interface
+        }
+
+        @Override
+        public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
+            // TODO(b/111441001): remove from interface
+        }
+
+        @Override
+        public void onSectionComplete(String title, int status, int size, int durationMs)
+                throws RemoteException {
+            // TODO(b/111441001): remove from interface
+        }
+    }
+}
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
new file mode 100644
index 0000000..4e696ae
--- /dev/null
+++ b/core/java/android/os/BugreportParams.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Parameters that specify what kind of bugreport should be taken.
+ *
+ * @hide
+ */
+// TODO: Expose API when the implementation is more complete.
+// @SystemApi
+public final class BugreportParams {
+    private final int mMode;
+
+    public BugreportParams(@BugreportMode int mode) {
+        mMode = mode;
+    }
+
+    public int getMode() {
+        return mMode;
+    }
+
+    /**
+     * Defines acceptable types of bugreports.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "BUGREPORT_MODE_" }, value = {
+            BUGREPORT_MODE_FULL,
+            BUGREPORT_MODE_INTERACTIVE,
+            BUGREPORT_MODE_REMOTE,
+            BUGREPORT_MODE_WEAR,
+            BUGREPORT_MODE_TELEPHONY,
+            BUGREPORT_MODE_WIFI
+    })
+    public @interface BugreportMode {}
+
+    /**
+     * Options for a bugreport without user interference (and hence causing less
+     * interference to the system), but includes all sections.
+     */
+    public static final int BUGREPORT_MODE_FULL = IDumpstate.BUGREPORT_MODE_FULL;
+
+    /**
+     * Options that allow user to monitor progress and enter additional data; might not
+     * include all sections.
+     */
+    public static final int BUGREPORT_MODE_INTERACTIVE = IDumpstate.BUGREPORT_MODE_INTERACTIVE;
+
+    /**
+     * Options for a bugreport requested remotely by administrator of the Device Owner app,
+     * not the device's user.
+     */
+    public static final int BUGREPORT_MODE_REMOTE = IDumpstate.BUGREPORT_MODE_REMOTE;
+
+    /**
+     * Options for a bugreport on a wearable device.
+     */
+    public static final int BUGREPORT_MODE_WEAR = IDumpstate.BUGREPORT_MODE_WEAR;
+
+    /**
+     * Options for a lightweight version of bugreport that only includes a few, urgent
+     * sections used to report telephony bugs.
+     */
+    public static final int BUGREPORT_MODE_TELEPHONY = IDumpstate.BUGREPORT_MODE_TELEPHONY;
+
+    /**
+     * Options for a lightweight bugreport that only includes a few sections related to
+     * Wifi.
+     */
+    public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI;
+}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9fea873..2d61a4e 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -289,6 +289,26 @@
                 "ro.build.version.preview_sdk", 0);
 
         /**
+         * The SDK fingerprint for a given prerelease SDK. This value will always be
+         * {@code REL} on production platform builds/devices.
+         *
+         * <p>When this value is not {@code REL}, it contains a string fingerprint of the API
+         * surface exposed by the preview SDK. Preview platforms with different API surfaces
+         * will have different {@code PREVIEW_SDK_FINGERPRINT}.
+         *
+         * <p>This attribute is intended for use by installers for finer grained targeting of
+         * packages. Applications targeting preview APIs should not use this field and should
+         * instead use {@code PREVIEW_SDK_INT} or use reflection or other runtime checks to
+         * detect the presence of an API or guard themselves against unexpected runtime
+         * behavior.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String PREVIEW_SDK_FINGERPRINT = SystemProperties.get(
+                "ro.build.version.preview_sdk_fingerprint", "REL");
+
+        /**
          * The current development codename, or the string "REL" if this is
          * a release build.
          */
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index e84a518..ca39051 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -846,7 +846,16 @@
         return contains(dir.getAbsolutePath(), file.getAbsolutePath());
     }
 
-    /** {@hide} */
+    /**
+     * Test if a file lives under the given directory, either as a direct child
+     * or a distant grandchild.
+     * <p>
+     * Both files <em>must</em> have been resolved using
+     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+     * attacks.
+     *
+     * @hide
+     */
     public static boolean contains(String dirPath, String filePath) {
         if (dirPath.equals(filePath)) {
             return true;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index d979309..f51ba9a 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -306,9 +306,18 @@
                                                 String packageName,
                                                 String paths,
                                                 String devOptIn) {
-        // Check for temporary rules if debuggable or root
-        if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) {
-            Log.v(TAG, "Skipping loading temporary rules file");
+        /**
+         * We only want to load a temp rules file for:
+         *  - apps that are marked 'debuggable' in their manifest
+         *  - devices that are running a userdebug build (ro.debuggable) or can inject libraries for
+         *    debugging (PR_SET_DUMPABLE).
+         */
+        boolean appIsDebuggable = isDebuggable(context);
+        boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1;
+        if (!(appIsDebuggable || deviceIsDebuggable)) {
+            Log.v(TAG, "Skipping loading temporary rules file: "
+                    + "appIsDebuggable = " + appIsDebuggable + ", "
+                    + "adbRootEnabled = " + deviceIsDebuggable);
             return false;
         }
 
@@ -480,18 +489,38 @@
             return;
         }
 
-        String applicationPackageName = context.getPackageName();
-        String devOptInApplicationName = coreSettings.getString(
-                Settings.Global.GUP_DEV_OPT_IN_APPS);
-        boolean devOptIn = applicationPackageName.equals(devOptInApplicationName);
-        boolean whitelisted = onWhitelist(context, driverPackageName, ai.packageName);
-        if (!devOptIn && !whitelisted) {
+        // GUP_DEV_ALL_APPS
+        // 0: Default (Invalid values fallback to default as well)
+        // 1: All apps use Game Update Package
+        // 2: All apps use system graphics driver
+        int gupDevAllApps = coreSettings.getInt(Settings.Global.GUP_DEV_ALL_APPS, 0);
+        if (gupDevAllApps == 2) {
             if (DEBUG) {
-                Log.w(TAG, applicationPackageName + " is not on the whitelist.");
+                Log.w(TAG, "GUP is turned off on this device");
             }
             return;
         }
 
+        if (gupDevAllApps != 1) {
+            // GUP_DEV_OPT_OUT_APPS has higher priority than GUP_DEV_OPT_IN_APPS
+            if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS)
+                            .contains(ai.packageName)) {
+                if (DEBUG) {
+                    Log.w(TAG, ai.packageName + " opts out from GUP.");
+                }
+                return;
+            }
+
+            if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS)
+                            .contains(ai.packageName)
+                    && !onWhitelist(context, driverPackageName, ai.packageName)) {
+                if (DEBUG) {
+                    Log.w(TAG, ai.packageName + " is not on the whitelist.");
+                }
+                return;
+            }
+        }
+
         ApplicationInfo driverInfo;
         try {
             driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 3de3494..9e3e83e 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -24,6 +25,7 @@
 
 /** @hide */
 @SystemApi
+@TestApi
 public abstract class HwBinder implements IHwBinder {
     private static final String TAG = "HwBinder";
 
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 6a5bb1c..0ec63b5 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -28,6 +29,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public class HwBlob {
     private static final String TAG = "HwBlob";
 
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 7a51db2..7919a00 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -28,6 +29,7 @@
 
 /** @hide */
 @SystemApi
+@TestApi
 public class HwParcel {
     private static final String TAG = "HwParcel";
 
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 249eb3a..46fa6ef 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -17,9 +17,11 @@
 package android.os;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 /** @hide */
 @SystemApi
+@TestApi
 public interface IHwBinder {
     /**
      * Process a hwbinder transaction.
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index f9edd5b..0a5a715 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -17,8 +17,11 @@
 package android.os;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
+
 /** @hide */
 @SystemApi
+@TestApi
 public interface IHwInterface {
     /**
      * @return the binder object that corresponds to this interface.
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index fdd7488..8ced722 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -356,16 +356,6 @@
     void removeVpnUidRanges(int netId, in UidRange[] ranges);
 
     /**
-     * Start the clatd (464xlat) service on the given interface.
-     */
-    void startClatd(String interfaceName);
-
-    /**
-     * Stop the clatd (464xlat) service on the given interface.
-     */
-    void stopClatd(String interfaceName);
-
-    /**
      * Start listening for mobile activity state changes.
      */
     void registerNetworkActivityListener(INetworkActivityListener listener);
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index f7ffc37..f13bf5f 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.system.ErrnoException;
 import android.system.Os;
 
@@ -32,6 +33,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public final class NativeHandle implements Closeable {
     // whether this object owns mFds
     private boolean mOwn = false;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index cc6bb12..b9cdcc0 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -899,6 +899,33 @@
     }
 
     /**
+     * Flatten an {@link ArrayMap} with string keys containing a particular object
+     * type into the parcel at the current dataPosition() and growing dataCapacity()
+     * if needed. The type of the objects in the array must be one that implements
+     * Parcelable. Only the raw data of the objects is written and not their type,
+     * so you must use the corresponding {@link #createTypedArrayMap(Parcelable.Creator)}
+     *
+     * @param val The map of objects to be written.
+     * @param parcelableFlags The parcelable flags to use.
+     *
+     * @see #createTypedArrayMap(Parcelable.Creator)
+     * @see Parcelable
+     */
+    public <T extends Parcelable> void writeTypedArrayMap(@Nullable ArrayMap<String, T> val,
+            int parcelableFlags) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        final int count = val.size();
+        writeInt(count);
+        for (int i = 0; i < count; i++) {
+            writeString(val.keyAt(i));
+            writeTypedObject(val.valueAt(i), parcelableFlags);
+        }
+    }
+
+    /**
      * Write an array set to the parcel.
      *
      * @param val The array set to write.
@@ -1001,7 +1028,7 @@
      * values are written using {@link #writeValue} and must follow the
      * specification there.
      */
-    public final void writeSparseArray(@Nullable SparseArray<Object> val) {
+    public final <T> void writeSparseArray(@Nullable SparseArray<T> val) {
         if (val == null) {
             writeInt(-1);
             return;
@@ -1400,6 +1427,34 @@
     }
 
     /**
+     * Flatten a {@link SparseArray} containing a particular object type into the parcel
+     * at the current dataPosition() and growing dataCapacity() if needed. The
+     * type of the objects in the array must be one that implements Parcelable.
+     * Unlike the generic {@link #writeSparseArray(SparseArray)} method, however, only
+     * the raw data of the objects is written and not their type, so you must use the
+     * corresponding {@link #createTypedSparseArray(Parcelable.Creator)}.
+     *
+     * @param val The list of objects to be written.
+     * @param parcelableFlags The parcelable flags to use.
+     *
+     * @see #createTypedSparseArray(Parcelable.Creator)
+     * @see Parcelable
+     */
+    public final <T extends Parcelable> void writeTypedSparseArray(@Nullable SparseArray<T> val,
+            int parcelableFlags) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        final int count = val.size();
+        writeInt(count);
+        for (int i = 0; i < count; i++) {
+            writeInt(val.keyAt(i));
+            writeTypedObject(val.valueAt(i), parcelableFlags);
+        }
+    }
+
+    /**
      * @hide
      */
     public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) {
@@ -2369,7 +2424,7 @@
      * Parcelables.
      */
     @Nullable
-    public final SparseArray readSparseArray(@Nullable ClassLoader loader) {
+    public final <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
         int N = readInt();
         if (N < 0) {
             return null;
@@ -2466,6 +2521,62 @@
     }
 
     /**
+     * Read into a new {@link SparseArray} items containing a particular object type
+     * that were written with {@link #writeTypedSparseArray(SparseArray, int)} at the
+     * current dataPosition().  The list <em>must</em> have previously been written
+     * via {@link #writeTypedSparseArray(SparseArray, int)} with the same object type.
+     *
+     * @param creator The creator to use when for instantiation.
+     *
+     * @return A newly created {@link SparseArray} containing objects with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeTypedSparseArray(SparseArray, int)
+     */
+    public final @Nullable <T extends Parcelable> SparseArray<T> createTypedSparseArray(
+            @NonNull Parcelable.Creator<T> creator) {
+        final int count = readInt();
+        if (count < 0) {
+            return null;
+        }
+        final SparseArray<T> array = new SparseArray<>(count);
+        for (int i = 0; i < count; i++) {
+            final int index = readInt();
+            final T value = readTypedObject(creator);
+            array.append(index, value);
+        }
+        return array;
+    }
+
+    /**
+     * Read into a new {@link ArrayMap} with string keys items containing a particular
+     * object type that were written with {@link #writeTypedArrayMap(ArrayMap, int)} at the
+     * current dataPosition().  The list <em>must</em> have previously been written
+     * via {@link #writeTypedArrayMap(ArrayMap, int)} with the same object type.
+     *
+     * @param creator The creator to use when for instantiation.
+     *
+     * @return A newly created {@link ArrayMap} containing objects with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeTypedArrayMap(ArrayMap, int)
+     */
+    public final @Nullable <T extends Parcelable> ArrayMap<String, T> createTypedArrayMap(
+            @NonNull Parcelable.Creator<T> creator) {
+        final int count = readInt();
+        if (count < 0) {
+            return null;
+        }
+        final ArrayMap<String, T> map = new ArrayMap<>(count);
+        for (int i = 0; i < count; i++) {
+            final String key = readString();
+            final T value = readTypedObject(creator);
+            map.append(key, value);
+        }
+        return map;
+    }
+
+    /**
      * Read and return a new ArrayList containing String objects from
      * the parcel that was written with {@link #writeStringList} at the
      * current dataPosition().  Returns null if the
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/core/java/android/os/ParcelableException.aidl
similarity index 82%
copy from media/java/android/media/session/ParcelableVolumeInfo.aidl
copy to core/java/android/os/ParcelableException.aidl
index c4250f0..d214922 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.aidl
+++ b/core/java/android/os/ParcelableException.aidl
@@ -1,4 +1,4 @@
-/* Copyright 2014, The Android Open Source Project
+/* 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.
@@ -13,6 +13,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.os;
 
-parcelable ParcelableVolumeInfo;
+parcelable ParcelableException;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index c3e0489..0942d97 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1326,7 +1326,7 @@
      * @return Battery saver state data.
      *
      * @hide
-     * @see com.android.server.power.BatterySaverPolicy
+     * @see com.android.server.power.batterysaver.BatterySaverPolicy
      * @see PowerSaveState
      */
     public PowerSaveState getPowerSaveState(@ServiceType int serviceType) {
diff --git a/core/java/android/os/PowerSaveState.java b/core/java/android/os/PowerSaveState.java
index de1128df..4918ad1 100644
--- a/core/java/android/os/PowerSaveState.java
+++ b/core/java/android/os/PowerSaveState.java
@@ -27,7 +27,7 @@
     /**
      * Whether we should enable battery saver for this service.
      *
-     * @see com.android.server.power.BatterySaverPolicy
+     * @see com.android.server.power.batterysaver.BatterySaverPolicy
      */
     public final boolean batterySaverEnabled;
     /**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 938b23c..760fef7 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -205,15 +205,38 @@
     public static final int LAST_APPLICATION_UID = 19999;
 
     /**
+     * First uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    @TestApi
+    public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
+
+    /**
+     * Number of UIDs we allocate per application zygote
+     * @hide
+     */
+    @TestApi
+    public static final int NUM_UIDS_PER_APP_ZYGOTE = 100;
+
+    /**
+     * Last uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    @TestApi
+    public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;
+
+    /**
      * First uid used for fully isolated sandboxed processes (with no permissions of their own)
      * @hide
      */
+    @TestApi
     public static final int FIRST_ISOLATED_UID = 99000;
 
     /**
      * Last uid used for fully isolated sandboxed processes (with no permissions of their own)
      * @hide
      */
+    @TestApi
     public static final int LAST_ISOLATED_UID = 99999;
 
     /**
@@ -650,7 +673,8 @@
     /** {@hide} */
     public static final boolean isIsolated(int uid) {
         uid = UserHandle.getAppId(uid);
-        return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
+        return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID)
+                || (uid >= FIRST_APP_ZYGOTE_ISOLATED_UID && uid <= LAST_APP_ZYGOTE_ISOLATED_UID);
     }
 
     /**
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 6ea155f..68d6d85 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.annotation.NonNull;
+
 import com.android.internal.os.Zygote;
 
 import dalvik.annotation.optimization.FastNative;
@@ -95,6 +97,8 @@
     public static final long TRACE_TAG_AIDL = 1L << 24;
     /** @hide */
     public static final long TRACE_TAG_NNAPI = 1L << 25;
+    /** @hide */
+    public static final long TRACE_TAG_RRO = 1L << 26;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
     private static final int MAX_SECTION_NAME_LEN = 127;
@@ -311,7 +315,7 @@
      * @param sectionName The name of the code section to appear in the trace.  This may be at
      * most 127 Unicode code units long.
      */
-    public static void beginSection(String sectionName) {
+    public static void beginSection(@NonNull String sectionName) {
         if (isTagEnabled(TRACE_TAG_APP)) {
             if (sectionName.length() > MAX_SECTION_NAME_LEN) {
                 throw new IllegalArgumentException("sectionName is too long");
@@ -343,7 +347,7 @@
      * @param methodName The method name to appear in the trace.
      * @param cookie Unique identifier for distinguishing simultaneous events
      */
-    public static void beginAsyncSection(String methodName, int cookie) {
+    public static void beginAsyncSection(@NonNull String methodName, int cookie) {
         asyncTraceBegin(TRACE_TAG_APP, methodName, cookie);
     }
 
@@ -355,7 +359,7 @@
      * @param methodName The method name to appear in the trace.
      * @param cookie Unique identifier for distinguishing simultaneous events
      */
-    public static void endAsyncSection(String methodName, int cookie) {
+    public static void endAsyncSection(@NonNull String methodName, int cookie) {
         asyncTraceEnd(TRACE_TAG_APP, methodName, cookie);
     }
 
@@ -365,7 +369,7 @@
      * @param counterName The counter name to appear in the trace.
      * @param counterValue The counter value.
      */
-    public static void setCounter(String counterName, long counterValue) {
+    public static void setCounter(@NonNull String counterName, long counterValue) {
         if (isTagEnabled(TRACE_TAG_APP)) {
             nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
         }
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 8f2826c..1df3dad 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -66,6 +66,7 @@
         public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
         public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
         public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
+        public static final int PAYLOAD_TIMESTAMP_ERROR = 51;
         public static final int UPDATED_BUT_NOT_ACTIVE = 52;
     }
 
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index f8feb7b..ad8a4d5 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -138,8 +138,7 @@
      */
     public static boolean isIsolated(int uid) {
         if (uid > 0) {
-            final int appId = getAppId(uid);
-            return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID;
+            return Process.isIsolated(uid);
         } else {
             return false;
         }
@@ -294,9 +293,14 @@
             sb.append('u');
             sb.append(getUserId(uid));
             final int appId = getAppId(uid);
-            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
-                sb.append('i');
-                sb.append(appId - Process.FIRST_ISOLATED_UID);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    sb.append('i');
+                    sb.append(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    sb.append("ai");
+                    sb.append(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
             } else if (appId >= Process.FIRST_APPLICATION_UID) {
                 sb.append('a');
                 sb.append(appId - Process.FIRST_APPLICATION_UID);
@@ -330,9 +334,14 @@
             pw.print('u');
             pw.print(getUserId(uid));
             final int appId = getAppId(uid);
-            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
-                pw.print('i');
-                pw.print(appId - Process.FIRST_ISOLATED_UID);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    pw.print('i');
+                    pw.print(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    pw.print("ai");
+                    pw.print(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
             } else if (appId >= Process.FIRST_APPLICATION_UID) {
                 pw.print('a');
                 pw.print(appId - Process.FIRST_APPLICATION_UID);
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index b5aeba0..226d1d0 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -74,10 +74,13 @@
     private final String mPackageName;
     // The default vibration intensity level for haptic feedback.
     @VibrationIntensity
-    private final int mDefaultHapticFeedbackIntensity;
+    private int mDefaultHapticFeedbackIntensity;
     // The default vibration intensity level for notifications.
     @VibrationIntensity
-    private final int mDefaultNotificationVibrationIntensity;
+    private int mDefaultNotificationVibrationIntensity;
+    // The default vibration intensity level for ringtones.
+    @VibrationIntensity
+    private int mDefaultRingVibrationIntensity;
 
     /**
      * @hide to prevent subclassing from outside of the framework
@@ -85,10 +88,7 @@
     public Vibrator() {
         mPackageName = ActivityThread.currentPackageName();
         final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
-        mDefaultHapticFeedbackIntensity = loadDefaultIntensity(ctx,
-                com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
-        mDefaultNotificationVibrationIntensity = loadDefaultIntensity(ctx,
-                com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
+        loadVibrationIntensities(ctx);
     }
 
     /**
@@ -96,10 +96,16 @@
      */
     protected Vibrator(Context context) {
         mPackageName = context.getOpPackageName();
+        loadVibrationIntensities(context);
+    }
+
+    private void loadVibrationIntensities(Context context) {
         mDefaultHapticFeedbackIntensity = loadDefaultIntensity(context,
                 com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
         mDefaultNotificationVibrationIntensity = loadDefaultIntensity(context,
                 com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
+        mDefaultRingVibrationIntensity = loadDefaultIntensity(context,
+                com.android.internal.R.integer.config_defaultRingVibrationIntensity);
     }
 
     private int loadDefaultIntensity(Context ctx, int resId) {
@@ -115,13 +121,20 @@
     }
 
     /**
-     * Get the default vibration intensity for notifications and ringtones.
+     * Get the default vibration intensity for notifications.
      * @hide
      */
     public int getDefaultNotificationVibrationIntensity() {
         return mDefaultNotificationVibrationIntensity;
     }
 
+    /** Get the default vibration intensity for ringtones.
+     * @hide
+     */
+    public int getDefaultRingVibrationIntensity() {
+        return mDefaultRingVibrationIntensity;
+    }
+
     /**
      * Check whether the hardware has a vibrator.
      *
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index fb22194..23c54f4 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -16,23 +16,27 @@
 
 package android.os;
 
-import java.util.Map;
+import android.annotation.TestApi;
 
+import java.util.Map;
 
 /**
  * Java API for libvintf.
+ *
  * @hide
  */
+@TestApi
 public class VintfObject {
 
-    /// ---------- OTA
-
     /**
      * Slurps all device information (both manifests and both matrices)
      * and report them.
      * If any error in getting one of the manifests, it is not included in
      * the list.
+     *
+     * @hide
      */
+    @TestApi
     public static native String[] report();
 
     /**
@@ -44,6 +48,8 @@
      * @return = 0 if success (compatible)
      *         > 0 if incompatible
      *         < 0 if any error (mount partition fails, illformed XML, etc.)
+     *
+     * @hide
      */
     public static native int verify(String[] packageInfo);
 
@@ -55,22 +61,28 @@
      * @return = 0 if success (compatible)
      *         > 0 if incompatible
      *         < 0 if any error (mount partition fails, illformed XML, etc.)
+     *
+     * @hide
      */
     public static native int verifyWithoutAvb();
 
-    /// ---------- CTS Device Info
-
     /**
      * @return a list of HAL names and versions that is supported by this
      * device as stated in device and framework manifests. For example,
      * ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0",
      *  "android.hardware.camera.device@3.2"]. There are no duplicates.
+     *
+     * @hide
      */
+    @TestApi
     public static native String[] getHalNamesAndVersions();
 
     /**
      * @return the BOARD_SEPOLICY_VERS build flag available in device manifest.
+     *
+     * @hide
      */
+    @TestApi
     public static native String getSepolicyVersion();
 
     /**
@@ -78,13 +90,22 @@
      * specified in framework manifest. For example,
      * [("27", ["libjpeg.so", "libbase.so"]),
      *  ("28", ["libjpeg.so", "libbase.so"])]
+     *
+     * @hide
      */
+    @TestApi
     public static native Map<String, String[]> getVndkSnapshots();
 
     /**
-     * @return target FCM version, a number specified in the device manifest
-     * indicating the FCM version that the device manifest implements. Null if
-     * device manifest doesn't specify this number (for legacy devices).
+     * @return Target Framework Compatibility Matrix (FCM) version, a number
+     * specified in the device manifest indicating the FCM version that the
+     * device manifest implements. Null if device manifest doesn't specify this
+     * number (for legacy devices).
+     *
+     * @hide
      */
+    @TestApi
     public static native Long getTargetFrameworkCompatibilityMatrixVersion();
+
+    private VintfObject() {}
 }
diff --git a/core/java/android/os/VintfRuntimeInfo.java b/core/java/android/os/VintfRuntimeInfo.java
index 29698b9..f17039b 100644
--- a/core/java/android/os/VintfRuntimeInfo.java
+++ b/core/java/android/os/VintfRuntimeInfo.java
@@ -16,55 +16,84 @@
 
 package android.os;
 
+import android.annotation.TestApi;
+
 /**
  * Java API for ::android::vintf::RuntimeInfo. Methods return null / 0 on any error.
  *
  * @hide
  */
+@TestApi
 public class VintfRuntimeInfo {
 
     private VintfRuntimeInfo() {}
 
     /**
      * @return /sys/fs/selinux/policyvers, via security_policyvers() native call
+     *
+     * @hide
      */
     public static native long getKernelSepolicyVersion();
     /**
      * @return content of /proc/cpuinfo
+     *
+     * @hide
      */
+    @TestApi
     public static native String getCpuInfo();
     /**
      * @return os name extracted from uname() native call
+     *
+     * @hide
      */
+    @TestApi
     public static native String getOsName();
     /**
      * @return node name extracted from uname() native call
+     *
+     * @hide
      */
+    @TestApi
     public static native String getNodeName();
     /**
      * @return os release extracted from uname() native call
+     *
+     * @hide
      */
+    @TestApi
     public static native String getOsRelease();
     /**
      * @return os version extracted from uname() native call
+     *
+     * @hide
      */
+    @TestApi
     public static native String getOsVersion();
     /**
      * @return hardware id extracted from uname() native call
+     *
+     * @hide
      */
+    @TestApi
     public static native String getHardwareId();
     /**
      * @return kernel version extracted from uname() native call. Format is
      * {@code x.y.z}.
+     *
+     * @hide
      */
+    @TestApi
     public static native String getKernelVersion();
     /**
      * @return libavb version in OS. Format is {@code x.y}.
+     *
+     * @hide
      */
     public static native String getBootAvbVersion();
     /**
      * @return libavb version in bootloader. Format is {@code x.y}.
+     *
+     * @hide
      */
     public static native String getBootVbmetaAvbVersion();
-
 }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index f136cd6..99a85ea 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.util.Log;
@@ -34,6 +35,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
@@ -673,6 +675,36 @@
     }
 
     /**
+     * Instructs the zygote to pre-load the application code for the given Application.
+     * Only the app zygote supports this function.
+     * TODO preloadPackageForAbi() can probably be removed and the callers an use this instead.
+     */
+    public boolean preloadApp(ApplicationInfo appInfo, String abi) throws ZygoteStartFailedEx,
+                                                                          IOException {
+        synchronized (mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            state.writer.write("2");
+            state.writer.newLine();
+
+            state.writer.write("--preload-app");
+            state.writer.newLine();
+
+            // Zygote args needs to be strings, so in order to pass ApplicationInfo,
+            // write it to a Parcel, and base64 the raw Parcel bytes to the other side.
+            Parcel parcel = Parcel.obtain();
+            appInfo.writeToParcel(parcel, 0 /* flags */);
+            String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
+            parcel.recycle();
+            state.writer.write(encodedParcelData);
+            state.writer.newLine();
+
+            state.writer.flush();
+
+            return (state.inputStream.readInt() == 0);
+        }
+    }
+
+    /**
      * Instructs the zygote to pre-load the classes and native libraries at the given paths
      * for the specified abi. Not all zygotes support this function.
      */
@@ -763,6 +795,22 @@
      * secondary zygotes that inherit data from the zygote that this object
      * communicates with. This returns a new ZygoteProcess representing a connection
      * to the newly created zygote. Throws an exception if the zygote cannot be started.
+     *
+     * @param processClass The class to use as the child zygote's main entry
+     *                     point.
+     * @param niceName A more readable name to use for the process.
+     * @param uid The user-id under which the child zygote will run.
+     * @param gid The group-id under which the child zygote will run.
+     * @param gids Additional group-ids associated with the child zygote process.
+     * @param runtimeFlags Additional flags.
+     * @param seInfo null-ok SELinux information for the child zygote process.
+     * @param abi non-null the ABI of the child zygote
+     * @param acceptedAbiList ABIs this child zygote will accept connections for; this
+     *                        may be different from <code>abi</code> in case the children
+     *                        spawned from this Zygote only communicate using ABI-safe methods.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param uidRangeStart The first UID in the range the child zygote may setuid()/setgid() to
+     * @param uidRangeEnd The last UID in the range the child zygote may setuid()/setgid() to
      */
     public ChildZygoteProcess startChildZygote(final String processClass,
                                                final String niceName,
@@ -770,12 +818,18 @@
                                                int runtimeFlags,
                                                String seInfo,
                                                String abi,
-                                               String instructionSet) {
+                                               String acceptedAbiList,
+                                               String instructionSet,
+                                               int uidRangeStart,
+                                               int uidRangeEnd) {
         // Create an unguessable address in the global abstract namespace.
         final LocalSocketAddress serverAddress = new LocalSocketAddress(
                 processClass + "/" + UUID.randomUUID().toString());
 
-        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
+        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(),
+                                    Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList,
+                                    Zygote.CHILD_ZYGOTE_UID_RANGE_START + uidRangeStart,
+                                    Zygote.CHILD_ZYGOTE_UID_RANGE_END + uidRangeEnd};
 
         Process.ProcessStartResult result;
         try {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 9594a71..735f4f2 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1540,6 +1540,7 @@
     }
 
     /** {@hide} */
+    @SystemApi
     @TestApi
     public static boolean hasIsolatedStorage() {
         // Prefer to use snapshot for current boot when available
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 38951d5..249b622 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -17,6 +17,9 @@
 package android.permission;
 
 import android.os.RemoteCallback;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
 
 /**
  * Interface for system apps to communication with the permission controller.
@@ -24,8 +27,12 @@
  * @hide
  */
 oneway interface IPermissionController {
+    void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason,
+            String callerPackageName, in RemoteCallback callback);
+    void getRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe);
     void getAppPermissions(String packageName, in RemoteCallback callback);
     void revokeRuntimePermission(String packageName, String permissionName);
     void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
             boolean countSystem, in RemoteCallback callback);
+    void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback);
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 66e8666..bfcca7c 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -18,50 +18,131 @@
 
 import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
 
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
 import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.util.Preconditions;
 
+import libcore.io.IoUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
- * Interface for communicating with the permission controller from system apps. All UI operations
- * regarding permissions and any changes to the permission state should flow through this
- * interface.
+ * Interface for communicating with the permission controller.
  *
  * @hide
  */
+@TestApi
+@SystemApi
 @SystemService(Context.PERMISSION_CONTROLLER_SERVICE)
 public final class PermissionControllerManager {
     private static final String TAG = PermissionControllerManager.class.getSimpleName();
 
+    private static final Object sLock = new Object();
+
+    /** App global remote service used by all {@link PermissionControllerManager managers} */
+    @GuardedBy("sLock")
+    private static RemoteService sRemoteService;
+
     /**
      * The key for retrieving the result from the returned bundle.
+     *
+     * @hide
      */
     public static final String KEY_RESULT =
             "android.permission.PermissionControllerManager.key.result";
 
+    /** @hide */
+    @IntDef(prefix = { "REASON_" }, value = {
+            REASON_MALWARE,
+            REASON_INSTALLER_POLICY_VIOLATION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Reason {}
+
+    /** The permissions are revoked because the apps holding the permissions are malware */
+    public static final int REASON_MALWARE = 1;
+
+    /**
+     * The permissions are revoked because the apps holding the permissions violate a policy of the
+     * app that installed it.
+     *
+     * <p>If this reason is used only permissions of apps that are installed by the caller of the
+     * API can be revoked.
+     */
+    public static final int REASON_INSTALLER_POLICY_VIOLATION = 2;
+
+    /**
+     * Callback for delivering the result of {@link #revokeRuntimePermissions}.
+     */
+    public abstract static class OnRevokeRuntimePermissionsCallback {
+        /**
+         * The result for {@link #revokeRuntimePermissions}.
+         *
+         * @param revoked The actually revoked permissions as
+         *                {@code Map<packageName, List<permission>>}
+         */
+        public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked);
+    }
+
+    /**
+     * Callback for delivering the result of {@link #getRuntimePermissionBackup}.
+     *
+     * @hide
+     */
+    public interface OnGetRuntimePermissionBackupCallback {
+        /**
+         * The result for {@link #getRuntimePermissionBackup}.
+         *
+         * @param backup The backup file
+         */
+        void onGetRuntimePermissionsBackup(@NonNull byte[] backup);
+    }
+
     /**
      * Callback for delivering the result of {@link #getAppPermissions}.
+     *
+     * @hide
      */
     public interface OnGetAppPermissionResultCallback {
         /**
@@ -75,6 +156,8 @@
 
     /**
      * Callback for delivering the result of {@link #countPermissionApps}.
+     *
+     * @hide
      */
     public interface OnCountPermissionAppsResultCallback {
         /**
@@ -86,15 +169,95 @@
         void onCountPermissionApps(int numApps);
     }
 
-    private final RemoteService mRemoteService;
+    /**
+     * Callback for delivering the result of {@link #getPermissionUsages}.
+     *
+     * @hide
+     */
+    public interface OnPermissionUsageResultCallback {
+        /**
+         * The result for {@link #getPermissionUsages}.
+         *
+         * @param users The users list.
+         */
+        void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> users);
+    }
 
+    private final @NonNull Context mContext;
+
+    /**
+     * Create a new {@link PermissionControllerManager}.
+     *
+     * @param context to create the manager for
+     *
+     * @hide
+     */
     public PermissionControllerManager(@NonNull Context context) {
-        Intent intent = new Intent(SERVICE_INTERFACE);
-        intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
-        ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+        synchronized (sLock) {
+            if (sRemoteService == null) {
+                Intent intent = new Intent(SERVICE_INTERFACE);
+                intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
+                ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
 
-        mRemoteService = new RemoteService(context,
-                serviceInfo.getComponentInfo().getComponentName());
+                sRemoteService = new RemoteService(context.getApplicationContext(),
+                        serviceInfo.getComponentInfo().getComponentName());
+            }
+        }
+
+        mContext = context;
+    }
+
+    /**
+     * Revoke a set of runtime permissions for various apps.
+     *
+     * @param request The permissions to revoke as {@code Map<packageName, List<permission>>}
+     * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
+     * @param reason Why the permission should be revoked
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     */
+    @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+    public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request,
+            boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnRevokeRuntimePermissionsCallback callback) {
+        // Check input to fail immediately instead of inside the async request
+        checkNotNull(executor);
+        checkNotNull(callback);
+        checkNotNull(request);
+        for (Map.Entry<String, List<String>> appRequest : request.entrySet()) {
+            checkNotNull(appRequest.getKey());
+            checkCollectionElementsNotNull(appRequest.getValue(), "permissions");
+        }
+
+        // Check required permission to fail immediately instead of inside the oneway binder call
+        if (mContext.checkSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+                    + " required");
+        }
+
+        sRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(sRemoteService,
+                request, doDryRun, reason, mContext.getPackageName(), executor, callback));
+    }
+
+    /**
+     * Create a backup of the runtime permissions.
+     *
+     * @param user The user to be backed up
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void getRuntimePermissionBackup(@NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnGetRuntimePermissionBackupCallback callback) {
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        sRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(sRemoteService,
+                user, executor, callback));
     }
 
     /**
@@ -103,6 +266,8 @@
      * @param packageName The package for which to query.
      * @param callback Callback to receive the result.
      * @param handler Handler on which to invoke the callback.
+     *
+     * @hide
      */
     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
     public void getAppPermissions(@NonNull String packageName,
@@ -110,8 +275,8 @@
         checkNotNull(packageName);
         checkNotNull(callback);
 
-        mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
-                packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
+        sRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(sRemoteService,
+                packageName, callback, handler == null ? sRemoteService.getHandler() : handler));
     }
 
     /**
@@ -119,6 +284,8 @@
      *
      * @param packageName The package for which to revoke
      * @param permissionName The permission to revoke
+     *
+     * @hide
      */
     @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
     public void revokeRuntimePermission(@NonNull String packageName,
@@ -126,7 +293,7 @@
         checkNotNull(packageName);
         checkNotNull(permissionName);
 
-        mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
+        sRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
                 permissionName));
     }
 
@@ -138,6 +305,8 @@
      * @param countSystem Also count system apps
      * @param callback Callback to receive the result
      * @param handler Handler on which to invoke the callback
+     *
+     * @hide
      */
     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
     public void countPermissionApps(@NonNull List<String> permissionNames,
@@ -146,9 +315,31 @@
         checkCollectionElementsNotNull(permissionNames, "permissionNames");
         checkNotNull(callback);
 
-        mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
+        sRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(sRemoteService,
                 permissionNames, countOnlyGranted, countSystem, callback,
-                handler == null ? mRemoteService.getHandler() : handler));
+                handler == null ? sRemoteService.getHandler() : handler));
+    }
+
+    /**
+     * Count how many apps have used permissions.
+     *
+     * @param countSystem Also count system apps
+     * @param numMillis The number of milliseconds in the past to check for uses
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void getPermissionUsages(boolean countSystem, long numMillis,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPermissionUsageResultCallback callback) {
+        checkArgumentNonnegative(numMillis);
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        sRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(sRemoteService,
+                countSystem, numMillis, executor, callback));
     }
 
     /**
@@ -206,6 +397,237 @@
     }
 
     /**
+     * Task to read a large amount of data from a remote service.
+     */
+    private static class FileReaderTask<Callback extends Consumer<byte[]>>
+            extends AsyncTask<Void, Void, byte[]> {
+        private ParcelFileDescriptor mLocalPipe;
+        private ParcelFileDescriptor mRemotePipe;
+
+        private final @NonNull Callback mCallback;
+
+        FileReaderTask(@NonNull Callback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            ParcelFileDescriptor[] pipe;
+            try {
+                pipe = ParcelFileDescriptor.createPipe();
+            } catch (IOException e) {
+                Log.e(TAG, "Could not create pipe needed to get runtime permission backup", e);
+                return;
+            }
+
+            mLocalPipe = pipe[0];
+            mRemotePipe = pipe[1];
+        }
+
+        /**
+         * Get the file descriptor the remote service should write the data to.
+         *
+         * <p>Needs to be closed <u>locally</u> before the FileReader can finish.
+         *
+         * @return The file the data should be written to
+         */
+        ParcelFileDescriptor getRemotePipe() {
+            return mRemotePipe;
+        }
+
+        @Override
+        protected byte[] doInBackground(Void... ignored) {
+            ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream();
+
+            try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mLocalPipe)) {
+                byte[] buffer = new byte[16 * 1024];
+
+                while (!isCancelled()) {
+                    int numRead = in.read(buffer);
+                    if (numRead == -1) {
+                        break;
+                    }
+
+                    combinedBuffer.write(buffer, 0, numRead);
+                }
+            } catch (IOException | NullPointerException e) {
+                Log.e(TAG, "Error reading runtime permission backup", e);
+                combinedBuffer.reset();
+            }
+
+            return combinedBuffer.toByteArray();
+        }
+
+        /**
+         * Interrupt the reading of the data.
+         *
+         * <p>Needs to be called when canceling this task as it might be hung.
+         */
+        void interruptRead() {
+            IoUtils.closeQuietly(mLocalPipe);
+        }
+
+        @Override
+        protected void onCancelled() {
+            onPostExecute(new byte[]{});
+        }
+
+        @Override
+        protected void onPostExecute(byte[] backup) {
+            IoUtils.closeQuietly(mLocalPipe);
+            mCallback.accept(backup);
+        }
+    }
+
+    /**
+     * Request for {@link #revokeRuntimePermissions}
+     */
+    private static final class PendingRevokeRuntimePermissionRequest extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+        private final @NonNull Map<String, List<String>> mRequest;
+        private final boolean mDoDryRun;
+        private final int mReason;
+        private final @NonNull String mCallingPackage;
+        private final @NonNull Executor mExecutor;
+        private final @NonNull OnRevokeRuntimePermissionsCallback mCallback;
+
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingRevokeRuntimePermissionRequest(@NonNull RemoteService service,
+                @NonNull Map<String, List<String>> request, boolean doDryRun,
+                @Reason int reason, @NonNull String callingPackage,
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnRevokeRuntimePermissionsCallback callback) {
+            super(service);
+
+            mRequest = request;
+            mDoDryRun = doDryRun;
+            mReason = reason;
+            mCallingPackage = callingPackage;
+            mExecutor = executor;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    Map<String, List<String>> revoked = new ArrayMap<>();
+                    try {
+                        Bundle bundleizedRevoked = result.getBundle(KEY_RESULT);
+
+                        for (String packageName : bundleizedRevoked.keySet()) {
+                            Preconditions.checkNotNull(packageName);
+
+                            ArrayList<String> permissions =
+                                    bundleizedRevoked.getStringArrayList(packageName);
+                            Preconditions.checkCollectionElementsNotNull(permissions,
+                                    "permissions");
+
+                            revoked.put(packageName, permissions);
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Could not read result when revoking runtime permissions", e);
+                    }
+
+                    callback.onRevokeRuntimePermissions(revoked);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+
+                    finish();
+                }
+            }), null);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(
+                        () -> mCallback.onRevokeRuntimePermissions(Collections.emptyMap()));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void run() {
+            Bundle bundledizedRequest = new Bundle();
+            for (Map.Entry<String, List<String>> appRequest : mRequest.entrySet()) {
+                bundledizedRequest.putStringArrayList(appRequest.getKey(),
+                        new ArrayList<>(appRequest.getValue()));
+            }
+
+            try {
+                getService().getServiceInterface().revokeRuntimePermissions(bundledizedRequest,
+                        mDoDryRun, mReason, mCallingPackage, mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error revoking runtime permission", e);
+            }
+        }
+    }
+
+    /**
+     * Request for {@link #getRuntimePermissionBackup}
+     */
+    private static final class PendingGetRuntimePermissionBackup extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController>
+            implements Consumer<byte[]> {
+        private final @NonNull FileReaderTask<PendingGetRuntimePermissionBackup> mBackupReader;
+        private final @NonNull Executor mExecutor;
+        private final @NonNull OnGetRuntimePermissionBackupCallback mCallback;
+        private final @NonNull UserHandle mUser;
+
+        private PendingGetRuntimePermissionBackup(@NonNull RemoteService service,
+                @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnGetRuntimePermissionBackupCallback callback) {
+            super(service);
+
+            mUser = user;
+            mExecutor = executor;
+            mCallback = callback;
+
+            mBackupReader = new FileReaderTask<>(this);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mBackupReader.cancel(true);
+            mBackupReader.interruptRead();
+        }
+
+        @Override
+        public void run() {
+            mBackupReader.execute();
+
+            ParcelFileDescriptor remotePipe = mBackupReader.getRemotePipe();
+            try {
+                getService().getServiceInterface().getRuntimePermissionBackup(mUser, remotePipe);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error getting runtime permission backup", e);
+            } finally {
+                // Remote pipe end is duped by binder call. Local copy is not needed anymore
+                IoUtils.closeQuietly(remotePipe);
+            }
+        }
+
+        /**
+         * Called when the {@link #mBackupReader} finished reading the file.
+         *
+         * @param backup The data read
+         */
+        @Override
+        public void accept(byte[] backup) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onGetRuntimePermissionsBackup(backup));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+            finish();
+        }
+    }
+
+    /**
      * Request for {@link #getAppPermissions}
      */
     private static final class PendingGetAppPermissionRequest extends
@@ -331,4 +753,61 @@
             }
         }
     }
+
+    /**
+     * Request for {@link #getPermissionUsages}
+     */
+    private static final class PendingGetPermissionUsagesRequest extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+        private final @NonNull OnPermissionUsageResultCallback mCallback;
+        private final boolean mCountSystem;
+        private final long mNumMillis;
+
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingGetPermissionUsagesRequest(@NonNull RemoteService service,
+                boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnPermissionUsageResultCallback callback) {
+            super(service);
+
+            mCountSystem = countSystem;
+            mNumMillis = numMillis;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    final List<RuntimePermissionUsageInfo> reportedUsers;
+                    List<RuntimePermissionUsageInfo> users = null;
+                    if (result != null) {
+                        users = result.getParcelableArrayList(KEY_RESULT);
+                    } else {
+                        users = Collections.emptyList();
+                    }
+                    reportedUsers = users;
+
+                    callback.onPermissionUsageResult(reportedUsers);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+
+                    finish();
+                }
+            }), null);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mCallback.onPermissionUsageResult(Collections.emptyList());
+        }
+
+        @Override
+        public void run() {
+            try {
+                getService().getServiceInterface().getPermissionUsages(mCountSystem, mNumMillis,
+                        mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error counting permission users", e);
+            }
+        }
+    }
 }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 5dad071..10e8c8d 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -16,6 +16,8 @@
 
 package android.permission;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -26,12 +28,24 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This service is meant to be implemented by the app controlling permissions.
@@ -42,6 +56,7 @@
  */
 @SystemApi
 public abstract class PermissionControllerService extends Service {
+    private static final String LOG_TAG = PermissionControllerService.class.getSimpleName();
 
     /**
      * The {@link Intent} action that must be declared as handled by a service
@@ -60,6 +75,29 @@
     }
 
     /**
+     * Revoke a set of runtime permissions for various apps.
+     *
+     * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>}
+     * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
+     * @param reason Why the permission should be revoked
+     * @param callerPackageName The package name of the calling app
+     *
+     * @return the actually removed permissions as {@code Map<packageName, List<permission>>}
+     */
+    public abstract @NonNull Map<String, List<String>> onRevokeRuntimePermissions(
+            @NonNull Map<String, List<String>> requests, boolean doDryRun,
+            @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName);
+
+    /**
+     * Create a backup of the runtime permissions.
+     *
+     * @param user The user to back up
+     * @param out The stream to write the backup to
+     */
+    public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
+            @NonNull OutputStream out);
+
+    /**
      * Gets the runtime permissions for an app.
      *
      * @param packageName The package for which to query.
@@ -90,10 +128,68 @@
     public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
             boolean countOnlyGranted, boolean countSystem);
 
+    /**
+     * Count how many apps have used permissions.
+     *
+     * @param countSystem Also count system apps
+     * @param numMillis The number of milliseconds in the past to check for uses
+     *
+     * @return descriptions of the users of permissions
+     */
+    public abstract @NonNull List<RuntimePermissionUsageInfo>
+            onPermissionUsageResult(boolean countSystem, long numMillis);
+
     @Override
     public final IBinder onBind(Intent intent) {
         return new IPermissionController.Stub() {
             @Override
+            public void revokeRuntimePermissions(
+                    Bundle bundleizedRequest, boolean doDryRun, int reason,
+                    String callerPackageName, RemoteCallback callback) {
+                checkNotNull(bundleizedRequest, "bundleizedRequest");
+                checkNotNull(callerPackageName);
+                checkNotNull(callback);
+
+                Map<String, List<String>> request = new ArrayMap<>();
+                for (String packageName : bundleizedRequest.keySet()) {
+                    Preconditions.checkNotNull(packageName);
+
+                    ArrayList<String> permissions =
+                            bundleizedRequest.getStringArrayList(packageName);
+                    Preconditions.checkCollectionElementsNotNull(permissions, "permissions");
+
+                    request.put(packageName, permissions);
+                }
+
+                enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+
+                // Verify callerPackageName
+                try {
+                    PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0);
+                    checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid);
+                } catch (PackageManager.NameNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+
+                mHandler.sendMessage(obtainMessage(
+                        PermissionControllerService::revokeRuntimePermissions,
+                        PermissionControllerService.this, request, doDryRun, reason,
+                        callerPackageName, callback));
+            }
+
+            @Override
+            public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
+                checkNotNull(user);
+                checkNotNull(pipe);
+
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
+                mHandler.sendMessage(obtainMessage(
+                        PermissionControllerService::getRuntimePermissionsBackup,
+                        PermissionControllerService.this, user, pipe));
+            }
+
+            @Override
             public void getAppPermissions(String packageName, RemoteCallback callback) {
                 checkNotNull(packageName, "packageName");
                 checkNotNull(callback, "callback");
@@ -130,9 +226,53 @@
                                 PermissionControllerService.this, permissionNames, countOnlyGranted,
                                 countSystem, callback));
             }
+
+            @Override
+            public void getPermissionUsages(boolean countSystem, long numMillis,
+                    RemoteCallback callback) {
+                checkArgumentNonnegative(numMillis);
+                checkNotNull(callback, "callback");
+
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
+                mHandler.sendMessage(
+                        obtainMessage(PermissionControllerService::getPermissionUsages,
+                                PermissionControllerService.this, countSystem, numMillis,
+                                callback));
+            }
         };
     }
 
+    private void revokeRuntimePermissions(@NonNull Map<String, List<String>> requests,
+            boolean doDryRun, @PermissionControllerManager.Reason int reason,
+            @NonNull String callerPackageName, @NonNull RemoteCallback callback) {
+        Map<String, List<String>> revoked = onRevokeRuntimePermissions(requests,
+                doDryRun, reason, callerPackageName);
+
+        checkNotNull(revoked);
+        Bundle bundledizedRevoked = new Bundle();
+        for (Map.Entry<String, List<String>> appRevocation : revoked.entrySet()) {
+            checkNotNull(appRevocation.getKey());
+            checkCollectionElementsNotNull(appRevocation.getValue(), "permissions");
+
+            bundledizedRevoked.putStringArrayList(appRevocation.getKey(),
+                    new ArrayList<>(appRevocation.getValue()));
+        }
+
+        Bundle result = new Bundle();
+        result.putBundle(PermissionControllerManager.KEY_RESULT, bundledizedRevoked);
+        callback.sendResult(result);
+    }
+
+    private void getRuntimePermissionsBackup(@NonNull UserHandle user,
+            @NonNull ParcelFileDescriptor outFile) {
+        try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(outFile)) {
+            onGetRuntimePermissionsBackup(user, out);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Could not open pipe to write backup tp", e);
+        }
+    }
+
     private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
         List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
         if (permissions != null && !permissions.isEmpty()) {
@@ -152,4 +292,17 @@
         result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
         callback.sendResult(result);
     }
+
+    private void getPermissionUsages(boolean countSystem, long numMillis,
+            @NonNull RemoteCallback callback) {
+        List<RuntimePermissionUsageInfo> users =
+                onPermissionUsageResult(countSystem, numMillis);
+        if (users != null && !users.isEmpty()) {
+            Bundle result = new Bundle();
+            result.putParcelableList(PermissionControllerManager.KEY_RESULT, users);
+            callback.sendResult(result);
+        } else {
+            callback.sendResult(null);
+        }
+    }
 }
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.aidl b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
new file mode 100644
index 0000000..88820dd
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2019, 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.permission;
+
+parcelable RuntimePermissionUsageInfo;
\ No newline at end of file
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.java b/core/java/android/permission/RuntimePermissionUsageInfo.java
new file mode 100644
index 0000000..af1a1be
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 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.permission;
+
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is used. A single runtime permission presented to the user may
+ * correspond to multiple platform defined permissions, e.g. the
+ * location permission may control both the coarse and fine platform
+ * permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionUsageInfo implements Parcelable {
+    private final @NonNull CharSequence mName;
+    private final int mNumUsers;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name The permission group name.
+     * @param numUsers The number of apps that have used this permission.
+     */
+    public RuntimePermissionUsageInfo(@NonNull CharSequence name, int numUsers) {
+        checkNotNull(name);
+        checkArgumentNonnegative(numUsers);
+
+        mName = name;
+        mNumUsers = numUsers;
+    }
+
+    private RuntimePermissionUsageInfo(Parcel parcel) {
+        this(parcel.readCharSequence(), parcel.readInt());
+    }
+
+    /**
+     * @return The number of apps that accessed this permission
+     */
+    public int getAppAccessCount() {
+        return mNumUsers;
+    }
+
+    /**
+     * Gets the permission group name.
+     *
+     * @return The name.
+     */
+    public @NonNull CharSequence getName() {
+        return mName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeCharSequence(mName);
+        parcel.writeInt(mNumUsers);
+    }
+
+    public static final Creator<RuntimePermissionUsageInfo> CREATOR =
+            new Creator<RuntimePermissionUsageInfo>() {
+        public RuntimePermissionUsageInfo createFromParcel(Parcel source) {
+            return new RuntimePermissionUsageInfo(source);
+        }
+
+        public RuntimePermissionUsageInfo[] newArray(int size) {
+            return new RuntimePermissionUsageInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING
new file mode 100644
index 0000000..ba9f36a
--- /dev/null
+++ b/core/java/android/permission/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+    "presubmit": [
+        {
+            "name": "CtsPermissionTestCases",
+            "options": [
+                {
+                    "include-filter": "android.permission.cts.PermissionControllerTest"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4e207ed..9015703 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -16,9 +16,13 @@
 
 package android.provider;
 
+import static android.Manifest.permission.READ_DEVICE_CONFIG;
+import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.content.ContentResolver;
@@ -48,6 +52,40 @@
      */
     public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
 
+    /**
+     * Namespace for all Game Driver features.
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_GAME_DRIVER = "game_driver";
+
+    /**
+     * Namespace for autofill feature that provides suggestions across all apps when
+     * the user interacts with input fields.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_AUTOFILL = "autofill";
+
+    /**
+     * Namespace for content capture feature used by on-device machine intelligence
+     * to provide suggestions in a privacy-safe manner.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+
+    /**
+     * Namespace for all input-related features that are used at the native level.
+     * These features are applied at reboot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
@@ -69,6 +107,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(READ_DEVICE_CONFIG)
     public static String getProperty(String namespace, String name) {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
         String compositeName = createCompositeName(namespace, name);
@@ -96,6 +135,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static boolean setProperty(
             String namespace, String name, String value, boolean makeDefault) {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
@@ -116,6 +156,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
         Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
@@ -137,10 +178,12 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(READ_DEVICE_CONFIG)
     public static void addOnPropertyChangedListener(
             @NonNull String namespace,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnPropertyChangedListener onPropertyChangedListener) {
+        // TODO enforce READ_DEVICE_CONFIG permission
         synchronized (sLock) {
             Pair<String, Executor> oldNamespace = sListeners.get(onPropertyChangedListener);
             if (oldNamespace == null) {
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index e5fd29c..b348da4 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -409,6 +409,15 @@
         public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
 
         /**
+         * Similar to {@link #COLUMN_MEDIAPROVIDER_URI}, except this cannot be updated/queried
+         * by apps and will be the source of truth when updating/deleting download entries in
+         * MediaProvider database.
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
+
+        /**
          * The column that is used to remember whether the media scanner was invoked.
          * It can take the values: null or 0(not scanned), 1(scanned), 2 (not scannable).
          * <P>Type: TEXT</P>
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index 8e37559..e931826 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -34,6 +34,7 @@
 import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
+import android.os.Build.VERSION_CODES;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -651,7 +652,16 @@
                 if (familyBuilder == null) {
                     familyBuilder = new FontFamily.Builder(font);
                 } else {
-                    familyBuilder.addFont(font);
+                    try {
+                        familyBuilder.addFont(font);
+                    } catch (IllegalArgumentException e) {
+                        if (context.getApplicationInfo().targetSdkVersion <= VERSION_CODES.P) {
+                            // Surpress the IllegalArgumentException for keeping the backward
+                            // compatibility.
+                            continue;
+                        }
+                        throw e;
+                    }
                 }
             } catch (IOException e) {
                 continue;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index c4e2b12..cdbc979 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -17,6 +17,7 @@
 package android.provider;
 
 import android.annotation.BytesLong;
+import android.annotation.DurationMillisLong;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -55,6 +56,7 @@
 import android.os.storage.VolumeInfo;
 import android.service.media.CameraPrewarmService;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -131,6 +133,8 @@
     /** {@hide} */
     public static final String PARAM_INCLUDE_PENDING = "includePending";
     /** {@hide} */
+    public static final String PARAM_INCLUDE_TRASHED = "includeTrashed";
+    /** {@hide} */
     public static final String PARAM_PROGRESS = "progress";
     /** {@hide} */
     public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
@@ -485,12 +489,29 @@
      * By default no pending items are returned.
      *
      * @see MediaColumns#IS_PENDING
+     * @see MediaStore#setIncludePending(Uri)
+     * @see MediaStore#createPending(Context, PendingParams)
      */
     public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
         return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_PENDING, "1").build();
     }
 
     /**
+     * Update the given {@link Uri} to also include any trashed media items from
+     * calls such as
+     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+     * By default no trashed items are returned.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
+        return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
+    }
+
+    /**
      * Update the given {@link Uri} to indicate that the caller requires the
      * original file contents when calling
      * {@link ContentResolver#openFileDescriptor(Uri, String)}.
@@ -516,6 +537,9 @@
      *
      * @return token which can be passed to {@link #openPending(Context, Uri)}
      *         to work with this pending item.
+     * @see MediaColumns#IS_PENDING
+     * @see MediaStore#setIncludePending(Uri)
+     * @see MediaStore#createPending(Context, PendingParams)
      */
     public static @NonNull Uri createPending(@NonNull Context context,
             @NonNull PendingParams params) {
@@ -572,6 +596,8 @@
             this.insertValues.put(MediaColumns.DATE_ADDED, now);
             this.insertValues.put(MediaColumns.DATE_MODIFIED, now);
             this.insertValues.put(MediaColumns.IS_PENDING, 1);
+            this.insertValues.put(MediaColumns.DATE_EXPIRES,
+                    (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000);
         }
 
         /**
@@ -696,6 +722,7 @@
         public @NonNull Uri publish() {
             final ContentValues values = new ContentValues();
             values.put(MediaColumns.IS_PENDING, 0);
+            values.putNull(MediaColumns.DATE_EXPIRES);
             mContext.getContentResolver().update(mUri, values, null, null);
             return mUri;
         }
@@ -717,6 +744,67 @@
     }
 
     /**
+     * Mark the given item as being "trashed", meaning it should be deleted at
+     * some point in the future. This is a more gentle operation than simply
+     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
+     * would take effect immediately.
+     * <p>
+     * This method preserves trashed items for at least 48 hours before erasing
+     * them, giving the user a chance to untrash the item.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void trash(@NonNull Context context, @NonNull Uri uri) {
+        trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS);
+    }
+
+    /**
+     * Mark the given item as being "trashed", meaning it should be deleted at
+     * some point in the future. This is a more gentle operation than simply
+     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
+     * would take effect immediately.
+     * <p>
+     * This method preserves trashed items for at least the given timeout before
+     * erasing them, giving the user a chance to untrash the item.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void trash(@NonNull Context context, @NonNull Uri uri,
+            @DurationMillisLong long timeoutMillis) {
+        if (timeoutMillis < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_TRASHED, 1);
+        values.put(MediaColumns.DATE_EXPIRES,
+                (System.currentTimeMillis() + timeoutMillis) / 1000);
+        context.getContentResolver().update(uri, values, null, null);
+    }
+
+    /**
+     * Mark the given item as being "untrashed", meaning it should no longer be
+     * deleted as previously requested through {@link #trash(Context, Uri)}.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void untrash(@NonNull Context context, @NonNull Uri uri) {
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_TRASHED, 0);
+        values.putNull(MediaColumns.DATE_EXPIRES);
+        context.getContentResolver().update(uri, values, null, null);
+    }
+
+    /**
      * Common fields for most MediaProvider tables
      */
     public interface MediaColumns extends BaseColumns {
@@ -821,12 +909,34 @@
          * <p>
          * Type: BOOLEAN
          *
+         * @see MediaColumns#IS_PENDING
+         * @see MediaStore#setIncludePending(Uri)
          * @see MediaStore#createPending(Context, PendingParams)
-         * @see MediaStore#PARAM_INCLUDE_PENDING
          */
         public static final String IS_PENDING = "is_pending";
 
         /**
+         * Flag indicating if a media item is trashed.
+         * <p>
+         * Type: BOOLEAN
+         *
+         * @see MediaColumns#IS_TRASHED
+         * @see MediaStore#setIncludeTrashed(Uri)
+         * @see MediaStore#trash(Context, Uri)
+         * @see MediaStore#untrash(Context, Uri)
+         */
+        public static final String IS_TRASHED = "is_trashed";
+
+        /**
+         * The time the file should be considered expired. Units are seconds
+         * since 1970. Typically only meaningful in the context of
+         * {@link #IS_PENDING} or {@link #IS_TRASHED}.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String DATE_EXPIRES = "date_expires";
+
+        /**
          * The width of the image/video in pixels.
          */
         public static final String WIDTH = "width";
@@ -915,6 +1025,11 @@
             return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("dir").build();
         }
 
+        /** @hide */
+        public static final Uri getContentUriForPath(String path) {
+            return getContentUri(getVolumeNameForPath(path));
+        }
+
         /**
          * Fields for master table for all media files.
          * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
@@ -1021,6 +1136,13 @@
          * Type: TEXT
          */
         String REFERER_URI = "referer_uri";
+
+        /**
+         * The description of the download.
+         * <p>
+         * Type: Text
+         */
+        String DESCRIPTION = "description";
     }
 
     /**
@@ -1251,18 +1373,32 @@
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
-             * The bucket id of the image. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket ID of this media item. This can be useful to
+             * present the user a first-level clustering of related media items.
+             * This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
              */
             public static final String BUCKET_ID = "bucket_id";
 
             /**
-             * The bucket display name of the image. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket display name of this media item. This can be
+             * useful to present the user a first-level clustering of related
+             * media items. This is a read-only column that is automatically
+             * computed.
+             * <p>
+             * Type: TEXT
              */
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
+
+            /**
+             * The secondary bucket ID of this media item. This can be useful to
+             * present the user a second-level clustering of related media
+             * items. This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
+             */
+            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
         }
 
         public static final class Media implements ImageColumns {
@@ -2500,20 +2636,34 @@
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
-             * The bucket id of the video. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket ID of this media item. This can be useful to
+             * present the user a first-level clustering of related media items.
+             * This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
              */
             public static final String BUCKET_ID = "bucket_id";
 
             /**
-             * The bucket display name of the video. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket display name of this media item. This can be
+             * useful to present the user a first-level clustering of related
+             * media items. This is a read-only column that is automatically
+             * computed.
+             * <p>
+             * Type: TEXT
              */
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
+             * The secondary bucket ID of this media item. This can be useful to
+             * present the user a second-level clustering of related media
+             * items. This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
+             */
+            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
+
+            /**
              * The bookmark for the video. Time in ms. Represents the location in the video that the
              * video should start playing at the next time it is opened. If the value is null or
              * out of the range 0..DURATION-1 then the video should start playing from the
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bb1784a..d5de13c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -86,7 +86,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.MemoryIntArray;
-import android.view.textservice.TextServicesManager;
+import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ColorDisplayController;
@@ -1190,7 +1190,8 @@
     /**
      * Activity Action: Show Do Not Disturb access settings.
      * <p>
-     * Users can grant and deny access to Do Not Disturb configuration from here.
+     * Users can grant and deny access to Do Not Disturb configuration from here. Managed
+     * profiles cannot grant Do Not Disturb access.
      * See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more
      * details.
      * <p>
@@ -3377,6 +3378,22 @@
          */
         public static final String NOTIFICATION_VIBRATION_INTENSITY =
                 "notification_vibration_intensity";
+        /**
+         * The intensity of ringtone vibrations, if configurable.
+         *
+         * Not all devices are capable of changing their vibration intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String RING_VIBRATION_INTENSITY =
+                "ring_vibration_intensity";
 
         /**
          * The intensity of haptic feedback vibrations, if configurable.
@@ -4246,6 +4263,7 @@
             ACCELEROMETER_ROTATION,
             SHOW_BATTERY_PERCENT,
             NOTIFICATION_VIBRATION_INTENSITY,
+            RING_VIBRATION_INTENSITY,
             HAPTIC_FEEDBACK_INTENSITY,
             DISPLAY_COLOR_MODE,
             ALARM_ALERT,
@@ -4397,6 +4415,7 @@
             VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
             VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+            VALIDATORS.put(RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
             VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
             VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR);
@@ -5030,10 +5049,6 @@
         public static boolean putStringForUser(@NonNull ContentResolver resolver,
                 @NonNull String name, @Nullable String value, @Nullable String tag,
                 boolean makeDefault, @UserIdInt int userHandle) {
-            if (LOCATION_MODE.equals(name)) {
-                // Map LOCATION_MODE to underlying location provider storage API
-                return setLocationModeForUser(resolver, Integer.parseInt(value), userHandle);
-            }
             if (MOVED_TO_GLOBAL.contains(name)) {
                 Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
                         + " to android.provider.Settings.Global");
@@ -5186,10 +5201,6 @@
         /** @hide */
         @UnsupportedAppUsage
         public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
-            if (LOCATION_MODE.equals(name)) {
-                // Map from to underlying location provider storage API to location mode
-                return getLocationModeForUser(cr, userHandle);
-            }
             String v = getStringForUser(cr, name, userHandle);
             try {
                 return v != null ? Integer.parseInt(v) : def;
@@ -5224,10 +5235,6 @@
         /** @hide */
         public static int getIntForUser(ContentResolver cr, String name, int userHandle)
                 throws SettingNotFoundException {
-            if (LOCATION_MODE.equals(name)) {
-                // Map from to underlying location provider storage API to location mode
-                return getLocationModeForUser(cr, userHandle);
-            }
             String v = getStringForUser(cr, name, userHandle);
             try {
                 return Integer.parseInt(v);
@@ -5810,9 +5817,8 @@
          * this value being present in settings.db or on ContentObserver notifications on the
          * corresponding Uri.
          *
-         * @deprecated use {@link #LOCATION_MODE} and
-         * {@link LocationManager#MODE_CHANGED_ACTION} (or
-         * {@link LocationManager#PROVIDERS_CHANGED_ACTION})
+         * @deprecated Providers should not be controlled individually. See {@link #LOCATION_MODE}
+          * documentation for information on reading/writing location information.
          */
         @Deprecated
         public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
@@ -5830,9 +5836,7 @@
          * notifications for the corresponding Uri. Use {@link LocationManager#MODE_CHANGED_ACTION}
          * to receive changes in this value.
          *
-         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
-         *             get the status of a location provider, use
-         *             {@link LocationManager#isProviderEnabled(String)}.
+         * @deprecated To check location mode, use {@link LocationManager#isLocationEnabled()}.
          */
         @Deprecated
         public static final String LOCATION_MODE = "location_mode";
@@ -5861,9 +5865,7 @@
         /**
          * Location access disabled.
          *
-         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
-         *             get the status of a location provider, use
-         *             {@link LocationManager#isProviderEnabled(String)}.
+         * @deprecated See {@link #LOCATION_MODE}.
          */
         @Deprecated
         public static final int LOCATION_MODE_OFF = 0;
@@ -5883,9 +5885,7 @@
          * with {@link android.location.Criteria#POWER_HIGH} may be downgraded to
          * {@link android.location.Criteria#POWER_MEDIUM}.
          *
-         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
-         *             get the status of a location provider, use
-         *             {@link LocationManager#isProviderEnabled(String)}.
+         * @deprecated See {@link #LOCATION_MODE}.
          */
         @Deprecated
         public static final int LOCATION_MODE_BATTERY_SAVING = 2;
@@ -5893,9 +5893,7 @@
         /**
          * Best-effort location computation allowed.
          *
-         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
-         *             get the status of a location provider, use
-         *             {@link LocationManager#isProviderEnabled(String)}.
+         * @deprecated See {@link #LOCATION_MODE}.
          */
         @Deprecated
         public static final int LOCATION_MODE_HIGH_ACCURACY = 3;
@@ -6019,6 +6017,21 @@
                 "lock_screen_allow_remote_input";
 
         /**
+         * Indicates which clock face to show on lock screen and AOD.
+         * @hide
+         */
+        public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
+
+        private static final Validator LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR =
+                ANY_STRING_VALIDATOR;
+
+        /**
+         * Indicates which clock face to show on lock screen and AOD while docked.
+         * @hide
+         */
+        private static final String DOCKED_CLOCK_FACE = "docked_clock_face";
+
+        /**
          * Set by the system to track if the user needs to see the call to action for
          * the lockscreen notification policy.
          * @hide
@@ -6207,7 +6220,7 @@
         public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
 
         /**
-         * Whether to vibrate for wireless charging events.
+         * Whether to vibrate for charging events.
          * @hide
          */
         public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled";
@@ -7384,6 +7397,14 @@
         private static final Validator DOZE_DOUBLE_TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * Whether the device should respond to the SLPI tap gesture.
+         * @hide
+         */
+        public static final String DOZE_TAP_SCREEN_GESTURE = "doze_tap_gesture";
+
+        private static final Validator DOZE_TAP_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * Gesture that wakes up the lock screen.
          * @hide
          */
@@ -7400,6 +7421,22 @@
         private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * Gesture that skips media.
+         * @hide
+         */
+        public static final String SKIP_GESTURE = "skip_gesture";
+
+        private static final Validator SKIP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
+         * Gesture that silences sound (alarms, notification, calls).
+         * @hide
+         */
+        public static final String SILENCE_GESTURE = "silence_gesture";
+
+        private static final Validator SILENCE_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
@@ -7701,6 +7738,23 @@
         public static final String TV_INPUT_CUSTOM_LABELS = "tv_input_custom_labels";
 
         /**
+         * Whether TV app uses non-system inputs.
+         *
+         * <p>
+         * The value is boolean (1 or 0), where 1 means non-system TV inputs are allowed,
+         * and 0 means non-system TV inputs are not allowed.
+         *
+         * <p>
+         * Devices such as sound bars may have changed the system property allow_third_party_inputs
+         * to false so the TV Application only uses HDMI and other built in inputs. This setting
+         * allows user to override the default and have the TV Application use third party TV inputs
+         * available on play store.
+         *
+         * @hide
+         */
+        public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs";
+
+        /**
          * Whether automatic routing of system audio to USB audio peripheral is disabled.
          * The value is boolean (1 or 0), where 1 means automatic routing is disabled,
          * and 0 means automatic routing is enabled.
@@ -7835,6 +7889,19 @@
                 BOOLEAN_VALIDATOR;
 
         /**
+         * Whether or not face unlock always requires user confirmation, meaning {@link
+         * android.hardware.biometrics.BiometricPrompt.Builder#setRequireConfirmation(boolean)}
+         * is always 'true'. This overrides the behavior that apps choose in the
+         * setRequireConfirmation API.
+         * @hide
+         */
+        public static final String FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
+                "face_unlock_always_require_confirmation";
+
+        private static final Validator FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
+        /**
          * Whether the assist gesture should be enabled.
          *
          * @hide
@@ -8425,12 +8492,14 @@
             DOZE_ALWAYS_ON,
             DOZE_PICK_UP_GESTURE,
             DOZE_DOUBLE_TAP_GESTURE,
+            DOZE_TAP_SCREEN_GESTURE,
             DOZE_WAKE_LOCK_SCREEN_GESTURE,
             DOZE_WAKE_SCREEN_GESTURE,
             NFC_PAYMENT_DEFAULT_COMPONENT,
             AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
             FACE_UNLOCK_KEYGUARD_ENABLED,
             FACE_UNLOCK_APP_ENABLED,
+            FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
             ASSIST_GESTURE_ENABLED,
             ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
             ASSIST_GESTURE_WAKE_ENABLED,
@@ -8448,6 +8517,7 @@
             HUSH_GESTURE_USED,
             IN_CALL_NOTIFICATION_ENABLED,
             LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+            LOCK_SCREEN_CUSTOM_CLOCK_FACE,
             LOCK_SCREEN_SHOW_NOTIFICATIONS,
             ZEN_DURATION,
             SHOW_ZEN_UPGRADE_NOTIFICATION,
@@ -8461,6 +8531,8 @@
             NOTIFICATION_NEW_INTERRUPTION_MODEL,
             TRUST_AGENTS_EXTEND_UNLOCK,
             LOCK_SCREEN_WHEN_TRUST_LOST,
+            SKIP_GESTURE,
+            SILENCE_GESTURE,
         };
 
         /**
@@ -8577,6 +8649,7 @@
             VALIDATORS.put(DOZE_ALWAYS_ON, DOZE_ALWAYS_ON_VALIDATOR);
             VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
+            VALIDATORS.put(DOZE_TAP_SCREEN_GESTURE, DOZE_TAP_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
@@ -8584,6 +8657,8 @@
                     AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
             VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR);
             VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
+            VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+                    FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
                     ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
@@ -8624,7 +8699,10 @@
             VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_NEW_INTERRUPTION_MODEL, BOOLEAN_VALIDATOR);
             VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR);
+            VALIDATORS.put(LOCK_SCREEN_CUSTOM_CLOCK_FACE, LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR);
             VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR);
+            VALIDATORS.put(SKIP_GESTURE, SKIP_GESTURE_VALIDATOR);
+            VALIDATORS.put(SILENCE_GESTURE, SILENCE_GESTURE_VALIDATOR);
         }
 
         /**
@@ -8652,14 +8730,14 @@
             CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_ENABLED);
             CLONE_TO_MANAGED_PROFILE.add(ALLOW_MOCK_LOCATION);
             CLONE_TO_MANAGED_PROFILE.add(ALLOWED_GEOLOCATION_ORIGINS);
-            CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
             CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
-            CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_CHANGER);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED);
-            CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
-            if (TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER) {
+            if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
+                CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
+                CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
+                CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
                 CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER);
                 CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE);
             }
@@ -8778,84 +8856,6 @@
                         userId);
             }
         }
-
-        /**
-         * Thread-safe method for setting the location mode to one of
-         * {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
-         * {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
-         * Necessary because the mode is a composite of the underlying location provider
-         * settings.
-         *
-         * @param cr the content resolver to use
-         * @param mode such as {@link #LOCATION_MODE_HIGH_ACCURACY}
-         * @param userId the userId for which to change mode
-         * @return true if the value was set, false on database errors
-         *
-         * @throws IllegalArgumentException if mode is not one of the supported values
-         *
-         * @deprecated To enable/disable location, use
-         *             {@link LocationManager#setLocationEnabledForUser(boolean, int)}.
-         *             To enable/disable a specific location provider, use
-         *             {@link LocationManager#setProviderEnabledForUser(String, boolean, int)}.
-         */
-        @Deprecated
-        private static boolean setLocationModeForUser(
-                ContentResolver cr, int mode, int userId) {
-            synchronized (mLocationSettingsLock) {
-                boolean gps = false;
-                boolean network = false;
-                switch (mode) {
-                    case LOCATION_MODE_OFF:
-                        break;
-                    case LOCATION_MODE_SENSORS_ONLY:
-                        gps = true;
-                        break;
-                    case LOCATION_MODE_BATTERY_SAVING:
-                        network = true;
-                        break;
-                    case LOCATION_MODE_HIGH_ACCURACY:
-                        gps = true;
-                        network = true;
-                        break;
-                    default:
-                        throw new IllegalArgumentException("Invalid location mode: " + mode);
-                }
-
-                boolean nlpSuccess = Settings.Secure.setLocationProviderEnabledForUser(
-                        cr, LocationManager.NETWORK_PROVIDER, network, userId);
-                boolean gpsSuccess = Settings.Secure.setLocationProviderEnabledForUser(
-                        cr, LocationManager.GPS_PROVIDER, gps, userId);
-                return gpsSuccess && nlpSuccess;
-            }
-        }
-
-        /**
-         * Thread-safe method for reading the location mode, returns one of
-         * {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
-         * {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}. Necessary
-         * because the mode is a composite of the underlying location provider settings.
-         *
-         * @param cr the content resolver to use
-         * @param userId the userId for which to read the mode
-         * @return the location mode
-         */
-        private static final int getLocationModeForUser(ContentResolver cr, int userId) {
-            synchronized (mLocationSettingsLock) {
-                boolean gpsEnabled = Settings.Secure.isLocationProviderEnabledForUser(
-                        cr, LocationManager.GPS_PROVIDER, userId);
-                boolean networkEnabled = Settings.Secure.isLocationProviderEnabledForUser(
-                        cr, LocationManager.NETWORK_PROVIDER, userId);
-                if (gpsEnabled && networkEnabled) {
-                    return LOCATION_MODE_HIGH_ACCURACY;
-                } else if (gpsEnabled) {
-                    return LOCATION_MODE_SENSORS_ONLY;
-                } else if (networkEnabled) {
-                    return LOCATION_MODE_BATTERY_SAVING;
-                } else {
-                    return LOCATION_MODE_OFF;
-                }
-            }
-        }
     }
 
     /**
@@ -8882,6 +8882,14 @@
         public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked";
 
         /**
+         * Whether applying ramping ringer on incoming phone call ringtone.
+         * <p>1 = apply ramping ringer
+         * <p>0 = do not apply ramping ringer
+         * @hide
+         */
+        public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
+
+        /**
          * Setting whether the global gesture for enabling accessibility is enabled.
          * If this gesture is enabled the user will be able to perfrom it to enable
          * the accessibility state without visiting the settings app.
@@ -9406,6 +9414,15 @@
                 "hdmi_system_audio_control_enabled";
 
         /**
+         * Whether HDMI Routing Control feature is enabled. If enabled, the switch device will
+         * route to the correct input source on receiving Routing Control related messages. If
+         * disabled, you can only switch the input via controls on this device.
+         * @hide
+         */
+        public static final String HDMI_CEC_SWITCH_ENABLED =
+                "hdmi_cec_switch_enabled";
+
+        /**
          * Whether TV will automatically turn on upon reception of the CEC command
          * &lt;Text View On&gt; or &lt;Image View On&gt;. (0 = false, 1 = true)
          *
@@ -10941,6 +10958,21 @@
         public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
 
         /**
+         * Whether to try cellular data recovery when a bad network is reported.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_RECOVERY_ON_BAD_NETWORK =
+                "data_stall_recovery_on_bad_network";
+
+        /**
+         * Minumim duration in millisecodns between cellular data recovery attempts
+         *
+         * @hide
+         */
+        public static final String MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS =
+                "min_duration_between_recovery_steps";
+        /**
          * Whether network service discovery is enabled.
          *
          * @hide
@@ -11107,6 +11139,31 @@
         /** {@hide} */
         public static final String
                 BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_";
+        /**
+         * Enable/disable radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection";
+
+        /**
+         * Count threshold of RIL wakelock timeout for radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD =
+                "radio_bug_wakelock_timeout_count_threshold";
+
+        /**
+         * Count threshold of RIL system error for radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD =
+                "radio_bug_system_error_count_threshold";
 
         /**
          * Activity manager specific settings.
@@ -11254,14 +11311,14 @@
          * quick_doze_enabled                (boolean)
          * </pre>
          * @hide
-         * @see com.android.server.power.BatterySaverPolicy
+         * @see com.android.server.power.batterysaver.BatterySaverPolicy
          */
         public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
 
         /**
          * Battery Saver device specific settings
          * This is encoded as a key=value list, separated by commas.
-         * See {@link com.android.server.power.BatterySaverPolicy} for the details.
+         * See {@link com.android.server.power.batterysaver.BatterySaverPolicy} for the details.
          *
          * @hide
          */
@@ -11597,6 +11654,7 @@
          * battery_level_collection_delay_ms (long)
          * max_history_files (int)
          * max_history_buffer_kb (int)
+         * battery_charged_delay_ms (int)
          * </pre>
          *
          * <p>
@@ -11982,16 +12040,33 @@
                 "angle_gl_driver_selection_values";
 
         /**
-         * Apps that are selected to use Game Update Package.
+         * Game Update Package global preference for all Apps.
+         * 0 = Default
+         * 1 = All Apps use Game Update Package
+         * 2 = All Apps use system graphics driver
+         * @hide
+         */
+        public static final String GUP_DEV_ALL_APPS = "gup_dev_all_apps";
+
+        /**
+         * List of Apps selected to use Game Update Package.
+         * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
         public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps";
 
         /**
-         * Apps on the black list that are forbidden to useGame Update Package.
+         * List of Apps selected not to use Game Update Package.
+         * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
-        public static final String GUP_BLACK_LIST = "gup_black_list";
+        public static final String GUP_DEV_OPT_OUT_APPS = "gup_dev_opt_out_apps";
+
+        /**
+         * Apps on the blacklist that are forbidden to use Game Update Package.
+         * @hide
+         */
+        public static final String GUP_BLACKLIST = "gup_blacklist";
 
         /**
          * Ordered GPU debug layer list for Vulkan
@@ -13811,6 +13886,8 @@
          * enabled                         (boolean)
          * requires_targeting_p            (boolean)
          * max_squeeze_remeasure_attempts  (int)
+         * edit_choices_before_sending     (boolean)
+         * show_in_heads_up                (boolean)
          * </pre>
          * @see com.android.systemui.statusbar.policy.SmartReplyConstants
          * @hide
@@ -13995,6 +14072,53 @@
          */
         public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
                 "native_flags_health_check_enabled";
+
+        /**
+         * Parameter for {@link #APPOP_HISTORY_PARAMETERS} that controls the mode
+         * in which the historical registry operates.
+         *
+         * @hide
+         */
+        public static final String APPOP_HISTORY_MODE = "mode";
+
+        /**
+         * Parameter for {@link #APPOP_HISTORY_PARAMETERS} that controls how long
+         * is the interval between snapshots in the base case i.e. the most recent
+         * part of the history.
+         *
+         * @hide
+         */
+        public static final String APPOP_HISTORY_BASE_INTERVAL_MILLIS = "baseIntervalMillis";
+
+        /**
+         * Parameter for {@link #APPOP_HISTORY_PARAMETERS} that controls the base
+         * for the logarithmic step when building app op history.
+         *
+         * @hide
+         */
+        public static final String APPOP_HISTORY_INTERVAL_MULTIPLIER = "intervalMultiplier";
+
+        /**
+         * Appop history parameters. These parameters are represented by
+         * a comma-delimited key-value list.
+         *
+         * The following strings are supported as keys:
+         * <pre>
+         *     mode                  (int)
+         *     baseIntervalMillis    (long)
+         *     intervalMultiplier    (int)
+         * </pre>
+         *
+         * Ex: "enabled=true,baseIntervalMillis=1000,intervalMultiplier=10"
+         *
+         * @see #APPOP_HISTORY_MODE
+         * @see #APPOP_HISTORY_BASE_INTERVAL_MILLIS
+         * @see #APPOP_HISTORY_INTERVAL_MULTIPLIER
+         *
+         * @hide
+         */
+        public static final String APPOP_HISTORY_PARAMETERS =
+                "appop_history_parameters";
     }
 
     /**
@@ -14023,7 +14147,7 @@
          *
          * @hide
          */
-        // TODO(b/117663715): require a new read permission
+        @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
         static String getString(ContentResolver resolver, String name) {
             return sNameValueCache.getStringForUser(resolver, name, resolver.getUserId());
         }
@@ -14046,8 +14170,7 @@
          *
          * @hide
          */
-        // TODO(b/117663715): require a new write permission restricted to a single source
-        @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+        @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
         static boolean putString(@NonNull ContentResolver resolver, @NonNull String name,
                 @Nullable String value, boolean makeDefault) {
             return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault,
@@ -14069,7 +14192,7 @@
          * @hide
          */
         // TODO(b/117663715): require a new write permission restricted to a single source
-        @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+        @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
         static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
                 @Nullable String prefix) {
             try {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 31a5962..c43a666 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -533,7 +533,10 @@
      *     service.
      * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
      *     screen is required to generate recoverable keys.
+     *
+     * @deprecated Use the method {@link #generateKey(String, byte[])} instead.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public @NonNull Key generateKey(@NonNull String alias) throws InternalRecoveryServiceException,
             LockScreenRequiredException {
@@ -556,6 +559,47 @@
     }
 
     /**
+     * Generates a recoverable key with the given {@code alias} and {@code metadata}.
+     *
+     * <p>The metadata should contain any data that needs to be cryptographically bound to the
+     * generated key, but does not need to be encrypted by the key. For example, the metadata can
+     * be a byte string describing the algorithms and non-secret parameters to be used with the
+     * key. The supplied metadata can later be obtained via
+     * {@link WrappedApplicationKey#getMetadata()}.
+     *
+     * <p>During the key recovery process, the same metadata has to be supplied via
+     * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process
+     * will fail due to the checking of the cryptographic binding. This can help prevent
+     * potential attacks that try to swap key materials on the backup server and trick the
+     * application to use keys with different algorithms or parameters.
+     *
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+     *     screen is required to generate recoverable keys.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull Key generateKey(@NonNull String alias, @Nullable byte[] metadata)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        try {
+            String grantAlias = mBinder.generateKeyWithMetadata(alias, metadata);
+            if (grantAlias == null) {
+                throw new InternalRecoveryServiceException("null grant alias");
+            }
+            return getKeyFromGrant(grantAlias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (UnrecoverableKeyException e) {
+            throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_INSECURE_USER) {
+                throw new LockScreenRequiredException(e.getMessage());
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
      * Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code
      * keyBytes}.
      *
@@ -564,7 +608,9 @@
      * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
      *     screen is required to generate recoverable keys.
      *
+     * @deprecated Use the method {@link #importKey(String, byte[], byte[])} instead.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
@@ -587,6 +633,49 @@
     }
 
     /**
+     * Imports a recoverable 256-bit AES key with the given {@code alias}, the raw bytes {@code
+     * keyBytes}, and the {@code metadata}.
+     *
+     * <p>The metadata should contain any data that needs to be cryptographically bound to the
+     * imported key, but does not need to be encrypted by the key. For example, the metadata can
+     * be a byte string describing the algorithms and non-secret parameters to be used with the
+     * key. The supplied metadata can later be obtained via
+     * {@link WrappedApplicationKey#getMetadata()}.
+     *
+     * <p>During the key recovery process, the same metadata has to be supplied via
+     * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process
+     * will fail due to the checking of the cryptographic binding. This can help prevent
+     * potential attacks that try to swap key materials on the backup server and trick the
+     * application to use keys with different algorithms or parameters.
+     *
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+     *     screen is required to generate recoverable keys.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes,
+            @Nullable byte[] metadata)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        try {
+            String grantAlias = mBinder.importKeyWithMetadata(alias, keyBytes, metadata);
+            if (grantAlias == null) {
+                throw new InternalRecoveryServiceException("Null grant alias");
+            }
+            return getKeyFromGrant(grantAlias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (UnrecoverableKeyException e) {
+            throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_INSECURE_USER) {
+                throw new LockScreenRequiredException(e.getMessage());
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
      * Gets a key called {@code alias} from the recoverable key store.
      *
      * @param alias The key alias.
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index ae4448f..dbfd655 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -17,6 +17,7 @@
 package android.security.keystore.recovery;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -41,6 +42,8 @@
     private String mAlias;
     // The only supported format is AES-256 symmetric key.
     private byte[] mEncryptedKeyMaterial;
+    // The optional metadata that's authenticated (but unencrypted) with the key material.
+    private byte[] mMetadata;
 
     // IMPORTANT! PLEASE READ!
     // -----------------------
@@ -80,13 +83,23 @@
          * @param encryptedKeyMaterial The key material
          * @return This builder
          */
-
         public Builder setEncryptedKeyMaterial(@NonNull byte[] encryptedKeyMaterial) {
             mInstance.mEncryptedKeyMaterial = encryptedKeyMaterial;
             return this;
         }
 
         /**
+         * Sets the metadata that is authenticated (but unecrypted) with the key material.
+         *
+         * @param metadata The metadata
+         * @return This builder
+         */
+        public Builder setMetadata(@Nullable byte[] metadata) {
+            mInstance.mMetadata = metadata;
+            return this;
+        }
+
+        /**
          * Creates a new {@link WrappedApplicationKey} instance.
          *
          * @return new instance
@@ -102,9 +115,10 @@
     private WrappedApplicationKey() { }
 
     /**
-     * Deprecated - consider using Builder.
+     * @deprecated Use the builder instead.
      * @hide
      */
+    @Deprecated
     public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
         mAlias = Preconditions.checkNotNull(alias);
         mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
@@ -124,6 +138,11 @@
         return mEncryptedKeyMaterial;
     }
 
+    /** The metadata with the key. */
+    public @Nullable byte[] getMetadata() {
+        return mMetadata;
+    }
+
     public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
             new Parcelable.Creator<WrappedApplicationKey>() {
                 public WrappedApplicationKey createFromParcel(Parcel in) {
@@ -139,6 +158,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(mAlias);
         out.writeByteArray(mEncryptedKeyMaterial);
+        out.writeByteArray(mMetadata);
     }
 
     /**
@@ -147,6 +167,10 @@
     protected WrappedApplicationKey(Parcel in) {
         mAlias = in.readString();
         mEncryptedKeyMaterial = in.createByteArray();
+        // Check if there is still data to be read.
+        if (in.dataAvail() > 0) {
+            mMetadata = in.createByteArray();
+        }
     }
 
     @Override
diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java
new file mode 100644
index 0000000..b77405a
--- /dev/null
+++ b/core/java/android/service/appprediction/AppPredictionService.java
@@ -0,0 +1,322 @@
+/*
+ * 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.service.appprediction;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.app.prediction.IPredictionCallback;
+import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.service.appprediction.IPredictionService.Stub;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * TODO(b/111701043): Add java docs
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AppPredictionService extends Service {
+
+    private static final String TAG = "AppPredictionService";
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     * TODO(b/111701043): Add any docs about permissions the service must hold
+     *
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.appprediction.AppPredictionService";
+
+    private final ArrayMap<AppPredictionSessionId, ArrayList<CallbackWrapper>> mSessionCallbacks =
+            new ArrayMap<>();
+    private Handler mHandler;
+
+    private final IPredictionService mInterface = new Stub() {
+
+        @Override
+        public void onCreatePredictionSession(AppPredictionContext context,
+                AppPredictionSessionId sessionId) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::doCreatePredictionSession,
+                            AppPredictionService.this, context, sessionId));
+        }
+
+        @Override
+        public void notifyAppTargetEvent(AppPredictionSessionId sessionId, AppTargetEvent event) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::onAppTargetEvent,
+                            AppPredictionService.this, sessionId, event));
+        }
+
+        @Override
+        public void notifyLocationShown(AppPredictionSessionId sessionId, String launchLocation,
+                ParceledListSlice targetIds) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::onLocationShown, AppPredictionService.this,
+                            sessionId, launchLocation, targetIds.getList()));
+        }
+
+        @Override
+        public void sortAppTargets(AppPredictionSessionId sessionId, ParceledListSlice targets,
+                IPredictionCallback callback) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::onSortAppTargets,
+                            AppPredictionService.this, sessionId, targets.getList(), null,
+                            new CallbackWrapper(callback)));
+        }
+
+        @Override
+        public void registerPredictionUpdates(AppPredictionSessionId sessionId,
+                IPredictionCallback callback) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::doRegisterPredictionUpdates,
+                            AppPredictionService.this, sessionId, callback));
+        }
+
+        @Override
+        public void unregisterPredictionUpdates(AppPredictionSessionId sessionId,
+                IPredictionCallback callback) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::doUnregisterPredictionUpdates,
+                            AppPredictionService.this, sessionId, callback));
+        }
+
+        @Override
+        public void requestPredictionUpdate(AppPredictionSessionId sessionId) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::doRequestPredictionUpdate,
+                            AppPredictionService.this, sessionId));
+        }
+
+        @Override
+        public void onDestroyPredictionSession(AppPredictionSessionId sessionId) {
+            mHandler.sendMessage(
+                    obtainMessage(AppPredictionService::doDestroyPredictionSession,
+                            AppPredictionService.this, sessionId));
+        }
+    };
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        return mInterface.asBinder();
+    }
+
+    /**
+     * Called by a client app to indicate a target launch
+     */
+    @MainThread
+    public abstract void onAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
+            @NonNull AppTargetEvent event);
+
+    /**
+     * Called by a client app to indication a particular location has been shown to the user.
+     */
+    @MainThread
+    public abstract void onLocationShown(@NonNull AppPredictionSessionId sessionId,
+            @NonNull String launchLocation, @NonNull List<AppTargetId> targetIds);
+
+    private void doCreatePredictionSession(@NonNull AppPredictionContext context,
+            @NonNull AppPredictionSessionId sessionId) {
+        mSessionCallbacks.put(sessionId, new ArrayList<>());
+        onCreatePredictionSession(context, sessionId);
+    }
+
+    /**
+     * Creates a new interaction session.
+     *
+     * @param context interaction context
+     * @param sessionId the session's Id
+     */
+    public void onCreatePredictionSession(@NonNull AppPredictionContext context,
+            @NonNull AppPredictionSessionId sessionId) {}
+
+    /**
+     * Called by the client app to request sorting of targets based on prediction rank.
+     * TODO(b/111701043): Implement CancellationSignal so caller can cancel a long running request
+     */
+    @MainThread
+    public abstract void onSortAppTargets(@NonNull AppPredictionSessionId sessionId,
+            @NonNull List<AppTarget> targets, @NonNull CancellationSignal cancellationSignal,
+            @NonNull Consumer<List<AppTarget>> callback);
+
+    private void doRegisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+            @NonNull IPredictionCallback callback) {
+        final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks == null) {
+            Slog.e(TAG, "Failed to register for updates for unknown session: " + sessionId);
+            return;
+        }
+
+        final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+        if (wrapper == null) {
+            callbacks.add(new CallbackWrapper(callback));
+            if (callbacks.size() == 1) {
+                onStartPredictionUpdates();
+            }
+        }
+    }
+
+    /**
+     * Called when any continuous prediction callback is registered.
+     */
+    @MainThread
+    public void onStartPredictionUpdates() {}
+
+    private void doUnregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+            @NonNull IPredictionCallback callback) {
+        final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks == null) {
+            Slog.e(TAG, "Failed to unregister for updates for unknown session: " + sessionId);
+            return;
+        }
+
+        final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+        if (wrapper != null) {
+            callbacks.remove(wrapper);
+            if (callbacks.isEmpty()) {
+                onStopPredictionUpdates();
+            }
+        }
+    }
+
+    /**
+     * Called when there are no longer any continuous prediction callbacks registered.
+     */
+    @MainThread
+    public void onStopPredictionUpdates() {}
+
+    private void doRequestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
+        final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks != null && !callbacks.isEmpty()) {
+            onRequestPredictionUpdate(sessionId);
+        }
+    }
+
+    /**
+     * Called by the client app to request target predictions. This method is only called if there
+     * are one or more prediction callbacks registered.
+     * TODO(b/111701043): Add java docs
+     *
+     * @see #updatePredictions(AppPredictionSessionId, List)
+     */
+    @MainThread
+    public abstract void onRequestPredictionUpdate(@NonNull AppPredictionSessionId sessionId);
+
+    private void doDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
+        mSessionCallbacks.remove(sessionId);
+        onDestroyPredictionSession(sessionId);
+    }
+
+    /**
+     * Destroys the interaction session.
+     *
+     * @param sessionId the id of the session to destroy
+     */
+    @MainThread
+    public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {}
+
+    /**
+     * Used by the prediction factory to send back results the client app. The can be called
+     * in response to {@link #onRequestPredictionUpdate(AppPredictionSessionId)} or proactively as
+     * a result of changes in predictions.
+     */
+    public final void updatePredictions(@NonNull AppPredictionSessionId sessionId,
+            @NonNull List<AppTarget> targets) {
+        List<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks != null) {
+            for (CallbackWrapper callback : callbacks) {
+                callback.accept(targets);
+            }
+        }
+    }
+
+    /**
+     * Finds the callback wrapper for the given callback.
+     */
+    private CallbackWrapper findCallbackWrapper(ArrayList<CallbackWrapper> callbacks,
+            IPredictionCallback callback) {
+        for (int i = callbacks.size() - 1; i >= 0; i--) {
+            if (callbacks.get(i).isCallback(callback)) {
+                return callbacks.get(i);
+            }
+        }
+        return null;
+    }
+
+    private static final class CallbackWrapper implements Consumer<List<AppTarget>>,
+            IBinder.DeathRecipient {
+
+        private IPredictionCallback mCallback;
+
+        CallbackWrapper(IPredictionCallback callback) {
+            mCallback = callback;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to link to death: " + e);
+            }
+        }
+
+        public boolean isCallback(@NonNull IPredictionCallback callback) {
+            return mCallback.equals(callback);
+        }
+
+        @Override
+        public void accept(List<AppTarget> ts) {
+            try {
+                if (mCallback != null) {
+                    mCallback.onResult(new ParceledListSlice(ts));
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error sending result:" + e);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mCallback = null;
+        }
+    }
+}
diff --git a/core/java/android/service/appprediction/IPredictionService.aidl b/core/java/android/service/appprediction/IPredictionService.aidl
new file mode 100644
index 0000000..3a6d166
--- /dev/null
+++ b/core/java/android/service/appprediction/IPredictionService.aidl
@@ -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 android.service.appprediction;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.IPredictionCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * Interface from the system to a prediction service.
+ *
+ * @hide
+ */
+oneway interface IPredictionService {
+
+    void onCreatePredictionSession(in AppPredictionContext context,
+            in AppPredictionSessionId sessionId);
+
+    void notifyAppTargetEvent(in AppPredictionSessionId sessionId, in AppTargetEvent event);
+
+    void notifyLocationShown(in AppPredictionSessionId sessionId,  in String launchLocation,
+            in ParceledListSlice targetIds);
+
+    void sortAppTargets(in AppPredictionSessionId sessionId, in ParceledListSlice targets,
+            in IPredictionCallback callback);
+
+    void registerPredictionUpdates(in AppPredictionSessionId sessionId,
+            in IPredictionCallback callback);
+
+    void unregisterPredictionUpdates(in AppPredictionSessionId sessionId,
+            in IPredictionCallback callback);
+
+    void requestPredictionUpdate(in AppPredictionSessionId sessionId);
+
+    void onDestroyPredictionSession(in AppPredictionSessionId sessionId);
+}
diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java
new file mode 100644
index 0000000..f6e448dc
--- /dev/null
+++ b/core/java/android/service/attention/AttentionService.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.attention;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Abstract base class for Attention service.
+ *
+ * <p> An attention service provides attention estimation related features to the system.
+ * The system's default AttentionService implementation is configured in
+ * {@code config_AttentionComponent}. If this config has no value, a stub is returned.
+ *
+ * See: {@link AttentionManagerService}.
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourAttentionService"
+ *          android:permission="android.permission.BIND_ATTENTION_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AttentionService extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service. To be supported, the
+     * service must also require the {@link android.Manifest.permission#BIND_ATTENTION_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.attention.AttentionService";
+
+    /** Attention is absent. */
+    public static final int ATTENTION_SUCCESS_ABSENT = 0;
+
+    /** Attention is present. */
+    public static final int ATTENTION_SUCCESS_PRESENT = 1;
+
+    /** Preempted by other camera user. */
+    public static final int ATTENTION_FAILURE_PREEMPTED = 2;
+
+    /** Preempted by other camera user. */
+    public static final int ATTENTION_FAILURE_TIMED_OUT = 3;
+
+    /** Unknown reasons for failing to determine the attention. */
+    public static final int ATTENTION_FAILURE_UNKNOWN = 4;
+
+    /**
+     * Result codes for when attention check was successful.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"ATTENTION_SUCCESS_"}, value = {ATTENTION_SUCCESS_ABSENT,
+            ATTENTION_SUCCESS_PRESENT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AttentionSuccessCodes {
+    }
+
+    /**
+     * Result codes explaining why attention check was not successful.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"ATTENTION_FAILURE_"}, value = {ATTENTION_FAILURE_PREEMPTED,
+            ATTENTION_FAILURE_TIMED_OUT, ATTENTION_FAILURE_UNKNOWN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AttentionFailureCodes {
+    }
+
+    private final IAttentionService.Stub mBinder = new IAttentionService.Stub() {
+
+        /** {@inheritDoc} */
+        @Override
+        public void checkAttention(int requestCode, IAttentionCallback callback) {
+            Preconditions.checkNotNull(callback);
+            AttentionService.this.onCheckAttention(requestCode, new AttentionCallback(callback));
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void cancelAttentionCheck(int requestCode) {
+            AttentionService.this.onCancelAttentionCheck(requestCode);
+        }
+    };
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mBinder;
+        }
+        return null;
+    }
+
+    /**
+     * Checks the user attention and calls into the provided callback.
+     *
+     * @param requestCode an identifier that could be used to cancel the request
+     * @param callback    the callback to return the result to
+     */
+    public abstract void onCheckAttention(int requestCode, @NonNull AttentionCallback callback);
+
+    /** Cancels the attention check for a given request code. */
+    public abstract void onCancelAttentionCheck(int requestCode);
+
+
+    /** Callbacks for AttentionService results. */
+    public static final class AttentionCallback {
+        private final IAttentionCallback mCallback;
+
+        private AttentionCallback(IAttentionCallback callback) {
+            mCallback = callback;
+        }
+
+        /** Returns the result. */
+        public void onSuccess(int requestCode, @AttentionSuccessCodes int result, long timestamp) {
+            try {
+                mCallback.onSuccess(requestCode, result, timestamp);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+
+        /** Signals a failure. */
+        public void onFailure(int requestCode, @AttentionFailureCodes int error) {
+            try {
+                mCallback.onFailure(requestCode, error);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/attention/IAttentionCallback.aidl b/core/java/android/service/attention/IAttentionCallback.aidl
new file mode 100644
index 0000000..0e8a1e7
--- /dev/null
+++ b/core/java/android/service/attention/IAttentionCallback.aidl
@@ -0,0 +1,27 @@
+ /*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.attention;
+
+/**
+ * Callback for onCheckAttention request.
+ *
+ * @hide
+ */
+oneway interface IAttentionCallback {
+    void onSuccess(int requestCode, int result, long timestamp);
+    void onFailure(int requestCode, int error);
+}
diff --git a/core/java/android/service/attention/IAttentionService.aidl b/core/java/android/service/attention/IAttentionService.aidl
new file mode 100644
index 0000000..c3b6f48
--- /dev/null
+++ b/core/java/android/service/attention/IAttentionService.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.attention;
+
+import android.service.attention.IAttentionCallback;
+
+/**
+ * Interface for a concrete implementation to provide to the AttentionManagerService.
+ *
+ * @hide
+ */
+oneway interface IAttentionService {
+    void checkAttention(int requestCode, IAttentionCallback callback);
+    void cancelAttentionCheck(int requestCode);
+}
\ No newline at end of file
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 7683b8a..aaba85b 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -41,6 +42,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAugmentedAutofillManagerClient;
+import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -58,6 +60,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class AugmentedAutofillService extends Service {
 
     private static final String TAG = AugmentedAutofillService.class.getSimpleName();
@@ -91,10 +96,10 @@
         }
 
         @Override
-        public void onDestroyFillWindowRequest(int sessionId) {
+        public void onDestroyAllFillWindowsRequest() {
             mHandler.sendMessage(
-                    obtainMessage(AugmentedAutofillService::handleOnDestroyFillWindowRequest,
-                            AugmentedAutofillService.this, sessionId));
+                    obtainMessage(AugmentedAutofillService::handleOnDestroyAllFillWindowsRequest,
+                            AugmentedAutofillService.this));
         }
     };
 
@@ -181,18 +186,21 @@
                 new FillCallback(proxy));
     }
 
-    private void handleOnDestroyFillWindowRequest(@NonNull int sessionId) {
-        AutofillProxy proxy = null;
+    private void handleOnDestroyAllFillWindowsRequest() {
         if (mAutofillProxies != null) {
-            proxy = mAutofillProxies.get(sessionId);
+            final int size = mAutofillProxies.size();
+            for (int i = 0; i < size; i++) {
+                final int sessionId = mAutofillProxies.keyAt(i);
+                final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+                if (proxy == null) {
+                    // TODO(b/111330312): this might be fine, in which case we should logv it
+                    Log.w(TAG, "No proxy for session " + sessionId);
+                    return;
+                }
+                proxy.destroy();
+            }
+            mAutofillProxies.clear();
         }
-        if (proxy == null) {
-            // TODO(b/111330312): this might be fine, in which case we should logv it
-            Log.w(TAG, "No proxy for session " + sessionId);
-            return;
-        }
-        proxy.destroy();
-        mAutofillProxies.remove(sessionId);
     }
 
     private void handleOnUnbind() {
@@ -214,7 +222,7 @@
     }
 
     @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    protected final void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mAutofillProxies != null) {
             final int size = mAutofillProxies.size();
             pw.print("Number proxies: "); pw.println(size);
@@ -225,6 +233,15 @@
                 proxy.dump("  ", pw);
             }
         }
+        dump(pw, args);
+    }
+
+    /**
+     * Implementation specific {@code dump}.
+     */
+    protected void dump(@NonNull PrintWriter pw,
+            @SuppressWarnings("unused") @NonNull String[] args) {
+        pw.print(getClass().getName()); pw.println(": nothing to dump");
     }
 
     /** @hide */
@@ -259,7 +276,6 @@
          *
          * <p>Used to make sure the SmartSuggestionsParams is updated when a new fields is focused.
          */
-        // TODO(b/111330312): might not be needed when using IME
         @GuardedBy("mLock")
         private AutofillId mLastShownId;
 
@@ -338,6 +354,16 @@
             }
         }
 
+        public void requestShowFillUi(int width, int height, Rect anchorBounds,
+                IAutofillWindowPresenter presenter) throws RemoteException {
+            mClient.requestShowFillUi(mSessionId, mFocusedId, width, height, anchorBounds,
+                    presenter);
+        }
+
+        public void requestHideFillUi() throws RemoteException {
+            mClient.requestHideFillUi(mSessionId, mFocusedId);
+        }
+
         private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
             synchronized (mLock) {
                 // TODO(b/111330312): should we close the popupwindow if the focused id changed?
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 620ec59..bfb4aad 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
 
@@ -29,6 +30,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillCallback {
 
     private static final String TAG = FillCallback.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index e65cf47..d7bc893 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -19,6 +19,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
@@ -36,6 +37,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillController {
     private static final String TAG = "FillController";
 
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index 57d2cc8..dad5067 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.view.autofill.AutofillId;
@@ -29,6 +30,9 @@
  */
 @SystemApi
 // TODO(b/111330312): pass a requestId and/or sessionId
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillRequest {
 
     final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 1ecfab4..5285132 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.AutofillId;
@@ -30,6 +31,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillResponse implements Parcelable {
 
     private final FillWindow mFillWindow;
@@ -50,6 +54,9 @@
      * @hide
      */
     @SystemApi
+    @TestApi
+    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+    //in the same package as the test, and that module is compiled with SDK=test_current
     public static final class Builder {
 
         private FillWindow mFillWindow;
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index bad7ddd..51b0f01 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -16,21 +16,25 @@
 package android.service.autofill.augmented;
 
 import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+import static android.service.autofill.augmented.AugmentedAutofillService.VERBOSE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.app.Dialog;
+import android.annotation.TestApi;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.service.autofill.augmented.PresentationParams.Area;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
 import android.view.WindowManager;
+import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -61,13 +65,16 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillWindow implements AutoCloseable {
     private static final String TAG = "FillWindow";
 
     /** Indicates the data being shown is a physical address */
     public static final long FLAG_METADATA_ADDRESS = 0x1;
 
-    // TODO(b/111330312): add moar flags
+    // TODO(b/111330312): add more flags
 
     /** @hide */
     @LongDef(prefix = { "FLAG" }, value = {
@@ -79,8 +86,17 @@
     private final Object mLock = new Object();
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
+    private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
+    private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();
+
     @GuardedBy("mLock")
-    private Dialog mDialog;
+    private WindowManager mWm;
+    @GuardedBy("mLock")
+    private View mFillView;
+    @GuardedBy("mLock")
+    private boolean mShowing;
+    @GuardedBy("mLock")
+    private Rect mBounds;
 
     @GuardedBy("mLock")
     private boolean mDestroyed;
@@ -136,51 +152,28 @@
             // window instead of destroying. In fact, it might be better to allocate a full window
             // initially, which is transparent (and let touches get through) everywhere but in the
             // rect boundaries.
-            destroy();
 
             // TODO(b/111330312): make sure all touch events are handled, window is always closed,
             // etc.
 
-            mDialog = new Dialog(rootView.getContext()) {
-                @Override
-                public boolean onTouchEvent(MotionEvent event) {
-                    if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-                        FillWindow.this.destroy();
+            mWm = rootView.getContext().getSystemService(WindowManager.class);
+            mFillView = rootView;
+            // Listen to the touch outside to destroy the window when typing is detected.
+            mFillView.setOnTouchListener(
+                    (view, motionEvent) -> {
+                        if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                            if (VERBOSE) Log.v(TAG, "Outside touch detected, hiding the window");
+                            hide();
+                        }
+                        return false;
                     }
-                    return false;
-                }
-            };
-            mCloseGuard.open("destroy");
-            final Window window = mDialog.getWindow();
-            window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
-            // Makes sure touch outside the dialog is received by the window behind the dialog.
-            window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-            // Makes sure the touch outside the dialog is received by the dialog to dismiss it.
-            window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-            // Makes sure keyboard shows up.
-            window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-
-            final int height = rect.bottom - rect.top;
-            final int width = rect.right - rect.left;
-            final WindowManager.LayoutParams windowParams = window.getAttributes();
-            windowParams.gravity = Gravity.TOP | Gravity.LEFT;
-            windowParams.y = rect.top + height;
-            windowParams.height = height;
-            windowParams.x = rect.left;
-            windowParams.width = width;
-
-            window.setAttributes(windowParams);
-            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-            window.setBackgroundDrawableResource(android.R.color.transparent);
-
-            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-            final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
-            mDialog.setContentView(rootView, diagParams);
-
+            );
+            mShowing = false;
+            mBounds = new Rect(area.getBounds());
             if (DEBUG) {
                 Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
             }
-
+            mDestroyed = false;
             mProxy.setFillWindow(this);
             return true;
         }
@@ -190,36 +183,87 @@
     void show() {
         // TODO(b/111330312): check if updated first / throw exception
         if (DEBUG) Log.d(TAG, "show()");
-
         synchronized (mLock) {
             checkNotDestroyedLocked();
-            if (mDialog == null) {
+            if (mWm == null || mFillView == null) {
                 throw new IllegalStateException("update() not called yet, or already destroyed()");
             }
-
-            mDialog.show();
             if (mProxy != null) {
+                try {
+                    mProxy.requestShowFillUi(mBounds.right - mBounds.left,
+                            mBounds.bottom - mBounds.top,
+                            /*anchorBounds=*/ null, mFillWindowPresenter);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Error requesting to show fill window", e);
+                }
                 mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
             }
         }
     }
 
     /**
+     * Hides the window.
+     *
+     * <p>The window is not destroyed and can be shown again
+     */
+    private void hide() {
+        if (DEBUG) Log.d(TAG, "hide()");
+        synchronized (mLock) {
+            checkNotDestroyedLocked();
+            if (mWm == null || mFillView == null) {
+                throw new IllegalStateException("update() not called yet, or already destroyed()");
+            }
+            if (mProxy != null && mShowing) {
+                try {
+                    mProxy.requestHideFillUi();
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Error requesting to hide fill window", e);
+                }
+            }
+        }
+    }
+
+    private void handleShow(WindowManager.LayoutParams p) {
+        if (DEBUG) Log.d(TAG, "handleShow()");
+        synchronized (mLock) {
+            if (mWm != null && mFillView != null) {
+                p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+                if (!mShowing) {
+                    mWm.addView(mFillView, p);
+                    mShowing = true;
+                } else {
+                    mWm.updateViewLayout(mFillView, p);
+                }
+            }
+        }
+    }
+
+    private void handleHide() {
+        if (DEBUG) Log.d(TAG, "handleHide()");
+        synchronized (mLock) {
+            if (mWm != null && mFillView != null && mShowing) {
+                mWm.removeView(mFillView);
+                mShowing = false;
+            }
+        }
+    }
+
+    /**
      * Destroys the window.
      *
      * <p>Once destroyed, this window cannot be used anymore
      */
     public void destroy() {
-        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog);
-
-        synchronized (this) {
-            if (mDestroyed || mDialog == null) return;
-
-            mDialog.dismiss();
-            mDialog = null;
-            if (mProxy != null) {
-                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
-            }
+        if (DEBUG) {
+            Log.d(TAG,
+                    "destroy(): mDestroyed=" + mDestroyed + " mShowing=" + mShowing + " mFillView="
+                            + mFillView);
+        }
+        synchronized (mLock) {
+            if (mDestroyed) return;
+            hide();
+            mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+            mDestroyed = true;
             mCloseGuard.close();
         }
     }
@@ -246,11 +290,15 @@
     public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         synchronized (this) {
             pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
-            if (mDialog != null) {
-                pw.print(prefix); pw.print("dialog: ");
-                pw.println(mDialog.isShowing() ? "shown" : "hidden");
-                pw.print(prefix); pw.print("window: ");
-                pw.println(mDialog.getWindow().getAttributes());
+            if (mFillView != null) {
+                pw.print(prefix); pw.print("fill window: ");
+                pw.println(mShowing ? "shown" : "hidden");
+                pw.print(prefix); pw.print("fill view: ");
+                pw.println(mFillView);
+                pw.print(prefix); pw.print("mBounds: ");
+                pw.println(mBounds);
+                pw.print(prefix); pw.print("mWm: ");
+                pw.println(mWm);
             }
         }
     }
@@ -260,4 +308,19 @@
     public void close() throws Exception {
         destroy();
     }
+
+    private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+        @Override
+        public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
+                boolean fitsSystemWindows, int layoutDirection) {
+            if (DEBUG) Log.d(TAG, "FillWindowPresenter.show()");
+            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
+        }
+
+        @Override
+        public void hide(Rect transitionEpicenter) {
+            if (DEBUG) Log.d(TAG, "FillWindowPresenter.hide()");
+            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
+        }
+    }
 }
diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
index b3ac2da..fb6912a 100644
--- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
+++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
@@ -36,5 +36,5 @@
                        in ComponentName activityComponent, in AutofillId focusedId,
                        in AutofillValue focusedValue, long requestTime, in IFillCallback callback);
 
-    void onDestroyFillWindowRequest(int sessionId);
+    void onDestroyAllFillWindowsRequest();
 }
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 0124ecc..b60064e 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.DebugUtils;
@@ -48,6 +49,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class PresentationParams {
 
     /**
@@ -147,8 +151,11 @@
      * Area associated with a {@link PresentationParams Smart Suggestions} provider.
      *
      * @hide
-     * */
+     */
     @SystemApi
+    @TestApi
+    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+    //in the same package as the test, and that module is compiled with SDK=test_current
     public abstract static class Area {
 
         /** @hide */
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index 784e719..db242a2 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -34,7 +34,7 @@
 @SystemApi
 @Deprecated
 public final class ContentCaptureEventsRequest implements Parcelable {
-// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore
+// TODO(b/121051220): remove .java and .aidl once service implementation doesn't use it anymore
 
     private final ContentCaptureEvent mEvent;
 
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index c9d46dd..302e1a6 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -42,11 +42,13 @@
 import android.view.contentcapture.ContentCaptureSessionId;
 import android.view.contentcapture.IContentCaptureDirectManager;
 import android.view.contentcapture.MainContentCaptureSession;
+import android.view.contentcapture.UserDataRemovalRequest;
 
 import com.android.internal.os.IResultReceiver;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -76,6 +78,7 @@
             "android.service.contentcapture.ContentCaptureService";
 
     private Handler mHandler;
+    private IContentCaptureServiceCallback mCallback;
 
     /**
      * Binder that receives calls from the system server.
@@ -83,9 +86,15 @@
     private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
 
         @Override
-        public void onConnectedStateChanged(boolean state) {
-            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnectedStateChanged,
-                    ContentCaptureService.this, state));
+        public void onConnected(IBinder callback) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnected,
+                    ContentCaptureService.this, callback));
+        }
+
+        @Override
+        public void onDisconnected() {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnDisconnected,
+                    ContentCaptureService.this));
         }
 
         @Override
@@ -123,12 +132,12 @@
     };
 
     /**
-     * List of sessions per UID.
+     * UIDs associated with each session.
      *
      * <p>This map is populated when an session is started, which is called by the system server
      * and can be trusted. Then subsequent calls made by the app are verified against this map.
      */
-    private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
+    private final ArrayMap<String, Integer> mSessionUids = new ArrayMap<>();
 
     @CallSuper
     @Override
@@ -165,7 +174,16 @@
      */
     public final void setContentCaptureWhitelist(@Nullable List<String> packages,
             @Nullable List<ComponentName> activities) {
-        //TODO(b/111276913): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "setContentCaptureWhitelist(): no server callback");
+            return;
+        }
+        try {
+            callback.setContentCaptureWhitelist(packages, activities);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -176,7 +194,16 @@
      */
     public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
             boolean enabled) {
-        //TODO(b/111276913): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "setActivityContentCaptureEnabled(): no server callback");
+            return;
+        }
+        try {
+            callback.setActivityContentCaptureEnabled(activity, enabled);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -187,7 +214,16 @@
      */
     public final void setPackageContentCaptureEnabled(@NonNull String packageName,
             boolean enabled) {
-        //TODO(b/111276913): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "setPackageContentCaptureEnabled(): no server callback");
+            return;
+        }
+        try {
+            callback.setPackageContentCaptureEnabled(packageName, enabled);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -196,7 +232,12 @@
      */
     @NonNull
     public final Set<ComponentName> getContentCaptureDisabledActivities() {
-        //TODO(b/111276913): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "getContentCaptureDisabledActivities(): no server callback");
+            return Collections.emptySet();
+        }
+        //TODO(b/122595322): implement (using SyncResultReceiver)
         return null;
     }
 
@@ -206,7 +247,12 @@
      */
     @NonNull
     public final Set<String> getContentCaptureDisabledPackages() {
-        //TODO(b/111276913): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "getContentCaptureDisabledPackages(): no server callback");
+            return Collections.emptySet();
+        }
+        //TODO(b/122595322): implement (using SyncResultReceiver)
         return null;
     }
 
@@ -255,6 +301,16 @@
         if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
         onContentCaptureEventsRequest(sessionId, new ContentCaptureEventsRequest(event));
     }
+
+    /**
+     * Notifies the service that the app requested to remove data associated with the user.
+     *
+     * @param request the user data requested to be removed
+     */
+    public void onUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) {
+        if (VERBOSE) Log.v(TAG, "onUserDataRemovalRequest()");
+    }
+
     /**
      * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
      *
@@ -285,23 +341,25 @@
     @Override
     @CallSuper
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        final int size = mSessionsByUid.size();
+        final int size = mSessionUids.size();
         pw.print("Number sessions: "); pw.println(size);
         if (size > 0) {
             final String prefix = "  ";
             for (int i = 0; i < size; i++) {
-                pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
-                pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
+                pw.print(prefix); pw.print(mSessionUids.keyAt(i));
+                pw.print(": uid="); pw.println(mSessionUids.valueAt(i));
             }
         }
     }
 
-    private void handleOnConnectedStateChanged(boolean state) {
-        if (state) {
-            onConnected();
-        } else {
-            onDisconnected();
-        }
+    private void handleOnConnected(@NonNull IBinder callback) {
+        mCallback = IContentCaptureServiceCallback.Stub.asInterface(callback);
+        onConnected();
+    }
+
+    private void handleOnDisconnected() {
+        onDisconnected();
+        mCallback = null;
     }
 
     //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
@@ -309,10 +367,24 @@
 
     private void handleOnCreateSession(@NonNull ContentCaptureContext context,
             @NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
-        mSessionsByUid.put(sessionId, uid);
+        mSessionUids.put(sessionId, uid);
         onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
-        setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
-                mClientInterface.asBinder());
+
+        final int clientFlags = context.getFlags();
+        int stateFlags = 0;
+        if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) {
+            stateFlags |= ContentCaptureSession.STATE_FLAG_SECURE;
+        }
+        if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0) {
+            stateFlags |= ContentCaptureSession.STATE_BY_APP;
+        }
+        if (stateFlags == 0) {
+            stateFlags = ContentCaptureSession.STATE_ACTIVE;
+        } else {
+            stateFlags |= ContentCaptureSession.STATE_DISABLED;
+
+        }
+        setClientState(clientReceiver, stateFlags, mClientInterface.asBinder());
     }
 
     private void handleSendEvents(int uid,
@@ -336,11 +408,11 @@
                 case ContentCaptureEvent.TYPE_SESSION_STARTED:
                     final ContentCaptureContext clientContext = event.getClientContext();
                     clientContext.setParentSessionId(event.getParentSessionId());
-                    mSessionsByUid.put(sessionIdString, uid);
+                    mSessionUids.put(sessionIdString, uid);
                     onCreateContentCaptureSession(clientContext, sessionId);
                     break;
                 case ContentCaptureEvent.TYPE_SESSION_FINISHED:
-                    mSessionsByUid.remove(sessionIdString);
+                    mSessionUids.remove(sessionIdString);
                     onDestroyContentCaptureSession(sessionId);
                     break;
                 default:
@@ -355,7 +427,7 @@
     }
 
     private void handleFinishSession(@NonNull String sessionId) {
-        mSessionsByUid.remove(sessionId);
+        mSessionUids.remove(sessionId);
         onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
     }
 
@@ -372,10 +444,13 @@
             default:
                 sessionId = event.getSessionId();
         }
-        final Integer rightUid = mSessionsByUid.get(sessionId);
+        final Integer rightUid = mSessionUids.get(sessionId);
         if (rightUid == null) {
-            if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
-            // Just ignore, as the session could have finished
+            if (DEBUG) {
+                Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
+                        + ": " + mSessionUids);
+            }
+            // Just ignore, as the session could have been finished already
             return false;
         }
         if (rightUid != uid) {
@@ -389,15 +464,16 @@
     }
 
     /**
-     * Sends the state of the {@link ContentCaptureManager} in the cleint app.
+     * Sends the state of the {@link ContentCaptureManager} in the client app.
      *
      * @param clientReceiver receiver in the client app.
+     * @param sessionState state of the session
      * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
      * service.
      * @hide
      */
     public static void setClientState(@NonNull IResultReceiver clientReceiver,
-            int sessionStatus, @Nullable IBinder binder) {
+            int sessionState, @Nullable IBinder binder) {
         try {
             final Bundle extras;
             if (binder != null) {
@@ -406,7 +482,7 @@
             } else {
                 extras = null;
             }
-            clientReceiver.send(sessionStatus, extras);
+            clientReceiver.send(sessionState, extras);
         } catch (RemoteException e) {
             Slog.w(TAG, "Error async reporting result to client: " + e);
         }
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index 1b4cccf..a8dd213 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -16,6 +16,7 @@
 
 package android.service.contentcapture;
 
+import android.os.IBinder;
 import android.service.contentcapture.SnapshotData;
 import android.view.contentcapture.ContentCaptureContext;
 
@@ -29,7 +30,8 @@
  * @hide
  */
 oneway interface IContentCaptureService {
-    void onConnectedStateChanged(boolean state);
+    void onConnected(IBinder callback);
+    void onDisconnected();
     void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
                           in IResultReceiver clientReceiver);
     void onSessionFinished(String sessionId);
diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
new file mode 100644
index 0000000..e84bd6f
--- /dev/null
+++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.contentcapture;
+
+import android.content.ComponentName;
+import com.android.internal.os.IResultReceiver;
+
+import java.util.List;
+
+/**
+ * Interface from the Content Capture service to the system.
+ *
+ * @hide
+ */
+oneway interface IContentCaptureServiceCallback {
+    void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities);
+    void setActivityContentCaptureEnabled(in ComponentName activity, boolean enabled);
+    void setPackageContentCaptureEnabled(in String packageName, boolean enabled);
+    void getContentCaptureDisabledActivities(in IResultReceiver receiver);
+    void getContentCaptureDisabledPackages(in IResultReceiver receiver);
+}
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
new file mode 100644
index 0000000..0da8039
--- /dev/null
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -0,0 +1,154 @@
+/*
+ * 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.service.contentsuggestions;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.ContentSuggestionsManager;
+import android.app.contentsuggestions.IClassificationsCallback;
+import android.app.contentsuggestions.ISelectionsCallback;
+import android.app.contentsuggestions.SelectionsRequest;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+/**
+ * @hide
+ */
+@SystemApi
+public abstract class ContentSuggestionsService extends Service {
+
+    private static final String TAG = ContentSuggestionsService.class.getSimpleName();
+
+    private Handler mHandler;
+
+    /**
+     * The action for the intent used to define the content suggestions service.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.contentsuggestions.ContentSuggestionsService";
+
+    private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() {
+        @Override
+        public void provideContextImage(int taskId, GraphicBuffer contextImage,
+                Bundle imageContextRequestExtras) {
+            mHandler.sendMessage(
+                    obtainMessage(ContentSuggestionsService::processContextImage,
+                            ContentSuggestionsService.this, taskId,
+                            Bitmap.createHardwareBitmap(contextImage),
+                            imageContextRequestExtras));
+        }
+
+        @Override
+        public void suggestContentSelections(SelectionsRequest request,
+                ISelectionsCallback callback) {
+            mHandler.sendMessage(obtainMessage(ContentSuggestionsService::suggestContentSelections,
+                    ContentSuggestionsService.this, request, wrapSelectionsCallback(callback)));
+
+        }
+
+        @Override
+        public void classifyContentSelections(ClassificationsRequest request,
+                IClassificationsCallback callback) {
+            mHandler.sendMessage(obtainMessage(ContentSuggestionsService::classifyContentSelections,
+                    ContentSuggestionsService.this, request, wrapClassificationCallback(callback)));
+        }
+
+        @Override
+        public void notifyInteraction(String requestId, Bundle interaction) {
+            mHandler.sendMessage(
+                    obtainMessage(ContentSuggestionsService::notifyInteraction,
+                            ContentSuggestionsService.this, requestId, interaction));
+        }
+    };
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    /** @hide */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mInterface.asBinder();
+        }
+        Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+        return null;
+    }
+
+    /**
+     * Called by the system to provide the snapshot for the task associated with the given
+     * {@param taskId}.
+     */
+    public abstract void processContextImage(
+            int taskId, @Nullable Bitmap contextImage, @NonNull Bundle extras);
+
+    /**
+     * Called by a client app to make a request for content selections.
+     */
+    public abstract void suggestContentSelections(@NonNull SelectionsRequest request,
+            @NonNull ContentSuggestionsManager.SelectionsCallback callback);
+
+    /**
+     * Called by a client app to classify the provided content selections.
+     */
+    public abstract void classifyContentSelections(@NonNull ClassificationsRequest request,
+            @NonNull ContentSuggestionsManager.ClassificationsCallback callback);
+
+    /**
+     * Called by a client app to report an interaction.
+     */
+    public abstract void notifyInteraction(@NonNull String requestId, @NonNull Bundle interaction);
+
+    private ContentSuggestionsManager.SelectionsCallback wrapSelectionsCallback(
+            ISelectionsCallback callback) {
+        return (statusCode, selections) -> {
+            try {
+                callback.onContentSelectionsAvailable(statusCode, selections);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error sending result: " + e);
+            }
+        };
+    }
+
+    private ContentSuggestionsManager.ClassificationsCallback wrapClassificationCallback(
+            IClassificationsCallback callback) {
+        return ((statusCode, classifications) -> {
+            try {
+                callback.onContentClassificationsAvailable(statusCode, classifications);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error sending result: " + e);
+            }
+        });
+    }
+}
diff --git a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
new file mode 100644
index 0000000..1926478
--- /dev/null
+++ b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
@@ -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 android.service.contentsuggestions;
+
+import android.app.contentsuggestions.IClassificationsCallback;
+import android.app.contentsuggestions.ISelectionsCallback;
+import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.SelectionsRequest;
+import android.graphics.GraphicBuffer;
+import android.os.Bundle;
+
+/**
+ * Interface from the system to an implementation of a content suggestions service.
+ *
+ * @hide
+ */
+oneway interface IContentSuggestionsService {
+    void provideContextImage(
+            int taskId,
+            in GraphicBuffer contextImage,
+            in Bundle imageContextRequestExtras);
+    void suggestContentSelections(
+            in SelectionsRequest request,
+            in ISelectionsCallback callback);
+    void classifyContentSelections(
+            in ClassificationsRequest request,
+            in IClassificationsCallback callback);
+    void notifyInteraction(in String requestId, in Bundle interaction);
+}
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index be3f3b3..b84e6c9 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -33,4 +33,5 @@
     void finishSelf(in IBinder token, boolean immediate);
     void startDozing(in IBinder token, int screenState, int screenBrightness);
     void stopDozing(in IBinder token);
+    void forceAmbientDisplayEnabled(boolean enabled);
 }
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 4be1f9c..4dc10cd 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -184,6 +184,12 @@
     public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED =
             "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
 
+    /**
+     * Intent extra set for resolution requests containing an int indicating the current card Id.
+     */
+    public static final String EXTRA_RESOLUTION_CARD_ID =
+            "android.service.euicc.extra.RESOLUTION_CARD_ID";
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "RESULT_" }, value = {
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index dd0242a..63fd563 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -185,7 +185,7 @@
      * Implement this to know when a direct reply is sent from a notification.
      * @param key the notification key
      */
-    public void onNotificationDirectReply(@NonNull String key) {}
+    public void onNotificationDirectReplied(@NonNull String key) {}
 
     /**
      * Implement this to know when a suggested reply is sent.
@@ -203,7 +203,7 @@
      * @param action the action that is just clicked
      * @param source the source that provided the action, e.g. SOURCE_FROM_APP
      */
-    public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+    public void onActionInvoked(@NonNull String key, @NonNull Notification.Action action,
             @Source int source) {
     }
 
@@ -338,7 +338,7 @@
             args.arg1 = key;
             args.arg2 = action;
             args.argi2 = source;
-            mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_CLICKED, args).sendToTarget();
+            mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_INVOKED, args).sendToTarget();
         }
     }
 
@@ -349,7 +349,7 @@
         public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
         public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
         public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
-        public static final int MSG_ON_ACTION_CLICKED = 7;
+        public static final int MSG_ON_ACTION_INVOKED = 7;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -407,7 +407,7 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     String key = (String) args.arg1;
                     args.recycle();
-                    onNotificationDirectReply(key);
+                    onNotificationDirectReplied(key);
                     break;
                 }
                 case MSG_ON_SUGGESTED_REPLY_SENT: {
@@ -419,13 +419,13 @@
                     onSuggestedReplySent(key, reply, source);
                     break;
                 }
-                case MSG_ON_ACTION_CLICKED: {
+                case MSG_ON_ACTION_INVOKED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     String key = (String) args.arg1;
                     Notification.Action action = (Notification.Action) args.arg2;
                     int source = args.argi2;
                     args.recycle();
-                    onActionClicked(key, action, source);
+                    onActionInvoked(key, action, source);
                     break;
                 }
             }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index ad10cc9..3a644d4 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -22,16 +22,21 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.metrics.LogMaker;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 /**
  * Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
  * the status bar and any {@link android.service.notification.NotificationListenerService}s.
  */
 public class StatusBarNotification implements Parcelable {
+    static final int MAX_LOG_TAG_LENGTH = 36;
+
     @UnsupportedAppUsage
     private final String pkg;
     @UnsupportedAppUsage
@@ -56,6 +61,9 @@
 
     private Context mContext; // used for inflation & icon expansion
 
+    // Contains the basic logging data of the notification.
+    private LogMaker mLogMaker;
+
     /** @hide */
     public StatusBarNotification(String pkg, String opPkg, int id,
             String tag, int uid, int initialPid, Notification notification, UserHandle user,
@@ -381,4 +389,51 @@
         }
         return mContext;
     }
+
+    /**
+     * Returns a LogMaker that contains all basic information of the notification.
+     * @hide
+     */
+    public LogMaker getLogMaker() {
+        if (mLogMaker == null) {
+            // Initialize fields that only change on update (so a new record).
+            mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
+                .setPackageName(getPackageName())
+                .addTaggedData(MetricsEvent.NOTIFICATION_ID, getId())
+                .addTaggedData(MetricsEvent.NOTIFICATION_TAG, getTag())
+                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
+        }
+        // Reset fields that can change between updates, or are used by multiple logs.
+        return mLogMaker
+            .clearCategory()
+            .clearType()
+            .clearSubtype()
+            .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
+            .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
+                getNotification().isGroupSummary() ? 1 : 0);
+    }
+
+    private String getGroupLogTag() {
+        return shortenTag(getGroup());
+    }
+
+    private String getChannelIdLogTag() {
+        if (notification.getChannelId() == null) {
+            return null;
+        }
+        return shortenTag(notification.getChannelId());
+    }
+
+    // Make logTag with max size MAX_LOG_TAG_LENGTH.
+    // For shorter or equal tags, returns the tag.
+    // For longer tags, truncate the tag and append a hash of the full tag to
+    // fill the maximum size.
+    private String shortenTag(String logTag) {
+        if (logTag == null || logTag.length() <= MAX_LOG_TAG_LENGTH) {
+            return logTag;
+        }
+        String hash = Integer.toHexString(logTag.hashCode());
+        return logTag.substring(0, MAX_LOG_TAG_LENGTH - hash.length() - 1) + "-"
+            + hash;
+    }
 }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 163e3d5..6f27447 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -121,6 +121,12 @@
      */
     public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6;
 
+    /**
+     * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
+     * from an Android automotive system Ui.
+     */
+    public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7;
+
     final Context mContext;
     final HandlerCaller mHandlerCaller;
 
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index a976d0e..f334d9d 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -27,5 +27,5 @@
     void attachEngine(IWallpaperEngine engine, int displayId);
     void engineShown(IWallpaperEngine engine);
     ParcelFileDescriptor setWallpaper(String name);
-    void onWallpaperColorsChanged(in WallpaperColors colors);
+    void onWallpaperColorsChanged(in WallpaperColors colors, int displayId);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a011d67..0d7223d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -56,6 +56,7 @@
 import android.view.InputEventReceiver;
 import android.view.InsetsState;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.View;
 import android.view.ViewGroup;
@@ -217,6 +218,8 @@
         private Context mDisplayContext;
         private int mDisplayState;
 
+        SurfaceControl mSurfaceControl = new SurfaceControl();
+
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
                 mRequestedFormat = PixelFormat.RGBX_8888;
@@ -642,7 +645,7 @@
             try {
                 final WallpaperColors newColors = onComputeColors();
                 if (mConnection != null) {
-                    mConnection.onWallpaperColorsChanged(newColors);
+                    mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId());
                 } else {
                     Log.w(TAG, "Can't notify system because wallpaper connection "
                             + "was not established.");
@@ -843,8 +846,12 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+                            mDisplayCutout, mMergedConfiguration, mSurfaceControl,
                             mInsetsState);
+                    if (mSurfaceControl.isValid()) {
+                        mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
+                        mSurfaceControl.release();
+                    }
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -963,7 +970,7 @@
                             mFinalSystemInsets.set(mDispatchedOverscanInsets);
                             mFinalStableInsets.set(mDispatchedStableInsets);
                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
-                                    null, mFinalStableInsets,
+                                    mFinalStableInsets,
                                     getResources().getConfiguration().isScreenRound(), false,
                                     mDispatchedDisplayCutout);
                             if (DEBUG) {
@@ -1466,7 +1473,7 @@
                         break;
                     }
                     try {
-                        mConnection.onWallpaperColorsChanged(mEngine.onComputeColors());
+                        mConnection.onWallpaperColorsChanged(mEngine.onComputeColors(), mDisplayId);
                     } catch (RemoteException e) {
                         // Connection went away, nothing to do in here.
                     }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 3d0c662..24d746e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -376,9 +376,13 @@
          * Set paragraph justification mode. The default value is
          * {@link Layout#JUSTIFICATION_MODE_NONE}. If the last line is too short for justification,
          * the last line will be displayed with the alignment set by {@link #setAlignment}.
+         * When Justification mode is JUSTIFICATION_MODE_INTER_WORD, wordSpacing on the given
+         * {@link Paint} will be ignored. This behavior also affects Spans which change the
+         * wordSpacing.
          *
          * @param justificationMode justification mode for the paragraph.
          * @return this builder, useful for chaining.
+         * @see Paint#setWordSpacing(float)
          */
         @NonNull
         public Builder setJustificationMode(@JustificationMode int justificationMode) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 6eb433a..949328f 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -75,8 +75,9 @@
     private int mEllipsisEnd;
 
     // Additional width of whitespace for justification. This value is per whitespace, thus
-    // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
-    private float mAddedWidth;
+    // the line width will increase by mAddedWidthForJustify x (number of stretchable whitespaces).
+    private float mAddedWidthForJustify;
+    private boolean mIsJustifying;
 
     private final TextPaint mWorkPaint = new TextPaint();
     private final TextPaint mActivePaint = new TextPaint();
@@ -229,7 +230,8 @@
             }
         }
         mTabs = tabStops;
-        mAddedWidth = 0;
+        mAddedWidthForJustify = 0;
+        mIsJustifying = false;
 
         mEllipsisStart = ellipsisStart != ellipsisEnd ? ellipsisStart : 0;
         mEllipsisEnd = ellipsisStart != ellipsisEnd ? ellipsisEnd : 0;
@@ -255,7 +257,8 @@
             return;
         }
         final float width = Math.abs(measure(end, false, null));
-        mAddedWidth = (justifyWidth - width) / spaces;
+        mAddedWidthForJustify = (justifyWidth - width) / spaces;
+        mIsJustifying = true;
     }
 
     /**
@@ -713,7 +716,9 @@
 
         TextPaint wp = mWorkPaint;
         wp.set(mPaint);
-        wp.setWordSpacing(mAddedWidth);
+        if (mIsJustifying) {
+            wp.setWordSpacing(mAddedWidthForJustify);
+        }
 
         int spanStart = runStart;
         int spanLimit;
@@ -849,7 +854,9 @@
             FontMetricsInt fmi, boolean needWidth, int offset,
             @Nullable ArrayList<DecorationInfo> decorations) {
 
-        wp.setWordSpacing(mAddedWidth);
+        if (mIsJustifying) {
+            wp.setWordSpacing(mAddedWidthForJustify);
+        }
         // Get metrics first (even for empty strings or "0" width runs)
         if (fmi != null) {
             expandMetricsFromPaint(fmi, wp);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 85b6b88..44353b1 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -33,8 +33,7 @@
 import android.icu.util.ULocale;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemProperties;
-import android.provider.Settings;
+import android.sysprop.DisplayProperties;
 import android.text.style.AbsoluteSizeSpan;
 import android.text.style.AccessibilityClickableSpan;
 import android.text.style.AccessibilityURLSpan;
@@ -2059,7 +2058,7 @@
         return ((locale != null && !locale.equals(Locale.ROOT)
                         && ULocale.forLocale(locale).isRightToLeft())
                 // If forcing into RTL layout mode, return RTL as default
-                || SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false))
+                || DisplayProperties.debug_force_rtl().orElse(false))
             ? View.LAYOUT_DIRECTION_RTL
             : View.LAYOUT_DIRECTION_LTR;
     }
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 433483f7..7a58681 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
@@ -370,6 +371,7 @@
     /**
      * @return The color of the underline for that span, or 0 if there is no underline
      */
+    @ColorInt
     public int getUnderlineColor() {
         // The order here should match what is used in updateDrawState
         final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index eef7ea2..50e7ec3 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -37,7 +37,6 @@
 import com.android.i18n.phonenumbers.PhoneNumberMatch;
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
-import com.android.internal.annotations.GuardedBy;
 
 import libcore.util.EmptyArray;
 
@@ -49,6 +48,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Locale;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -69,7 +69,6 @@
  *
  * @see MatchFilter
  * @see TransformFilter
- * @see UrlSpanFactory
  */
 
 public class Linkify {
@@ -228,44 +227,6 @@
     }
 
     /**
-     * Factory class to create {@link URLSpan}s. While adding spans to a {@link Spannable},
-     * {@link Linkify} will call {@link UrlSpanFactory#create(String)} function to create a
-     * {@link URLSpan}.
-     *
-     * @see #addLinks(Spannable, int, UrlSpanFactory)
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
-     */
-    public static class UrlSpanFactory {
-        private static final Object sInstanceLock = new Object();
-
-        @GuardedBy("sInstanceLock")
-        private static volatile UrlSpanFactory sInstance = null;
-
-        private static synchronized UrlSpanFactory getInstance() {
-            if (sInstance == null) {
-                synchronized (sInstanceLock) {
-                    if (sInstance == null) {
-                        sInstance = new UrlSpanFactory();
-                    }
-                }
-            }
-            return sInstance;
-        }
-
-        /**
-         * Factory function that will called by {@link Linkify} in order to create a
-         * {@link URLSpan}.
-         *
-         * @param url URL found
-         * @return a URLSpan instance
-         */
-        public URLSpan create(final String url) {
-            return new URLSpan(url);
-        }
-    }
-
-    /**
      *  Scans the text of the provided Spannable and turns all occurrences
      *  of the link types indicated in the mask into clickable links.
      *  If the mask is nonzero, it also removes any existing URLSpans
@@ -277,7 +238,7 @@
      *
      *  @return True if at least one link is found and applied.
      *
-     * @see #addLinks(Spannable, int, UrlSpanFactory)
+     * @see #addLinks(Spannable, int, Function)
      */
     public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
         return addLinks(text, mask, null, null);
@@ -292,11 +253,11 @@
      *
      *  @param text Spannable whose text is to be marked-up with links
      *  @param mask mask to define which kinds of links will be searched
-     *  @param urlSpanFactory factory class used to create {@link URLSpan}s
+     *  @param urlSpanFactory function used to create {@link URLSpan}s
      *  @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
-            @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Function<String, URLSpan> urlSpanFactory) {
         return addLinks(text, mask, null, urlSpanFactory);
     }
 
@@ -309,11 +270,11 @@
      * @param text Spannable whose text is to be marked-up with links
      * @param mask mask to define which kinds of links will be searched
      * @param context Context to be used while identifying phone numbers
-     * @param urlSpanFactory factory class used to create {@link URLSpan}s
+     * @param urlSpanFactory function used to create {@link URLSpan}s
      * @return true if at least one link is found and applied.
      */
     private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
-            @Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Context context, @Nullable Function<String, URLSpan> urlSpanFactory) {
         if (text != null && containsUnsupportedCharacters(text.toString())) {
             android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
             return false;
@@ -398,7 +359,7 @@
      *
      *  @return True if at least one link is found and applied.
      *
-     *  @see #addLinks(Spannable, int, UrlSpanFactory)
+     *  @see #addLinks(Spannable, int, Function)
      */
     public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
         if (mask == 0) {
@@ -512,8 +473,7 @@
      *  @param pattern      Regex pattern to be used for finding links
      *  @param scheme       URL scheme string (eg <code>http://</code>) to be
      *                      prepended to the links that do not start with this scheme.
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function)
      */
     public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
             @Nullable String scheme) {
@@ -534,8 +494,7 @@
      * @param transformFilter Filter to allow the client code to update the link found.
      *
      * @return True if at least one link is found and applied.
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function)
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
@@ -560,8 +519,7 @@
      *
      * @return True if at least one link is found and applied.
      *
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function)
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String defaultScheme, @Nullable String[] schemes,
@@ -584,14 +542,14 @@
      * @param matchFilter     the filter that is used to allow the client code additional control
      *                        over which pattern matches are to be converted into links.
      * @param transformFilter filter to allow the client code to update the link found.
-     * @param urlSpanFactory  factory class used to create {@link URLSpan}s
+     * @param urlSpanFactory  function used to create {@link URLSpan}s
      *
      * @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
-            @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Function<String, URLSpan> urlSpanFactory) {
         if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
             android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
             return false;
@@ -634,11 +592,11 @@
     }
 
     private static void applyLink(String url, int start, int end, Spannable text,
-            @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Function<String, URLSpan> urlSpanFactory) {
         if (urlSpanFactory == null) {
-            urlSpanFactory = UrlSpanFactory.getInstance();
+            urlSpanFactory = DEFAULT_SPAN_FACTORY;
         }
-        final URLSpan span = urlSpanFactory.create(url);
+        final URLSpan span = urlSpanFactory.apply(url);
         text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
     }
 
@@ -805,6 +763,13 @@
             i++;
         }
     }
+
+    /**
+     * Default factory function to create {@link URLSpan}s. While adding spans to a
+     * {@link Spannable}, {@link Linkify} will call this function to create a {@link URLSpan}.
+     */
+    private static final Function<String, URLSpan> DEFAULT_SPAN_FACTORY =
+            (String string) -> new URLSpan(string);
 }
 
 class LinkSpec {
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index af73a16..265b0d3 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
+
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -250,7 +251,7 @@
                     if (value == 0 && flagsWasZero) {
                         return constNameWithoutPrefix(prefix, field);
                     }
-                    if ((flags & value) != 0) {
+                    if ((flags & value) == value) {
                         flags &= ~value;
                         res.append(constNameWithoutPrefix(prefix, field)).append('|');
                     }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 8b97e0e..0edcb3d 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -22,7 +22,9 @@
 import android.text.TextUtils;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Util class to get feature flag information.
@@ -37,8 +39,11 @@
     public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
     public static final String SAFETY_HUB = "settings_safety_hub";
     public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
+    public static final String AOD_IMAGEWALLPAPER_ENABLED = "settings_aod_imagewallpaper_enabled";
 
     private static final Map<String, String> DEFAULT_FLAGS;
+    private static final Set<String> OBSERVABLE_FLAGS;
+
     static {
         DEFAULT_FLAGS = new HashMap<>();
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
@@ -54,6 +59,10 @@
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put(SAFETY_HUB, "false");
         DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
+        DEFAULT_FLAGS.put(AOD_IMAGEWALLPAPER_ENABLED, "false");
+
+        OBSERVABLE_FLAGS = new HashSet<>();
+        OBSERVABLE_FLAGS.add(AOD_IMAGEWALLPAPER_ENABLED);
     }
 
     /**
@@ -90,6 +99,16 @@
      */
     public static void setEnabled(Context context, String feature, boolean enabled) {
         SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false");
+
+        // Also update Settings.Global if needed so that we can observe it via observer.
+        if (OBSERVABLE_FLAGS.contains(feature)) {
+            setObservableFlag(context, feature, enabled);
+        }
+    }
+
+    private static void setObservableFlag(Context context, String feature, boolean enabled) {
+        Settings.Global.putString(
+                context.getContentResolver(), feature, enabled ? "true" : "false");
     }
 
     /**
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 5718d99..70d049a 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -162,6 +162,7 @@
     private static final int FP32_EXPONENT_MASK     = 0xff;
     private static final int FP32_SIGNIFICAND_MASK  = 0x7fffff;
     private static final int FP32_EXPONENT_BIAS     = 127;
+    private static final int FP32_QNAN_MASK         = 0x400000;
 
     private static final int FP32_DENORMAL_MAGIC = 126 << 23;
     private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
@@ -903,6 +904,9 @@
             outM = m << 13;
             if (e == 0x1f) { // Infinite or NaN
                 outE = 0xff;
+                if (outM != 0) { // SNaNs are quieted
+                    outM |= FP32_QNAN_MASK;
+                }
             } else {
                 outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
             }
diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index 7eef63e..d051ed8 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -16,7 +16,9 @@
 package android.util;
 
 import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
 
+import java.io.PrintWriter;
 import java.time.Duration;
 import java.time.format.DateTimeParseException;
 
@@ -212,4 +214,163 @@
         }
         return def;
     }
+
+    /** Represents an integer config value. */
+    public static class IntValue {
+        private final String mKey;
+        private final int mDefaultValue;
+        private int mValue;
+
+        /** Constructor, initialize with a config key and a default value. */
+        public IntValue(String key, int defaultValue) {
+            mKey = key;
+            mDefaultValue = defaultValue;
+            mValue = mDefaultValue;
+        }
+
+        /** Read a value from {@link KeyValueListParser} */
+        public void parse(KeyValueListParser parser) {
+            mValue = parser.getInt(mKey, mDefaultValue);
+        }
+
+        /** Return the config key. */
+        public String getKey() {
+            return mKey;
+        }
+
+        /** Return the default value. */
+        public int getDefaultValue() {
+            return mDefaultValue;
+        }
+
+        /** Return the actual config value. */
+        public int getValue() {
+            return mValue;
+        }
+
+        /** Overwrites with a value. */
+        public void setValue(int value) {
+            mValue = value;
+        }
+
+        /** Used for dumpsys */
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix);
+            pw.print(mKey);
+            pw.print("=");
+            pw.print(mValue);
+            pw.println();
+        }
+
+        /** Used for proto dumpsys */
+        public void dumpProto(ProtoOutputStream proto, long tag) {
+            proto.write(tag, mValue);
+        }
+    }
+
+    /** Represents an long config value. */
+    public static class LongValue {
+        private final String mKey;
+        private final long mDefaultValue;
+        private long mValue;
+
+        /** Constructor, initialize with a config key and a default value. */
+        public LongValue(String key, long defaultValue) {
+            mKey = key;
+            mDefaultValue = defaultValue;
+            mValue = mDefaultValue;
+        }
+
+        /** Read a value from {@link KeyValueListParser} */
+        public void parse(KeyValueListParser parser) {
+            mValue = parser.getLong(mKey, mDefaultValue);
+        }
+
+        /** Return the config key. */
+        public String getKey() {
+            return mKey;
+        }
+
+        /** Return the default value. */
+        public long getDefaultValue() {
+            return mDefaultValue;
+        }
+
+        /** Return the actual config value. */
+        public long getValue() {
+            return mValue;
+        }
+
+        /** Overwrites with a value. */
+        public void setValue(long value) {
+            mValue = value;
+        }
+
+        /** Used for dumpsys */
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix);
+            pw.print(mKey);
+            pw.print("=");
+            pw.print(mValue);
+            pw.println();
+        }
+
+        /** Used for proto dumpsys */
+        public void dumpProto(ProtoOutputStream proto, long tag) {
+            proto.write(tag, mValue);
+        }
+    }
+
+    /** Represents an string config value. */
+    public static class StringValue {
+        private final String mKey;
+        private final String mDefaultValue;
+        private String mValue;
+
+        /** Constructor, initialize with a config key and a default value. */
+        public StringValue(String key, String defaultValue) {
+            mKey = key;
+            mDefaultValue = defaultValue;
+            mValue = mDefaultValue;
+        }
+
+        /** Read a value from {@link KeyValueListParser} */
+        public void parse(KeyValueListParser parser) {
+            mValue = parser.getString(mKey, mDefaultValue);
+        }
+
+        /** Return the config key. */
+        public String getKey() {
+            return mKey;
+        }
+
+        /** Return the default value. */
+        public String getDefaultValue() {
+            return mDefaultValue;
+        }
+
+        /** Return the actual config value. */
+        public String getValue() {
+            return mValue;
+        }
+
+        /** Overwrites with a value. */
+        public void setValue(String value) {
+            mValue = value;
+        }
+
+        /** Used for dumpsys */
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix);
+            pw.print(mKey);
+            pw.print("=");
+            pw.print(mValue);
+            pw.println();
+        }
+
+        /** Used for proto dumpsys */
+        public void dumpProto(ProtoOutputStream proto, long tag) {
+            proto.write(tag, mValue);
+        }
+    }
 }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 91bc3eb..e4c8eeb 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -43,6 +43,9 @@
     /** {@hide} */
     private static SimpleDateFormat sLoggingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
+    /** @hide */
+    public static final SimpleDateFormat sDumpDateFormat =
+            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
     /**
      * Tries to return a time zone that would have had the specified offset
      * and DST value at the specified moment in the specified country.
@@ -360,4 +363,28 @@
             return sLoggingFormat.format(new Date(millis));
         }
     }
-}
+
+    /**
+     * Dump a currentTimeMillis style timestamp for dumpsys.
+     *
+     * @hide
+     */
+    public static void dumpTime(PrintWriter pw, long time) {
+        pw.print(sDumpDateFormat.format(new Date(time)));
+    }
+
+    /**
+     * Dump a currentTimeMillis style timestamp for dumpsys, with the delta time from now.
+     *
+     * @hide
+     */
+    public static void dumpTimeWithDelta(PrintWriter pw, long time, long now) {
+        pw.print(sDumpDateFormat.format(new Date(time)));
+        if (time == now) {
+            pw.print(" (now)");
+        } else {
+            pw.print(" (");
+            TimeUtils.formatDuration(time, now, pw);
+            pw.print(")");
+        }
+    }}
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index ea4464d..99106be 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -216,6 +216,12 @@
      * */
     public int density;
 
+    /**
+     * If the Value came from a style resource, this holds the corresponding style resource id
+     * against which the attribute was resolved.
+     */
+    public int sourceStyleResourceId;
+
     /* ------------------------------------------------------------ */
 
     /** Return the data for this value as a float.  Only use for values
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index 9f7560c..54cfc00 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -147,6 +147,9 @@
         @Override
         public void onConfigurationChanged(Configuration globalConfig) {
             final Locale locale = globalConfig.getLocales().get(0);
+            if (locale == null) {
+                return;
+            }
             if (!mLocale.equals(locale)) {
                 mLocale = locale;
                 onLocaleChanged(locale);
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 96ef8ba..ccd0fc1 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -22,6 +22,7 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.FrameInfo;
+import android.graphics.Insets;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Build;
 import android.os.Handler;
@@ -199,7 +200,7 @@
      * @hide
      */
     private static final String[] CALLBACK_TRACE_TITLES = {
-            "input", "animation", "traversal", "commit"
+            "input", "animation", "insets_animation", "traversal", "commit"
     };
 
     /**
@@ -209,18 +210,33 @@
     public static final int CALLBACK_INPUT = 0;
 
     /**
-     * Callback type: Animation callback.  Runs before traversals.
+     * Callback type: Animation callback.  Runs before {@link #CALLBACK_INSETS_ANIMATION}.
      * @hide
      */
     @TestApi
     public static final int CALLBACK_ANIMATION = 1;
 
     /**
+     * Callback type: Animation callback to handle inset updates. This is separate from
+     * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
+     * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
+     * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
+     * that contains all the combined updated insets.
+     * <p>
+     * Both input and animation may change insets, so we need to run this after these callbacks, but
+     * before traversals.
+     * <p>
+     * Runs before traversals.
+     * @hide
+     */
+    public static final int CALLBACK_INSETS_ANIMATION = 2;
+
+    /**
      * Callback type: Traversal callback.  Handles layout and draw.  Runs
      * after all other asynchronous messages have been handled.
      * @hide
      */
-    public static final int CALLBACK_TRAVERSAL = 2;
+    public static final int CALLBACK_TRAVERSAL = 3;
 
     /**
      * Callback type: Commit callback.  Handles post-draw operations for the frame.
@@ -232,7 +248,7 @@
      * to the view hierarchy state) actually took effect.
      * @hide
      */
-    public static final int CALLBACK_COMMIT = 3;
+    public static final int CALLBACK_COMMIT = 4;
 
     private static final int CALLBACK_LAST = CALLBACK_COMMIT;
 
@@ -704,6 +720,7 @@
 
             mFrameInfo.markAnimationsStart();
             doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
+            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
 
             mFrameInfo.markPerformTraversalsStart();
             doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4ead34e..f58efc9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1391,11 +1391,17 @@
          */
         public static final int HDR_TYPE_HLG = 3;
 
+        /**
+         * HDR10+ display.
+         */
+        public static final int HDR_TYPE_HDR10_PLUS = 4;
+
         /** @hide */
         @IntDef(prefix = { "HDR_TYPE_" }, value = {
                 HDR_TYPE_DOLBY_VISION,
                 HDR_TYPE_HDR10,
                 HDR_TYPE_HLG,
+                HDR_TYPE_HDR10_PLUS,
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface HdrType {}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d1115c7..658f06a 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -99,7 +99,7 @@
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
             out DisplayCutout.ParcelableWrapper displayCutout,
-            out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+            out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
             out InsetsState insetsState);
 
     /*
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 7b9f78e..ce71b07 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -45,7 +45,9 @@
  * @hide
  */
 @VisibleForTesting
-public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
+public class InsetsAnimationControlImpl implements WindowInsetsAnimationController  {
+
+    private final Rect mTmpFrame = new Rect();
 
     private final WindowInsetsAnimationControlListener mListener;
     private final SparseArray<InsetsSourceConsumer> mConsumers;
@@ -61,19 +63,23 @@
     private final InsetsState mInitialInsetsState;
     private final @InsetType int mTypes;
     private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
-
+    private final InsetsController mController;
+    private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
     private Insets mCurrentInsets;
+    private Insets mPendingInsets;
 
     @VisibleForTesting
     public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
             InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetType int types,
-            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
+            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier,
+            InsetsController controller) {
         mConsumers = consumers;
         mListener = listener;
         mTypes = types;
         mTransactionApplierSupplier = transactionApplierSupplier;
-        mInitialInsetsState = new InsetsState(state);
+        mController = controller;
+        mInitialInsetsState = new InsetsState(state, true /* copySources */);
         mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
         mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
                 null /* typeSideMap */);
@@ -83,6 +89,10 @@
 
         // TODO: Check for controllability first and wait for IME if needed.
         listener.onReady(this, types);
+
+        mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets,
+                mShownInsets);
+        mController.dispatchAnimationStarted(mAnimation);
     }
 
     @Override
@@ -108,29 +118,35 @@
 
     @Override
     public void changeInsets(Insets insets) {
-        insets = sanitize(insets);
-        final Insets offset = Insets.subtract(mShownInsets, insets);
+        mPendingInsets = sanitize(insets);
+        mController.scheduleApplyChangeInsets();
+    }
+
+    void applyChangeInsets(InsetsState state) {
+        final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
         ArrayList<SurfaceParams> params = new ArrayList<>();
         if (offset.left != 0) {
-            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
+            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params, state);
         }
         if (offset.top != 0) {
-            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
+            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params, state);
         }
         if (offset.right != 0) {
-            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
+            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params, state);
         }
         if (offset.bottom != 0) {
-            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
+            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params, state);
         }
         SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
         applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
-        mCurrentInsets = insets;
+        mCurrentInsets = mPendingInsets;
     }
 
     @Override
     public void finish(int shownTypes) {
         // TODO
+
+        mController.dispatchAnimationFinished(mAnimation);
     }
 
     private Insets calculateInsets(InsetsState state, Rect frame,
@@ -146,7 +162,7 @@
             @Nullable @InsetSide SparseIntArray typeSideMap) {
         return state.calculateInsets(frame, false /* isScreenRound */,
                 false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
-                .getSystemWindowInsets();
+                .getInsets(mTypes);
     }
 
     private Insets sanitize(Insets insets) {
@@ -154,7 +170,7 @@
     }
 
     private void updateLeashesForSide(@InsetSide int side, int inset,
-            ArrayList<SurfaceParams> surfaceParams) {
+            ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
         ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
         // TODO: Implement behavior when inset spans over multiple types
         for (int i = items.size() - 1; i >= 0; i--) {
@@ -162,24 +178,32 @@
             final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
             final SurfaceControl leash = consumer.getControl().getLeash();
             mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
-            addTranslationToMatrix(side, inset, mTmpMatrix);
+
+            mTmpFrame.set(source.getFrame());
+            addTranslationToMatrix(side, inset, mTmpMatrix, mTmpFrame);
+
+            state.getSource(source.getType()).setFrame(mTmpFrame);
             surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
         }
     }
 
-    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
+    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m, Rect frame) {
         switch (side) {
             case INSET_SIDE_LEFT:
                 m.postTranslate(-inset, 0);
+                frame.offset(-inset, 0);
                 break;
             case INSET_SIDE_TOP:
                 m.postTranslate(0, -inset);
+                frame.offset(0, -inset);
                 break;
             case INSET_SIDE_RIGHT:
                 m.postTranslate(inset, 0);
+                frame.offset(inset, 0);
                 break;
             case INSET_SIDE_BOTTOM:
                 m.postTranslate(0, inset);
+                frame.offset(0, inset);
                 break;
         }
     }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 01af37e..c2ade76 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.ArraySet;
@@ -49,9 +50,29 @@
 
     private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
     private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
+    private WindowInsets mLastInsets;
+
+    private boolean mAnimCallbackScheduled;
+
+    private final Runnable mAnimCallback;
 
     public InsetsController(ViewRootImpl viewRoot) {
         mViewRoot = viewRoot;
+        mAnimCallback = () -> {
+            mAnimCallbackScheduled = false;
+            if (mAnimationControls.isEmpty()) {
+                return;
+            }
+
+            InsetsState state = new InsetsState(mState, true /* copySources */);
+            for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
+                mAnimationControls.get(i).applyChangeInsets(state);
+            }
+            WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
+                    mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
+                    null /* typeSideMap */);
+            mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
+        };
     }
 
     void onFrameChanged(Rect frame) {
@@ -82,8 +103,9 @@
     @VisibleForTesting
     public WindowInsets calculateInsets(boolean isScreenRound,
             boolean alwaysConsumeNavBar, DisplayCutout cutout) {
-        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
+        mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
                 null /* typeSideMap */);
+        return mLastInsets;
     }
 
     /**
@@ -148,7 +170,7 @@
         }
         final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
                 mFrame, mState, listener, types,
-                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
+                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
         mAnimationControls.add(controller);
     }
 
@@ -200,4 +222,20 @@
         pw.println(prefix); pw.println("InsetsController:");
         mState.dump(prefix + "  ", pw);
     }
+
+    void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
+        mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
+    }
+
+    void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
+        mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
+    }
+
+    void scheduleApplyChangeInsets() {
+        if (!mAnimCallbackScheduled) {
+            mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
+                    mAnimCallback, null /* token*/);
+            mAnimCallbackScheduled = true;
+        }
+    }
 }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 3f8f882..cf8c0707 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.WindowInsets.Type.indexOf;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.graphics.Insets;
@@ -105,6 +107,10 @@
         set(copy);
     }
 
+    public InsetsState(InsetsState copy, boolean copySources) {
+        set(copy, copySources);
+    }
+
     /**
      * Calculates {@link WindowInsets} based on the current source configuration.
      *
@@ -114,8 +120,8 @@
     public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
             boolean alwaysConsumeNavBar, DisplayCutout cutout,
             @Nullable @InsetSide SparseIntArray typeSideMap) {
-        Insets systemInsets = Insets.NONE;
-        Insets maxInsets = Insets.NONE;
+        Insets[] typeInsetsMap = new Insets[Type.SIZE];
+        Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
         final Rect relativeFrame = new Rect(frame);
         final Rect relativeFrameMax = new Rect(frame);
         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -123,32 +129,38 @@
             if (source == null) {
                 continue;
             }
-            systemInsets = processSource(source, systemInsets, relativeFrame,
-                    false /* ignoreVisibility */, typeSideMap);
+            processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
+                    typeSideMap);
 
             // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
             // target.
             if (source.getType() != TYPE_IME) {
-                maxInsets = processSource(source, maxInsets, relativeFrameMax,
-                        true /* ignoreVisibility */, null /* typeSideMap */);
+                processSource(source, relativeFrameMax, true /* ignoreVisibility */,
+                        typeMaxInsetsMap, null /* typeSideMap */);
             }
         }
-        return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+        return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, isScreenRound,
                 alwaysConsumeNavBar, cutout);
     }
 
-    private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
-            boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
-        Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
-        insets = Insets.add(currentInsets, insets);
-        relativeFrame.inset(insets);
-        if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
-            @InsetSide int insetSide = getInsetSide(currentInsets);
+    private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
+            Insets[] typeInsetsMap, @Nullable @InsetSide SparseIntArray typeSideMap) {
+        Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
+
+        int index = indexOf(toPublicType(source.getType()));
+        Insets existing = typeInsetsMap[index];
+        if (existing == null) {
+            typeInsetsMap[index] = insets;
+        } else {
+            typeInsetsMap[index] = Insets.max(existing, insets);
+        }
+
+        if (typeSideMap != null && !Insets.NONE.equals(insets)) {
+            @InsetSide int insetSide = getInsetSide(insets);
             if (insetSide != INSET_SIDE_UNKNWON) {
-                typeSideMap.put(source.getType(), getInsetSide(currentInsets));
+                typeSideMap.put(source.getType(), getInsetSide(insets));
             }
         }
-        return insets;
     }
 
     /**
@@ -229,6 +241,21 @@
         return result;
     }
 
+    static @InsetType int toPublicType(@InternalInsetType int type) {
+        switch (type) {
+            case TYPE_TOP_BAR:
+                return Type.TOP_BAR;
+            case TYPE_SIDE_BAR_1:
+            case TYPE_SIDE_BAR_2:
+            case TYPE_SIDE_BAR_3:
+                return Type.SIDE_BARS;
+            case TYPE_IME:
+                return Type.IME;
+            default:
+                throw new IllegalArgumentException("Unknown type: " + type);
+        }
+    }
+
     public static boolean getDefaultVisibly(@InsetType int type) {
         switch (type) {
             case TYPE_TOP_BAR:
@@ -250,7 +277,7 @@
         }
     }
 
-    static String typeToString(int type) {
+    public static String typeToString(int type) {
         switch (type) {
             case TYPE_TOP_BAR:
                 return "TYPE_TOP_BAR";
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 9cced4e..f9a46b1 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -818,7 +818,10 @@
     public static final int KEYCODE_THUMBS_UP = 286;
     /** Key code constant: Thumbs down key. Apps can use this to let user downvote content. */
     public static final int KEYCODE_THUMBS_DOWN = 287;
-    /** Key code constant: Consumed by system to switch current viewer profile. */
+    /**
+     * Key code constant: Used to switch current {@link android.accounts.Account} that is
+     * consuming content. May be consumed by system to set account globally.
+     */
     public static final int KEYCODE_PROFILE_SWITCH = 288;
 
     /**
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index be81c06..dc7c343 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -29,6 +30,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -38,6 +40,9 @@
 
 import com.android.internal.R;
 
+import dalvik.system.PathClassLoader;
+import java.io.File;
+import java.lang.reflect.Method;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -72,6 +77,10 @@
     private static final String TAG = LayoutInflater.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    private static final String USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY
+        = "view.precompiled_layout_enabled";
+    private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex";
+
     /** Empty stack trace used to avoid log spam in re-throw exceptions. */
     private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
 
@@ -93,6 +102,13 @@
     private Factory2 mPrivateFactory;
     private Filter mFilter;
 
+    // Indicates whether we should try to inflate layouts using a precompiled layout instead of
+    // inflating from the XML resource.
+    private boolean mUseCompiledView;
+    // This variable holds the classloader that will be used to look for precompiled layouts. The
+    // The classloader includes the generated compiled_view.dex file.
+    private ClassLoader mPrecompiledClassLoader;
+
     @UnsupportedAppUsage
     final Object[] mConstructorArgs = new Object[2];
 
@@ -223,6 +239,7 @@
      */
     protected LayoutInflater(Context context) {
         mContext = context;
+        initPrecompiledViews();
     }
 
     /**
@@ -239,6 +256,7 @@
         mFactory2 = original.mFactory2;
         mPrivateFactory = original.mPrivateFactory;
         setFilter(original.mFilter);
+        initPrecompiledViews();
     }
 
     /**
@@ -380,6 +398,41 @@
         }
     }
 
+    private void initPrecompiledViews() {
+        // Check if precompiled layouts are enabled by a system property.
+        mUseCompiledView =
+            SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false);
+        if (!mUseCompiledView) {
+            return;
+        }
+
+        // Make sure the application allows code generation
+        ApplicationInfo appInfo = mContext.getApplicationInfo();
+        if ((appInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0
+            || appInfo.isPrivilegedApp()) {
+            mUseCompiledView = false;
+            return;
+        }
+
+        // Try to load the precompiled layout file.
+        try {
+            mPrecompiledClassLoader = mContext.getClassLoader();
+            String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME;
+            if (new File(dexFile).exists()) {
+                mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader);
+            } else {
+                // If the precompiled layout file doesn't exist, then disable precompiled
+                // layouts.
+                mUseCompiledView = false;
+            }
+        } catch (Throwable e) {
+            if (DEBUG) {
+                Log.e(TAG, "Failed to initialized precompiled views layouts", e);
+            }
+            mUseCompiledView = false;
+        }
+    }
+
     /**
      * Inflate a new view hierarchy from the specified xml resource. Throws
      * {@link InflateException} if there is an error.
@@ -436,10 +489,14 @@
         final Resources res = getContext().getResources();
         if (DEBUG) {
             Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
-                    + Integer.toHexString(resource) + ")");
+                  + Integer.toHexString(resource) + ")");
         }
 
-        final XmlResourceParser parser = res.getLayout(resource);
+        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
+        if (view != null) {
+            return view;
+        }
+        XmlResourceParser parser = res.getLayout(resource);
         try {
             return inflate(parser, root, attachToRoot);
         } finally {
@@ -447,6 +504,73 @@
         }
     }
 
+    private @Nullable
+    View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
+        boolean attachToRoot) {
+        if (!mUseCompiledView) {
+            return null;
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)");
+
+        // Try to inflate using a precompiled layout.
+        String pkg = res.getResourcePackageName(resource);
+        String layout = res.getResourceEntryName(resource);
+
+        try {
+            Class clazz = mPrecompiledClassLoader.loadClass("" + pkg + ".CompiledView");
+            Method inflater = clazz.getMethod(layout, Context.class, int.class);
+            View view = (View) inflater.invoke(null, mContext, resource);
+
+            if (view != null && root != null) {
+                // We were able to use the precompiled inflater, but now we need to do some work to
+                // attach the view to the root correctly.
+                XmlResourceParser parser = res.getLayout(resource);
+                try {
+                    AttributeSet attrs = Xml.asAttributeSet(parser);
+                    advanceToRootNode(parser);
+                    ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);
+
+                    if (attachToRoot) {
+                        root.addView(view, params);
+                    } else {
+                        view.setLayoutParams(params);
+                    }
+                } finally {
+                    parser.close();
+                }
+            }
+
+            return view;
+        } catch (Throwable e) {
+            if (DEBUG) {
+                Log.e(TAG, "Failed to use precompiled view", e);
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+        return null;
+    }
+
+    /**
+     * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is
+     * found.
+     */
+    private void advanceToRootNode(XmlPullParser parser)
+        throws InflateException, IOException, XmlPullParserException {
+        // Look for the root node.
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG &&
+            type != XmlPullParser.END_DOCUMENT) {
+            // Empty
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new InflateException(parser.getPositionDescription()
+                + ": No start tag found!");
+        }
+    }
+
     /**
      * Inflate a new view hierarchy from the specified XML node. Throws
      * {@link InflateException} if there is an error.
@@ -480,18 +604,7 @@
             View result = root;
 
             try {
-                // Look for the root node.
-                int type;
-                while ((type = parser.next()) != XmlPullParser.START_TAG &&
-                        type != XmlPullParser.END_DOCUMENT) {
-                    // Empty
-                }
-
-                if (type != XmlPullParser.START_TAG) {
-                    throw new InflateException(parser.getPositionDescription()
-                            + ": No start tag found!");
-                }
-
+                advanceToRootNode(parser);
                 final String name = parser.getName();
 
                 if (DEBUG) {
@@ -784,24 +897,8 @@
             ta.recycle();
         }
 
-        if (name.equals(TAG_1995)) {
-            // Let's party like it's 1995!
-            return new BlinkLayout(context, attrs);
-        }
-
         try {
-            View view;
-            if (mFactory2 != null) {
-                view = mFactory2.onCreateView(parent, name, context, attrs);
-            } else if (mFactory != null) {
-                view = mFactory.onCreateView(name, context, attrs);
-            } else {
-                view = null;
-            }
-
-            if (view == null && mPrivateFactory != null) {
-                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
-            }
+            View view = tryCreateView(parent, name, context, attrs);
 
             if (view == null) {
                 final Object lastContext = mConstructorArgs[0];
@@ -836,6 +933,48 @@
     }
 
     /**
+     * Tries to create a view from a tag name using the supplied attribute set.
+     *
+     * This method gives the factory provided by {@link LayoutInflater#setFactory} and
+     * {@link LayoutInflater#setFactory2} a chance to create a view. However, it does not apply all
+     * of the general view creation logic, and thus may return {@code null} for some tags. This
+     * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects.
+     *
+     * @hide for use by precompiled layouts.
+     *
+     * @param parent the parent view, used to inflate layout params
+     * @param name the name of the XML tag used to define the view
+     * @param context the inflation context for the view, typically the
+     *                {@code parent} or base layout inflater context
+     * @param attrs the attribute set for the XML tag used to define the view
+     */
+    @UnsupportedAppUsage(trackingBug = 122360734)
+    @Nullable
+    public final View tryCreateView(@Nullable View parent, @NonNull String name,
+        @NonNull Context context,
+        @NonNull AttributeSet attrs) {
+        if (name.equals(TAG_1995)) {
+            // Let's party like it's 1995!
+            return new BlinkLayout(context, attrs);
+        }
+
+        View view;
+        if (mFactory2 != null) {
+            view = mFactory2.onCreateView(parent, name, context, attrs);
+        } else if (mFactory != null) {
+            view = mFactory.onCreateView(name, context, attrs);
+        } else {
+            view = null;
+        }
+
+        if (view == null && mPrivateFactory != null) {
+            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
+        }
+
+        return view;
+    }
+
+    /**
      * Recursive method used to inflate internal (non-root) children. This
      * method calls through to {@link #rInflate} using the parent context as
      * the inflation context.
@@ -968,82 +1107,85 @@
                 + "reference. The layout ID " + value + " is not valid.");
         }
 
-        final XmlResourceParser childParser = context.getResources().getLayout(layout);
+        final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
+            (ViewGroup) parent, /*attachToRoot=*/true);
+        if (precompiled == null) {
+            final XmlResourceParser childParser = context.getResources().getLayout(layout);
 
-        try {
-            final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
+            try {
+                final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
 
-            while ((type = childParser.next()) != XmlPullParser.START_TAG &&
-                type != XmlPullParser.END_DOCUMENT) {
-                // Empty.
+                while ((type = childParser.next()) != XmlPullParser.START_TAG &&
+                    type != XmlPullParser.END_DOCUMENT) {
+                    // Empty.
+                }
+
+                if (type != XmlPullParser.START_TAG) {
+                    throw new InflateException(childParser.getPositionDescription() +
+                        ": No start tag found!");
+                }
+
+                final String childName = childParser.getName();
+
+                if (TAG_MERGE.equals(childName)) {
+                    // The <merge> tag doesn't support android:theme, so
+                    // nothing special to do here.
+                    rInflate(childParser, parent, context, childAttrs, false);
+                } else {
+                    final View view = createViewFromTag(parent, childName,
+                        context, childAttrs, hasThemeOverride);
+                    final ViewGroup group = (ViewGroup) parent;
+
+                    final TypedArray a = context.obtainStyledAttributes(
+                        attrs, R.styleable.Include);
+                    final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
+                    final int visibility = a.getInt(R.styleable.Include_visibility, -1);
+                    a.recycle();
+
+                    // We try to load the layout params set in the <include /> tag.
+                    // If the parent can't generate layout params (ex. missing width
+                    // or height for the framework ViewGroups, though this is not
+                    // necessarily true of all ViewGroups) then we expect it to throw
+                    // a runtime exception.
+                    // We catch this exception and set localParams accordingly: true
+                    // means we successfully loaded layout params from the <include>
+                    // tag, false means we need to rely on the included layout params.
+                    ViewGroup.LayoutParams params = null;
+                    try {
+                        params = group.generateLayoutParams(attrs);
+                    } catch (RuntimeException e) {
+                        // Ignore, just fail over to child attrs.
+                    }
+                    if (params == null) {
+                        params = group.generateLayoutParams(childAttrs);
+                    }
+                    view.setLayoutParams(params);
+
+                    // Inflate all children.
+                    rInflateChildren(childParser, view, childAttrs, true);
+
+                    if (id != View.NO_ID) {
+                        view.setId(id);
+                    }
+
+                    switch (visibility) {
+                        case 0:
+                            view.setVisibility(View.VISIBLE);
+                            break;
+                        case 1:
+                            view.setVisibility(View.INVISIBLE);
+                            break;
+                        case 2:
+                            view.setVisibility(View.GONE);
+                            break;
+                    }
+
+                    group.addView(view);
+                }
+            } finally {
+                childParser.close();
             }
-
-            if (type != XmlPullParser.START_TAG) {
-                throw new InflateException(childParser.getPositionDescription() +
-                    ": No start tag found!");
-            }
-
-            final String childName = childParser.getName();
-
-            if (TAG_MERGE.equals(childName)) {
-                // The <merge> tag doesn't support android:theme, so
-                // nothing special to do here.
-                rInflate(childParser, parent, context, childAttrs, false);
-            } else {
-                final View view = createViewFromTag(parent, childName,
-                    context, childAttrs, hasThemeOverride);
-                final ViewGroup group = (ViewGroup) parent;
-
-                final TypedArray a = context.obtainStyledAttributes(
-                    attrs, R.styleable.Include);
-                final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
-                final int visibility = a.getInt(R.styleable.Include_visibility, -1);
-                a.recycle();
-
-                // We try to load the layout params set in the <include /> tag.
-                // If the parent can't generate layout params (ex. missing width
-                // or height for the framework ViewGroups, though this is not
-                // necessarily true of all ViewGroups) then we expect it to throw
-                // a runtime exception.
-                // We catch this exception and set localParams accordingly: true
-                // means we successfully loaded layout params from the <include>
-                // tag, false means we need to rely on the included layout params.
-                ViewGroup.LayoutParams params = null;
-                try {
-                    params = group.generateLayoutParams(attrs);
-                } catch (RuntimeException e) {
-                    // Ignore, just fail over to child attrs.
-                }
-                if (params == null) {
-                    params = group.generateLayoutParams(childAttrs);
-                }
-                view.setLayoutParams(params);
-
-                // Inflate all children.
-                rInflateChildren(childParser, view, childAttrs, true);
-
-                if (id != View.NO_ID) {
-                    view.setId(id);
-                }
-
-                switch (visibility) {
-                    case 0:
-                        view.setVisibility(View.VISIBLE);
-                        break;
-                    case 1:
-                        view.setVisibility(View.INVISIBLE);
-                        break;
-                    case 2:
-                        view.setVisibility(View.GONE);
-                        break;
-                }
-
-                group.addView(view);
-            }
-        } finally {
-            childParser.close();
         }
-
         LayoutInflater.consumeChildElements(parser);
     }
 
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a86abe5..9d3552f 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -18,6 +18,9 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Matrix;
@@ -30,6 +33,7 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import java.lang.annotation.Retention;
 import java.util.Objects;
 
 /**
@@ -462,7 +466,7 @@
 
     /**
      * This flag indicates that the event has been generated by a gesture generator. It
-     * provides a hint to the GestureDector to not apply any touch slop.
+     * provides a hint to the GestureDetector to not apply any touch slop.
      *
      * @hide
      */
@@ -1391,6 +1395,42 @@
     };
 
     /**
+     * Classification constant: None.
+     *
+     * No additional information is available about the current motion event stream.
+     *
+     * @see #getClassification
+     */
+    public static final int CLASSIFICATION_NONE = 0;
+
+    /**
+     * Classification constant: Ambiguous gesture.
+     *
+     * The user's intent with respect to the current event stream is not yet determined.
+     * Gestural actions, such as scrolling, should be inhibited until the classification resolves
+     * to another value or the event stream ends.
+     *
+     * @see #getClassification
+     */
+    public static final int CLASSIFICATION_AMBIGUOUS_GESTURE = 1;
+
+    /**
+     * Classification constant: Deep press.
+     *
+     * The current event stream represents the user intentionally pressing harder on the screen.
+     * This classification type should be used to accelerate the long press behaviour.
+     *
+     * @see #getClassification
+     */
+    public static final int CLASSIFICATION_DEEP_PRESS = 2;
+
+    /** @hide */
+    @Retention(SOURCE)
+    @IntDef(prefix = { "CLASSIFICATION" }, value = {
+            CLASSIFICATION_NONE, CLASSIFICATION_AMBIGUOUS_GESTURE, CLASSIFICATION_DEEP_PRESS})
+    public @interface Classification {};
+
+    /**
      * Tool type constant: Unknown tool type.
      * This constant is used when the tool type is not known or is not relevant,
      * such as for a trackball or other non-pointing device.
@@ -1478,7 +1518,7 @@
 
     private static native long nativeInitialize(long nativePtr,
             int deviceId, int source, int displayId, int action, int flags, int edgeFlags,
-            int metaState, int buttonState,
+            int metaState, int buttonState, @Classification int classification,
             float xOffset, float yOffset, float xPrecision, float yPrecision,
             long downTimeNanos, long eventTimeNanos,
             int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords);
@@ -1548,6 +1588,8 @@
     @CriticalNative
     private static native void nativeSetButtonState(long nativePtr, int buttonState);
     @CriticalNative
+    private static native int nativeGetClassification(long nativePtr);
+    @CriticalNative
     private static native int nativeGetActionButton(long nativePtr);
     @CriticalNative
     private static native void nativeSetActionButton(long nativePtr, int actionButton);
@@ -1648,7 +1690,7 @@
         MotionEvent ev = obtain();
         ev.mNativePtr = nativeInitialize(ev.mNativePtr,
                 deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState,
-                0, 0, xPrecision, yPrecision,
+                CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision,
                 downTime * NS_PER_MS, eventTime * NS_PER_MS,
                 pointerCount, pointerProperties, pointerCoords);
         if (ev.mNativePtr == 0) {
@@ -1833,7 +1875,7 @@
 
             ev.mNativePtr = nativeInitialize(ev.mNativePtr,
                     deviceId, source, displayId,
-                    action, 0, edgeFlags, metaState, 0,
+                    action, 0, edgeFlags, metaState, 0 /*buttonState*/, CLASSIFICATION_NONE,
                     0, 0, xPrecision, yPrecision,
                     downTime * NS_PER_MS, eventTime * NS_PER_MS,
                     1, pp, pc);
@@ -2539,6 +2581,18 @@
     }
 
     /**
+     * Returns the classification for the current gesture.
+     * The classification may change as more events become available for the same gesture.
+     *
+     * @see #CLASSIFICATION_NONE
+     * @see #CLASSIFICATION_AMBIGUOUS_GESTURE
+     * @see #CLASSIFICATION_DEEP_PRESS
+     */
+    public @Classification int getClassification() {
+        return nativeGetClassification(mNativePtr);
+    }
+
+    /**
      * Gets which button has been modified during a press or release action.
      *
      * For actions other than {@link #ACTION_BUTTON_PRESS} and {@link #ACTION_BUTTON_RELEASE}
@@ -3172,7 +3226,7 @@
     /**
      * Adds all of the movement samples of the specified event to this one if
      * it is compatible.  To be compatible, the event must have the same device id,
-     * source, display id, action, flags, pointer count, pointer properties.
+     * source, display id, action, flags, classification, pointer count, pointer properties.
      *
      * Only applies to {@link #ACTION_MOVE} or {@link #ACTION_HOVER_MOVE} events.
      *
@@ -3194,7 +3248,9 @@
         if (nativeGetDeviceId(mNativePtr) != nativeGetDeviceId(event.mNativePtr)
                 || nativeGetSource(mNativePtr) != nativeGetSource(event.mNativePtr)
                 || nativeGetDisplayId(mNativePtr) != nativeGetDisplayId(event.mNativePtr)
-                || nativeGetFlags(mNativePtr) != nativeGetFlags(event.mNativePtr)) {
+                || nativeGetFlags(mNativePtr) != nativeGetFlags(event.mNativePtr)
+                || nativeGetClassification(mNativePtr)
+                        != nativeGetClassification(event.mNativePtr)) {
             return false;
         }
 
@@ -3282,7 +3338,7 @@
                     nativeGetDisplayId(mNativePtr),
                     nativeGetAction(mNativePtr), nativeGetFlags(mNativePtr),
                     nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
-                    nativeGetButtonState(mNativePtr),
+                    nativeGetButtonState(mNativePtr), nativeGetClassification(mNativePtr),
                     nativeGetXOffset(mNativePtr), nativeGetYOffset(mNativePtr),
                     nativeGetXPrecision(mNativePtr), nativeGetYPrecision(mNativePtr),
                     nativeGetDownTimeNanos(mNativePtr),
@@ -3376,7 +3432,7 @@
                             nativeGetDisplayId(mNativePtr),
                             newAction, nativeGetFlags(mNativePtr),
                             nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
-                            nativeGetButtonState(mNativePtr),
+                            nativeGetButtonState(mNativePtr), nativeGetClassification(mNativePtr),
                             nativeGetXOffset(mNativePtr), nativeGetYOffset(mNativePtr),
                             nativeGetXPrecision(mNativePtr), nativeGetYPrecision(mNativePtr),
                             nativeGetDownTimeNanos(mNativePtr), eventTimeNanos,
@@ -3409,6 +3465,8 @@
         }
 
         appendUnless("0", msg, ", buttonState=", MotionEvent.buttonStateToString(getButtonState()));
+        appendUnless(classificationToString(CLASSIFICATION_NONE), msg, ", classification=",
+                classificationToString(getClassification()));
         appendUnless("0", msg, ", metaState=", KeyEvent.metaStateToString(getMetaState()));
         appendUnless("0", msg, ", flags=0x", Integer.toHexString(getFlags()));
         appendUnless("0", msg, ", edgeFlags=0x", Integer.toHexString(getEdgeFlags()));
@@ -3547,6 +3605,26 @@
     }
 
     /**
+     * Returns a string that represents the symbolic name of the specified classification.
+     *
+     * @param classification The classification type.
+     * @return The symbolic name of this classification.
+     * @hide
+     */
+    public static String classificationToString(@Classification int classification) {
+        switch (classification) {
+            case CLASSIFICATION_NONE:
+                return "NONE";
+            case CLASSIFICATION_AMBIGUOUS_GESTURE:
+                return "AMBIGUOUS_GESTURE";
+            case CLASSIFICATION_DEEP_PRESS:
+                return "DEEP_PRESS";
+
+        }
+        return "NONE";
+    }
+
+    /**
      * Returns a string that represents the symbolic name of the specified tool type
      * such as "TOOL_TYPE_FINGER" or an equivalent numeric constant such as "42" if unknown.
      *
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index cf11fd0..c3d13bd 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -31,6 +31,7 @@
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -144,7 +145,8 @@
     public static final int TYPE_DEFAULT = TYPE_ARROW;
 
     private static final PointerIcon gNullIcon = new PointerIcon(TYPE_NULL);
-    private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>();
+    private static final SparseArray<SparseArray<PointerIcon>> gSystemIconsByDisplay =
+            new SparseArray<SparseArray<PointerIcon>>();
     private static boolean sUseLargeIcons = false;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -163,6 +165,12 @@
     @UnsupportedAppUsage
     private int mDurationPerFrame;
 
+    /**
+     * Listener for displays lifecycle.
+     * @hide
+     */
+    private static DisplayManager.DisplayListener sDisplayListener;
+
     private PointerIcon(int type) {
         mType = type;
     }
@@ -211,7 +219,19 @@
             return gNullIcon;
         }
 
-        PointerIcon icon = gSystemIcons.get(type);
+        if (sDisplayListener == null) {
+            registerDisplayListener(context);
+        }
+
+        final int displayId = context.getDisplayId();
+        SparseArray<PointerIcon> systemIcons = gSystemIconsByDisplay.get(displayId);
+        if (systemIcons == null) {
+            systemIcons = new SparseArray<>();
+            gSystemIconsByDisplay.put(displayId, systemIcons);
+        }
+
+        PointerIcon icon = systemIcons.get(type);
+        // Reload if not in the same display.
         if (icon != null) {
             return icon;
         }
@@ -240,7 +260,7 @@
         } else {
             icon.loadResource(context, context.getResources(), resourceId);
         }
-        gSystemIcons.append(type, icon);
+        systemIcons.append(type, icon);
         return icon;
     }
 
@@ -250,7 +270,7 @@
      */
     public static void setUseLargeIcons(boolean use) {
         sUseLargeIcons = use;
-        gSystemIcons.clear();
+        gSystemIconsByDisplay.clear();
     }
 
     /**
@@ -576,4 +596,30 @@
                 return 0;
         }
     }
+
+    /**
+     * Manage system icon cache handled by display lifecycle.
+     * @param context The context.
+     */
+    private static void registerDisplayListener(@NonNull Context context) {
+        sDisplayListener = new DisplayManager.DisplayListener() {
+            @Override
+            public void onDisplayAdded(int displayId) {
+            }
+
+            @Override
+            public void onDisplayRemoved(int displayId) {
+                gSystemIconsByDisplay.remove(displayId);
+            }
+
+            @Override
+            public void onDisplayChanged(int displayId) {
+                gSystemIconsByDisplay.remove(displayId);
+            }
+        };
+
+        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+        displayManager.registerDisplayListener(sDisplayListener, null /* handler */);
+    }
+
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 1a708e8..5e98236 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -30,6 +30,7 @@
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
@@ -58,7 +59,7 @@
 
 /**
  * SurfaceControl
- *  @hide
+ * @hide
  */
 public class SurfaceControl implements Parcelable {
     private static final String TAG = "SurfaceControl";
@@ -67,6 +68,7 @@
             int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid)
             throws OutOfResourcesException;
     private static native long nativeReadFromParcel(Parcel in);
+    private static native long nativeCopyFromSurfaceControl(long nativeObject);
     private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeDestroy(long nativeObject);
@@ -140,6 +142,7 @@
     private static native int nativeGetActiveConfig(IBinder displayToken);
     private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
+    private static native int[] nativeGetCompositionDataspaces();
     private static native int nativeGetActiveColorMode(IBinder displayToken);
     private static native boolean nativeSetActiveColorMode(IBinder displayToken,
             int colorMode);
@@ -157,7 +160,6 @@
     private static native void nativeSeverChildren(long transactionObj, long nativeObject);
     private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
             int scalingMode);
-    private static native void nativeDestroy(long transactionObj, long nativeObject);
     private static native IBinder nativeGetHandle(long nativeObject);
     private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
 
@@ -167,9 +169,10 @@
             InputWindowHandle handle);
     private static native void nativeTransferTouchFocus(long transactionObj, IBinder fromToken,
             IBinder toToken);
+    private static native boolean nativeGetProtectedContentSupport();
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
-    private final String mName;
+    private String mName;
     long mNativeObject; // package visibility only for Surface.java access
 
     // TODO: Move this to native.
@@ -186,6 +189,7 @@
 
     /**
      * Surface creation flag: Surface is created hidden
+     * @hide
      */
     @UnsupportedAppUsage
     public static final int HIDDEN = 0x00000004;
@@ -196,7 +200,7 @@
      * from another process. In particular, screenshots and VNC servers will
      * be disabled, but other measures can take place, for instance the
      * surface might not be hardware accelerated.
-     *
+     * @hide
      */
     public static final int SECURE = 0x00000080;
 
@@ -220,7 +224,7 @@
      * pixel.
      * <p>
      * In some rare situations, a non pre-multiplied surface is preferable.
-     *
+     * @hide
      */
     public static final int NON_PREMULTIPLIED = 0x00000100;
 
@@ -240,6 +244,7 @@
      * </ul>
      * If the underlying buffer lacks an alpha channel, the OPAQUE flag is effectively
      * set automatically.
+     * @hide
      */
     public static final int OPAQUE = 0x00000400;
 
@@ -248,6 +253,7 @@
      * external display sink. If a hardware-protected path is not available,
      * then this surface will not be displayed on the external sink.
      *
+     * @hide
      */
     public static final int PROTECTED_APP = 0x00000800;
 
@@ -255,6 +261,7 @@
 
     /**
      * Surface creation flag: Window represents a cursor glyph.
+     * @hide
      */
     public static final int CURSOR_WINDOW = 0x00002000;
 
@@ -262,6 +269,7 @@
      * Surface creation flag: Creates a normal surface.
      * This is the default.
      *
+     * @hide
      */
     public static final int FX_SURFACE_NORMAL   = 0x00000000;
 
@@ -271,6 +279,7 @@
      * in {@link #setAlpha}.  It is an error to lock a Dim surface, since it
      * doesn't have a backing store.
      *
+     * @hide
      */
     public static final int FX_SURFACE_DIM = 0x00020000;
 
@@ -278,12 +287,14 @@
      * Surface creation flag: Creates a container surface.
      * This surface will have no buffers and will only be used
      * as a container for other surfaces, or for its InputInfo.
+     * @hide
      */
     public static final int FX_SURFACE_CONTAINER = 0x00080000;
 
     /**
      * Mask used for FX values above.
      *
+     * @hide
      */
     public static final int FX_SURFACE_MASK = 0x000F0000;
 
@@ -309,12 +320,14 @@
     /**
      * Built-in physical display id: Main display.
      * Use only with {@link SurfaceControl#getBuiltInDisplay(int)}.
+     * @hide
      */
     public static final int BUILT_IN_DISPLAY_ID_MAIN = 0;
 
     /**
      * Built-in physical display id: Attached HDMI display.
      * Use only with {@link SurfaceControl#getBuiltInDisplay(int)}.
+     * @hide
      */
     public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
 
@@ -323,30 +336,35 @@
     /**
      * Display power mode off: used while blanking the screen.
      * Use only with {@link SurfaceControl#setDisplayPowerMode}.
+     * @hide
      */
     public static final int POWER_MODE_OFF = 0;
 
     /**
      * Display power mode doze: used while putting the screen into low power mode.
      * Use only with {@link SurfaceControl#setDisplayPowerMode}.
+     * @hide
      */
     public static final int POWER_MODE_DOZE = 1;
 
     /**
      * Display power mode normal: used while unblanking the screen.
      * Use only with {@link SurfaceControl#setDisplayPowerMode}.
+     * @hide
      */
     public static final int POWER_MODE_NORMAL = 2;
 
     /**
      * Display power mode doze: used while putting the screen into a suspended
      * low power mode.  Use only with {@link SurfaceControl#setDisplayPowerMode}.
+     * @hide
      */
     public static final int POWER_MODE_DOZE_SUSPEND = 3;
 
     /**
      * Display power mode on: used while putting the screen into a suspended
      * full power mode.  Use only with {@link SurfaceControl#setDisplayPowerMode}.
+     * @hide
      */
     public static final int POWER_MODE_ON_SUSPEND = 4;
 
@@ -360,7 +378,32 @@
     public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
 
     /**
+     * internal representation of how to interpret pixel value, used only to convert to ColorSpace.
+     */
+    private static final int INTERNAL_DATASPACE_SRGB = 142671872;
+    private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
+    private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
+
+    private void assignNativeObject(long nativeObject) {
+        if (mNativeObject != 0) {
+            release();
+        }
+        mNativeObject = nativeObject;
+    }
+
+    /**
+     * @hide
+     */
+    public void copyFrom(SurfaceControl other) {
+        mName = other.mName;
+        mWidth = other.mWidth;
+        mHeight = other.mHeight;
+        assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject));
+    }
+
+    /**
      * Builder class for {@link SurfaceControl} objects.
+     * @hide
      */
     public static class Builder {
         private SurfaceSession mSession;
@@ -377,6 +420,7 @@
          * Begin building a SurfaceControl with a given {@link SurfaceSession}.
          *
          * @param session The {@link SurfaceSession} with which to eventually construct the surface.
+         * @hide
          */
         public Builder(SurfaceSession session) {
             mSession = session;
@@ -384,6 +428,7 @@
 
         /**
          * Construct a new {@link SurfaceControl} with the set parameters.
+         * @hide
          */
         public SurfaceControl build() {
             if (mWidth < 0 || mHeight < 0) {
@@ -402,6 +447,7 @@
          * Set a debugging-name for the SurfaceControl.
          *
          * @param name A name to identify the Surface in debugging.
+         * @hide
          */
         public Builder setName(String name) {
             mName = name;
@@ -413,6 +459,7 @@
          *
          * @param width The buffer width in pixels.
          * @param height The buffer height in pixels.
+         * @hide
          */
         public Builder setBufferSize(int width, int height) {
             if (width < 0 || height < 0) {
@@ -427,6 +474,7 @@
         /**
          * Set the pixel format of the controlled surface's buffers, using constants from
          * {@link android.graphics.PixelFormat}.
+         * @hide
          */
         public Builder setFormat(@PixelFormat.Format int format) {
             mFormat = format;
@@ -440,6 +488,7 @@
          * not be displayed.
          *
          * @param protectedContent Whether to require a protected sink.
+         * @hide
          */
         public Builder setProtected(boolean protectedContent) {
             if (protectedContent) {
@@ -455,6 +504,7 @@
          * will prevent the surfaces content from being copied by another process. In
          * particular screenshots and VNC servers will be disabled. This is however
          * not a complete prevention of readback as {@link #setProtected}.
+         * @hide
          */
         public Builder setSecure(boolean secure) {
             if (secure) {
@@ -487,6 +537,7 @@
          * If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
          * were set automatically.
          * @param opaque Whether the Surface is OPAQUE.
+         * @hide
          */
         public Builder setOpaque(boolean opaque) {
             if (opaque) {
@@ -505,6 +556,7 @@
          * of the parent.
          *
          * @param parent The parent control.
+         * @hide
          */
         public Builder setParent(SurfaceControl parent) {
             mParent = parent;
@@ -520,6 +572,7 @@
          *
          * @param windowType A window-type
          * @param ownerUid UID of the window owner.
+         * @hide
          */
         public Builder setMetadata(int windowType, int ownerUid) {
             if (UserHandle.getAppId(Process.myUid()) != Process.SYSTEM_UID) {
@@ -538,6 +591,7 @@
          * solid color (that is, solid before plane alpha). Currently that color is black.
          *
          * @param isColorLayer Whether to create a color layer.
+         * @hide
          */
         public Builder setColorLayer(boolean isColorLayer) {
             if (isColorLayer) {
@@ -559,6 +613,7 @@
          * as a parent of renderable layers.
          *
          * @param isContainerLayer Whether to create a container layer.
+         * @hide
          */
         public Builder setContainerLayer(boolean isContainerLayer) {
             if (isContainerLayer) {
@@ -578,6 +633,7 @@
          *
          * TODO: Finish conversion to individual builder methods?
          * @param flags The combined flags
+         * @hide
          */
         public Builder setFlags(int flags) {
             mFlags = flags;
@@ -646,9 +702,11 @@
         mCloseGuard.open("release");
     }
 
-    // This is a transfer constructor, useful for transferring a live SurfaceControl native
-    // object to another Java wrapper which could have some different behavior, e.g.
-    // event logging.
+    /** This is a transfer constructor, useful for transferring a live SurfaceControl native
+     * object to another Java wrapper which could have some different behavior, e.g.
+     * event logging.
+     * @hide
+     */
     public SurfaceControl(SurfaceControl other) {
         mName = other.mName;
         mWidth = other.mWidth;
@@ -660,27 +718,62 @@
     }
 
     private SurfaceControl(Parcel in) {
-        mName = in.readString();
-        mWidth = in.readInt();
-        mHeight = in.readInt();
-        mNativeObject = nativeReadFromParcel(in);
-        if (mNativeObject == 0) {
-            throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
-        }
+        readFromParcel(in);
         mCloseGuard.open("release");
     }
 
+    /**
+     * @hide
+     */
+    public SurfaceControl() {
+        mCloseGuard.open("release");
+    }
+
+    /**
+     * @hide
+     */
+    public void readFromParcel(Parcel in) {
+        if (in == null) {
+            throw new IllegalArgumentException("source must not be null");
+        }
+
+        mName = in.readString();
+        mWidth = in.readInt();
+        mHeight = in.readInt();
+
+        long object = 0;
+        if (in.readInt() != 0) {
+            object = nativeReadFromParcel(in);
+        }
+        assignNativeObject(object);
+    }
+
+    /**
+     * @hide
+     */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /**
+     * @hide
+     */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mName);
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
+        if (mNativeObject == 0) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+        }
         nativeWriteToParcel(mNativeObject, dest);
+
+        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+            release();
+        }
     }
 
     /**
@@ -698,6 +791,9 @@
         proto.end(token);
     }
 
+    /**
+     * @hide
+     */
     public static final Creator<SurfaceControl> CREATOR
             = new Creator<SurfaceControl>() {
         public SurfaceControl createFromParcel(Parcel in) {
@@ -709,6 +805,9 @@
         }
     };
 
+    /**
+     * @hide
+     */
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -727,6 +826,7 @@
      * Release the local reference to the server-side surface.
      * Always call release() when you're done with a Surface.
      * This will make the surface invalid.
+     * @hide
      */
     public void release() {
         if (mNativeObject != 0) {
@@ -740,6 +840,7 @@
      * Free all server-side state associated with this surface and
      * release this object's reference.  This method can only be
      * called from the process that created the service.
+     * @hide
      */
     public void destroy() {
         if (mNativeObject != 0) {
@@ -751,6 +852,7 @@
 
     /**
      * Disconnect any client still connected to the surface.
+     * @hide
      */
     public void disconnect() {
         if (mNativeObject != 0) {
@@ -763,12 +865,21 @@
                 "mNativeObject is null. Have you called release() already?");
     }
 
+    /**
+     * @hide
+     */
+    public boolean isValid() {
+        return mNativeObject != 0;
+    }
+
     /*
      * set surface parameters.
      * needs to be inside open/closeTransaction block
      */
 
-    /** start a transaction */
+    /** start a transaction
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void openTransaction() {
         synchronized (SurfaceControl.class) {
@@ -797,6 +908,7 @@
      * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
      * <p>
      * This is a utility for interop with legacy-code and will go away with the Global Transaction.
+     * @hide
      */
     @Deprecated
     public static void mergeToGlobalTransaction(Transaction t) {
@@ -805,46 +917,69 @@
         }
     }
 
-    /** end a transaction */
+    /** end a transaction 
+     * @hide 
+     */
     @UnsupportedAppUsage
     public static void closeTransaction() {
         closeTransaction(false);
     }
 
+    /**
+     * @hide
+     */
     public static void closeTransactionSync() {
         closeTransaction(true);
     }
 
+    /**
+     * @hide
+     */
     public void deferTransactionUntil(IBinder handle, long frame) {
         synchronized(SurfaceControl.class) {
             sGlobalTransaction.deferTransactionUntil(this, handle, frame);
         }
     }
 
+    /**
+     * @hide
+     */
     public void deferTransactionUntil(Surface barrier, long frame) {
         synchronized(SurfaceControl.class) {
             sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
         }
     }
 
+    /**
+     * @hide
+     */
     public void reparentChildren(IBinder newParentHandle) {
         synchronized(SurfaceControl.class) {
             sGlobalTransaction.reparentChildren(this, newParentHandle);
         }
     }
 
+    /**
+     * @hide
+     */
     public void reparent(IBinder newParentHandle) {
         synchronized(SurfaceControl.class) {
             sGlobalTransaction.reparent(this, newParentHandle);
         }
     }
 
+    /**
+     * @hide
+     */
     public void detachChildren() {
         synchronized(SurfaceControl.class) {
             sGlobalTransaction.detachChildren(this);
         }
     }
 
+    /**
+     * @hide
+     */
     public void setOverrideScalingMode(int scalingMode) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -852,16 +987,25 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public IBinder getHandle() {
         return nativeGetHandle(mNativeObject);
     }
 
+    /**
+     * @hide
+     */
     public static void setAnimationTransaction() {
         synchronized (SurfaceControl.class) {
             sGlobalTransaction.setAnimationTransaction();
         }
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void setLayer(int zorder) {
         checkNotReleased();
@@ -870,6 +1014,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setRelativeLayer(SurfaceControl relativeTo, int zorder) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -877,6 +1024,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void setPosition(float x, float y) {
         checkNotReleased();
@@ -885,6 +1035,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setGeometryAppliesWithResize() {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -892,6 +1045,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setBufferSize(int w, int h) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -899,6 +1055,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void hide() {
         checkNotReleased();
@@ -907,6 +1066,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void show() {
         checkNotReleased();
@@ -915,6 +1077,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setTransparentRegionHint(Region region) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -922,24 +1087,39 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public boolean clearContentFrameStats() {
         checkNotReleased();
         return nativeClearContentFrameStats(mNativeObject);
     }
 
+    /**
+     * @hide
+     */
     public boolean getContentFrameStats(WindowContentFrameStats outStats) {
         checkNotReleased();
         return nativeGetContentFrameStats(mNativeObject, outStats);
     }
 
+    /**
+     * @hide
+     */
     public static boolean clearAnimationFrameStats() {
         return nativeClearAnimationFrameStats();
     }
 
+    /**
+     * @hide
+     */
     public static boolean getAnimationFrameStats(WindowAnimationFrameStats outStats) {
         return nativeGetAnimationFrameStats(outStats);
     }
 
+    /**
+     * @hide
+     */
     public void setAlpha(float alpha) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -947,6 +1127,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setColor(@Size(3) float[] color) {
         checkNotReleased();
         synchronized (SurfaceControl.class) {
@@ -954,6 +1137,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -966,6 +1152,7 @@
      *
      * @param matrix The matrix to apply.
      * @param float9 An array of 9 floats to be used to extract the values from the matrix.
+     * @hide
      */
     public void setMatrix(Matrix matrix, float[] float9) {
         checkNotReleased();
@@ -981,6 +1168,7 @@
      * Sets the color transform for the Surface.
      * @param matrix A float array with 9 values represents a 3x3 transform matrix
      * @param translation A float array with 3 values represents a translation vector
+     * @hide
      */
     public void setColorTransform(@Size(9) float[] matrix, @Size(3) float[] translation) {
         checkNotReleased();
@@ -996,6 +1184,7 @@
      * constrained by the size of its parent bounds.
      *
      * @param crop Bounds of the crop to apply.
+     * @hide
      */
     public void setWindowCrop(Rect crop) {
         checkNotReleased();
@@ -1009,6 +1198,7 @@
      *
      * @param width width of crop rect
      * @param height height of crop rect
+     * @hide
      */
     public void setWindowCrop(int width, int height) {
         checkNotReleased();
@@ -1021,6 +1211,7 @@
      * Sets the corner radius of a {@link SurfaceControl}.
      *
      * @param cornerRadius Corner radius in pixels.
+     * @hide
      */
     public void setCornerRadius(float cornerRadius) {
         checkNotReleased();
@@ -1029,6 +1220,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setLayerStack(int layerStack) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -1036,6 +1230,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setOpaque(boolean isOpaque) {
         checkNotReleased();
 
@@ -1044,6 +1241,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void setSecure(boolean isSecure) {
         checkNotReleased();
 
@@ -1052,18 +1252,27 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public int getWidth() {
         synchronized (mSizeLock) {
             return mWidth;
         }
     }
 
+    /**
+     * @hide
+     */
     public int getHeight() {
         synchronized (mSizeLock) {
             return mHeight;
         }
     }
 
+    /**
+     * @hide
+     */
     @Override
     public String toString() {
         return "Surface(name=" + mName + ")/@0x" +
@@ -1079,38 +1288,85 @@
      * Describes the properties of a physical display known to surface flinger.
      */
     public static final class PhysicalDisplayInfo {
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public int width;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public int height;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public float refreshRate;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public float density;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public float xDpi;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public float yDpi;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public boolean secure;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public long appVsyncOffsetNanos;
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public long presentationDeadlineNanos;
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public PhysicalDisplayInfo() {
         }
 
+        /**
+         * @hide
+         */
         public PhysicalDisplayInfo(PhysicalDisplayInfo other) {
             copyFrom(other);
         }
 
+        /**
+         * @hide
+         */
         @Override
         public boolean equals(Object o) {
             return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o);
         }
 
+        /**
+         * @hide
+         */
         public boolean equals(PhysicalDisplayInfo other) {
             return other != null
                     && width == other.width
@@ -1124,11 +1380,17 @@
                     && presentationDeadlineNanos == other.presentationDeadlineNanos;
         }
 
+        /**
+         * @hide
+         */
         @Override
         public int hashCode() {
             return 0; // don't care
         }
 
+        /**
+         * @hide
+         */
         public void copyFrom(PhysicalDisplayInfo other) {
             width = other.width;
             height = other.height;
@@ -1141,7 +1403,9 @@
             presentationDeadlineNanos = other.presentationDeadlineNanos;
         }
 
-        // For debugging purposes
+        /**
+         * @hide
+         */
         @Override
         public String toString() {
             return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
@@ -1151,6 +1415,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public static void setDisplayPowerMode(IBinder displayToken, int mode) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1158,6 +1425,9 @@
         nativeSetDisplayPowerMode(displayToken, mode);
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static SurfaceControl.PhysicalDisplayInfo[] getDisplayConfigs(IBinder displayToken) {
         if (displayToken == null) {
@@ -1166,6 +1436,9 @@
         return nativeGetDisplayConfigs(displayToken);
     }
 
+    /**
+     * @hide
+     */
     public static int getActiveConfig(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1212,6 +1485,9 @@
     }
 
 
+    /**
+     * @hide
+     */
     public static boolean setActiveConfig(IBinder displayToken, int id) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1219,6 +1495,9 @@
         return nativeSetActiveConfig(displayToken, id);
     }
 
+    /**
+     * @hide
+     */
     public static int[] getDisplayColorModes(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1226,6 +1505,9 @@
         return nativeGetDisplayColorModes(displayToken);
     }
 
+    /**
+     * @hide
+     */
     public static int getActiveColorMode(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1233,6 +1515,9 @@
         return nativeGetActiveColorMode(displayToken);
     }
 
+    /**
+     * @hide
+     */
     public static boolean setActiveColorMode(IBinder displayToken, int colorMode) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1240,6 +1525,38 @@
         return nativeSetActiveColorMode(displayToken, colorMode);
     }
 
+    /**
+     * Returns an array of color spaces with 2 elements. The first color space is the
+     * default color space and second one is wide color gamut color space.
+     * @hide
+     */
+    public static ColorSpace[] getCompositionColorSpaces() {
+        int[] dataspaces = nativeGetCompositionDataspaces();
+        ColorSpace srgb = ColorSpace.get(ColorSpace.Named.SRGB);
+        ColorSpace[] colorSpaces = { srgb, srgb };
+        if (dataspaces.length == 2) {
+            for (int i = 0; i < 2; ++i) {
+                switch(dataspaces[i]) {
+                    case INTERNAL_DATASPACE_DISPLAY_P3:
+                        colorSpaces[i] = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
+                        break;
+                    case INTERNAL_DATASPACE_SCRGB:
+                        colorSpaces[i] = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
+                        break;
+                    case INTERNAL_DATASPACE_SRGB:
+                    // Other dataspace is not recognized, use SRGB color space instead,
+                    // the default value of the array is already SRGB, thus do nothing.
+                    default:
+                        break;
+                }
+            }
+        }
+        return colorSpaces;
+    }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void setDisplayProjection(IBinder displayToken,
             int orientation, Rect layerStackRect, Rect displayRect) {
@@ -1249,6 +1566,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
         synchronized (SurfaceControl.class) {
@@ -1256,6 +1576,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void setDisplaySurface(IBinder displayToken, Surface surface) {
         synchronized (SurfaceControl.class) {
@@ -1263,12 +1586,18 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public static void setDisplaySize(IBinder displayToken, int width, int height) {
         synchronized (SurfaceControl.class) {
             sGlobalTransaction.setDisplaySize(displayToken, width, height);
         }
     }
 
+    /**
+     * @hide
+     */
     public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1276,6 +1605,9 @@
         return nativeGetHdrCapabilities(displayToken);
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static IBinder createDisplay(String name, boolean secure) {
         if (name == null) {
@@ -1284,6 +1616,9 @@
         return nativeCreateDisplay(name, secure);
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void destroyDisplay(IBinder displayToken) {
         if (displayToken == null) {
@@ -1292,6 +1627,9 @@
         nativeDestroyDisplay(displayToken);
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static IBinder getBuiltInDisplay(int builtInDisplayId) {
         return nativeGetBuiltInDisplay(builtInDisplayId);
@@ -1299,6 +1637,7 @@
 
     /**
      * @see SurfaceControl#screenshot(IBinder, Surface, Rect, int, int, boolean, int)
+     * @hide
      */
     public static void screenshot(IBinder display, Surface consumer) {
         screenshot(display, consumer, new Rect(), 0, 0, false, 0);
@@ -1309,6 +1648,7 @@
      *
      * @param consumer The {@link Surface} to take the screenshot into.
      * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)
+     * @hide
      */
     public static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width,
             int height, boolean useIdentityTransform, int rotation) {
@@ -1327,6 +1667,7 @@
 
     /**
      * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
+     * @hide
      */
     @UnsupportedAppUsage
     public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
@@ -1344,6 +1685,7 @@
      * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
      *
      * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
+     * @hide
      */
     @UnsupportedAppUsage
     public static Bitmap screenshot(Rect sourceCrop, int width, int height,
@@ -1387,6 +1729,7 @@
      *                             this is useful for returning screenshots that are independent of
      *                             device orientation.
      * @return Returns a GraphicBuffer that contains the captured content.
+     * @hide
      */
     public static GraphicBuffer screenshotToBuffer(IBinder display, Rect sourceCrop, int width,
             int height, boolean useIdentityTransform, int rotation) {
@@ -1418,12 +1761,24 @@
      *                         screen will be scaled up/down.
      *
      * @return Returns a GraphicBuffer that contains the layer capture.
+     * @hide
      */
     public static GraphicBuffer captureLayers(IBinder layerHandleToken, Rect sourceCrop,
             float frameScale) {
         return nativeCaptureLayers(layerHandleToken, sourceCrop, frameScale);
     }
 
+    /**
+     * Returns whether protected content is supported in GPU composition.
+     * @hide
+     */
+    public static boolean getProtectedContentSupport() {
+        return nativeGetProtectedContentSupport();
+    }
+
+    /**
+     * @hide
+     */
     public static class Transaction implements Closeable {
         public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
                 Transaction.class.getClassLoader(),
@@ -1433,6 +1788,9 @@
         private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
         Runnable mFreeNativeResources;
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction() {
             mNativeObject = nativeCreateTransaction();
@@ -1443,6 +1801,7 @@
         /**
          * Apply the transaction, clearing it's state, and making it usable
          * as a new transaction.
+         * @hide
          */
         @UnsupportedAppUsage
         public void apply() {
@@ -1452,6 +1811,7 @@
         /**
          * Close the transaction, if the transaction was not already applied this will cancel the
          * transaction.
+         * @hide
          */
         @Override
         public void close() {
@@ -1461,6 +1821,7 @@
 
         /**
          * Jankier version of apply. Avoid use (b/28068298).
+         * @hide
          */
         public void apply(boolean sync) {
             applyResizedSurfaces();
@@ -1479,6 +1840,9 @@
             mResizedSurfaces.clear();
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction show(SurfaceControl sc) {
             sc.checkNotReleased();
@@ -1486,6 +1850,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction hide(SurfaceControl sc) {
             sc.checkNotReleased();
@@ -1493,6 +1860,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction setPosition(SurfaceControl sc, float x, float y) {
             sc.checkNotReleased();
@@ -1500,6 +1870,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
             sc.checkNotReleased();
@@ -1508,6 +1881,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction setLayer(SurfaceControl sc, int z) {
             sc.checkNotReleased();
@@ -1515,6 +1891,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) {
             sc.checkNotReleased();
             nativeSetRelativeLayer(mNativeObject, sc.mNativeObject,
@@ -1522,6 +1901,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) {
             sc.checkNotReleased();
             nativeSetTransparentRegionHint(mNativeObject,
@@ -1529,6 +1911,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction setAlpha(SurfaceControl sc, float alpha) {
             sc.checkNotReleased();
@@ -1536,6 +1921,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
             sc.checkNotReleased();
             nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);
@@ -1551,12 +1939,16 @@
          * @param fromToken The token of a window that currently has touch focus.
          * @param toToken The token of the window that should receive touch focus in
          * place of the first.
+         * @hide
          */
         public Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
             nativeTransferTouchFocus(mNativeObject, fromToken, toToken);
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction setMatrix(SurfaceControl sc,
                 float dsdx, float dtdx, float dtdy, float dsdy) {
@@ -1566,6 +1958,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) {
             matrix.getValues(float9);
@@ -1579,6 +1974,7 @@
          * Sets the color transform for the Surface.
          * @param matrix A float array with 9 values represents a 3x3 transform matrix
          * @param translation A float array with 3 values represents a translation vector
+         * @hide
          */
         public Transaction setColorTransform(SurfaceControl sc, @Size(9) float[] matrix,
                 @Size(3) float[] translation) {
@@ -1587,6 +1983,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
             sc.checkNotReleased();
@@ -1600,6 +1999,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setWindowCrop(SurfaceControl sc, int width, int height) {
             sc.checkNotReleased();
             nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, width, height);
@@ -1611,6 +2013,7 @@
          * @param sc SurfaceControl
          * @param cornerRadius Corner radius in pixels.
          * @return Itself.
+         * @hide
          */
         @UnsupportedAppUsage
         public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
@@ -1621,6 +2024,9 @@
         }
 
         @UnsupportedAppUsage
+        /**
+         * @hide
+         */
         public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
             sc.checkNotReleased();
             nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
@@ -1628,6 +2034,9 @@
         }
 
         @UnsupportedAppUsage
+        /**
+         * @hide
+         */
         public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
                 long frameNumber) {
             if (frameNumber < 0) {
@@ -1639,6 +2048,9 @@
         }
 
         @UnsupportedAppUsage
+        /**
+         * @hide
+         */
         public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
                 long frameNumber) {
             if (frameNumber < 0) {
@@ -1650,13 +2062,18 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction reparentChildren(SurfaceControl sc, IBinder newParentHandle) {
             sc.checkNotReleased();
             nativeReparentChildren(mNativeObject, sc.mNativeObject, newParentHandle);
             return this;
         }
 
-        /** Re-parents a specific child layer to a new parent */
+        /** Re-parents a specific child layer to a new parent 
+         * @hide
+         */
         public Transaction reparent(SurfaceControl sc, IBinder newParentHandle) {
             sc.checkNotReleased();
             nativeReparent(mNativeObject, sc.mNativeObject,
@@ -1664,12 +2081,18 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction detachChildren(SurfaceControl sc) {
             sc.checkNotReleased();
             nativeSeverChildren(mNativeObject, sc.mNativeObject);
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setOverrideScalingMode(SurfaceControl sc, int overrideScalingMode) {
             sc.checkNotReleased();
             nativeSetOverrideScalingMode(mNativeObject, sc.mNativeObject,
@@ -1680,6 +2103,7 @@
         /**
          * Sets a color for the Surface.
          * @param color A float array with three values to represent r, g, b in range [0..1]
+         * @hide
          */
         @UnsupportedAppUsage
         public Transaction setColor(SurfaceControl sc, @Size(3) float[] color) {
@@ -1694,6 +2118,7 @@
          * arrives. As transform matrix and size are already frozen in this fashion,
          * this enables totally freezing the surface until the resize has completed
          * (at which point the geometry influencing aspects of this transaction will then occur)
+         * @hide
          */
         public Transaction setGeometryAppliesWithResize(SurfaceControl sc) {
             sc.checkNotReleased();
@@ -1704,6 +2129,7 @@
         /**
          * Sets the security of the surface.  Setting the flag is equivalent to creating the
          * Surface with the {@link #SECURE} flag.
+         * @hide
          */
         public Transaction setSecure(SurfaceControl sc, boolean isSecure) {
             sc.checkNotReleased();
@@ -1718,6 +2144,7 @@
         /**
          * Sets the opacity of the surface.  Setting the flag is equivalent to creating the
          * Surface with the {@link #OPAQUE} flag.
+         * @hide
          */
         public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
             sc.checkNotReleased();
@@ -1730,29 +2157,8 @@
         }
 
         /**
-         * Same as {@link #destroy()} except this is invoked in a transaction instead of
-         * immediately.
+         * @hide
          */
-        public Transaction destroy(SurfaceControl sc) {
-            sc.checkNotReleased();
-
-            /**
-             * Perhaps it's safer to transfer the close guard to the Transaction
-             * but then we have a whole wonky scenario regarding merging, multiple
-             * close-guards per transaction etc...the whole scenario is kind of wonky
-             * and it seems really we'd like to just be able to call release here
-             * but the WindowManager has some code that looks like
-             * --- destroyInTransaction(a)
-             * --- reparentChildrenInTransaction(a)
-             * so we need to ensure the SC remains valid until the transaction
-             * is applied.
-             */
-            sc.mCloseGuard.close();
-
-            nativeDestroy(mNativeObject, sc.mNativeObject);
-            return this;
-        }
-
         public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
             if (displayToken == null) {
                 throw new IllegalArgumentException("displayToken must not be null");
@@ -1768,6 +2174,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) {
             if (displayToken == null) {
                 throw new IllegalArgumentException("displayToken must not be null");
@@ -1776,6 +2185,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setDisplayProjection(IBinder displayToken,
                 int orientation, Rect layerStackRect, Rect displayRect) {
             if (displayToken == null) {
@@ -1793,6 +2205,9 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
         public Transaction setDisplaySize(IBinder displayToken, int width, int height) {
             if (displayToken == null) {
                 throw new IllegalArgumentException("displayToken must not be null");
@@ -1805,7 +2220,9 @@
             return this;
         }
 
-        /** flag the transaction as an animation */
+        /** flag the transaction as an animation 
+         * @hide
+         */
         public Transaction setAnimationTransaction() {
             nativeSetAnimationTransaction(mNativeObject);
             return this;
@@ -1818,6 +2235,7 @@
          * order not to miss frame deadlines.
          * <p>
          * Corresponds to setting ISurfaceComposer::eEarlyWakeup
+         * @hide
          */
         public Transaction setEarlyWakeup() {
             nativeSetEarlyWakeup(mNativeObject);
@@ -1827,6 +2245,7 @@
         /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
+         * @hide
          */
         public Transaction merge(Transaction other) {
             mResizedSurfaces.putAll(other.mResizedSurfaces);
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index a4fa12a..361ac93 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -30,7 +30,6 @@
     private long mNativeClient; // SurfaceComposerClient*
 
     private static native long nativeCreate();
-    private static native long nativeCreateScoped(long surfacePtr);
     private static native void nativeDestroy(long ptr);
     private static native void nativeKill(long ptr);
 
@@ -40,15 +39,6 @@
         mNativeClient = nativeCreate();
     }
 
-    public SurfaceSession(Surface root) {
-        synchronized (root.mLock) {
-            if (root.mNativeObject == 0) {
-                throw new IllegalStateException("Surface is not initialized or has been released");
-            }
-            mNativeClient = nativeCreateScoped(root.mNativeObject);
-        }
-    }
-
     /* no user serviceable parts here ... */
     @Override
     protected void finalize() throws Throwable {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a0af83d..61fb00d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -547,7 +547,7 @@
 
                 if (creating) {
                     viewRoot.createBoundsSurface(mSubLayer);
-                    mSurfaceSession = new SurfaceSession(viewRoot.mBoundsSurface);
+                    mSurfaceSession = new SurfaceSession();
                     mDeferredDestroySurfaceControl = mSurfaceControl;
 
                     updateOpaqueFlag();
@@ -559,6 +559,7 @@
                             new SurfaceControl.Builder(mSurfaceSession)
                                     .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                                     .setFormat(mFormat)
+                                    .setParent(viewRoot.getSurfaceControl())
                                     .setFlags(mSurfaceFlags));
                 } else if (mSurfaceControl == null) {
                     return;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6b07efc..32974ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -76,8 +76,8 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.Trace;
+import android.sysprop.DisplayProperties;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -96,6 +96,7 @@
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.WindowInsetsAnimationListener.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -811,14 +812,6 @@
     private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture";
 
     /**
-     * When set to true, apps will draw debugging information about their layouts.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout";
-
-    /**
      * When set to true, this view will save its attribute data.
      *
      * @hide
@@ -939,6 +932,26 @@
      */
     private static boolean sAcceptZeroSizeDragShadow;
 
+    /**
+     * Prior to Q, {@link #dispatchApplyWindowInsets} had some issues:
+     * <ul>
+     *     <li>The modified insets changed by {@link #onApplyWindowInsets} were passed to the
+     *     entire view hierarchy in prefix order, including siblings as well as siblings of parents
+     *     further down the hierarchy. This violates the basic concepts of the view hierarchy, and
+     *     thus, the hierarchical dispatching mechanism was hard to use for apps.</li>
+     *
+     *     <li>Dispatch was stopped after the insets were fully consumed. This is somewhat confusing
+     *     for developers, but more importantly, by adding more granular information to
+     *     {@link WindowInsets} it becomes really cumbersome to define what consumed actually means
+     *     </li>
+     * </ul>
+     *
+     * In order to make window inset dispatching work properly, we dispatch window insets
+     * in the view hierarchy in a proper hierarchical manner and don't stop dispatching if the
+     * insets are consumed if this flag is set to {@code false}.
+     */
+    static boolean sBrokenInsetsDispatch;
+
     /** @hide */
     @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
     @Retention(RetentionPolicy.SOURCE)
@@ -4527,6 +4540,8 @@
         OnCapturedPointerListener mOnCapturedPointerListener;
 
         private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
+
+        private WindowInsetsAnimationListener mWindowInsetsAnimationListener;
     }
 
     @UnsupportedAppUsage
@@ -5108,6 +5123,9 @@
 
             sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
 
+            sBrokenInsetsDispatch = !ViewRootImpl.USE_NEW_INSETS
+                    || targetSdkVersion < Build.VERSION_CODES.Q;
+
             sCompatibilityDone = true;
         }
     }
@@ -7660,10 +7678,13 @@
 
     /**
      * Convenience method for sending a {@link AccessibilityEvent#TYPE_ANNOUNCEMENT}
-     * {@link AccessibilityEvent} to make an announcement which is related to some
-     * sort of a context change for which none of the events representing UI transitions
-     * is a good fit. For example, announcing a new page in a book. If accessibility
-     * is not enabled this method does nothing.
+     * {@link AccessibilityEvent} to suggest that an accessibility service announce the
+     * specified text to its users.
+     * <p>
+     * Note: The event generated with this API carries no semantic meaning, and is appropriate only
+     * in exceptional situations. Apps can generally achieve correct behavior for accessibility by
+     * accurately supplying the semantics of their UI.
+     * They should not need to specify what exactly is announced to users.
      *
      * @param text The announcement text.
      */
@@ -8173,6 +8194,19 @@
      * <p>The populated structure is then passed to the service through
      * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
      *
+     * <p><b>Note: </b>views that manage a virtual structure under this view must populate just
+     * the node representing this view and return right away, then asynchronously report (not
+     * necessarily in the UI thread) when the children nodes appear, disappear or have their text
+     * changed by calling
+     * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)},
+     * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
+     * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence, int)}
+     * respectively. The structure for the a child must be created using
+     * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, int)}, and the
+     * {@code autofillId} for a child can be obtained either through
+     * {@code childStructure.getAutofillId()} or
+     * {@link ContentCaptureSession#newAutofillId(AutofillId, int)}.
+     *
      * <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
      * <ul>
      *   <li>{@link ViewStructure#setChildCount(int)}
@@ -8209,10 +8243,6 @@
         } else {
             structure.setId(id, null, null, null);
         }
-        if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
-            //TODO(b/111276913): STOPSHIP - don't set it if not needed
-            structure.setDataIsSensitive(false);
-        }
 
         if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
                 || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
@@ -10453,6 +10483,37 @@
     }
 
     /**
+     * Sets a {@link WindowInsetsAnimationListener} to be notified about animations of windows that
+     * cause insets.
+     *
+     * @param listener The listener to set.
+     * @hide pending unhide
+     */
+    public void setWindowInsetsAnimationListener(WindowInsetsAnimationListener listener) {
+        getListenerInfo().mWindowInsetsAnimationListener = listener;
+    }
+
+    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
+            mListenerInfo.mWindowInsetsAnimationListener.onStarted(animation);
+        }
+    }
+
+    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
+            return mListenerInfo.mWindowInsetsAnimationListener.onProgress(insets);
+        } else {
+            return insets;
+        }
+    }
+
+    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
+        if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {
+            mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
+        }
+    }
+
+    /**
      * Compute the view's coordinate within the surface.
      *
      * <p>Computes the coordinates of this view in its surface. The argument
@@ -25053,9 +25114,10 @@
         }
 
         final ViewRootImpl root = mAttachInfo.mViewRootImpl;
-        final SurfaceSession session = new SurfaceSession(root.mSurface);
+        final SurfaceSession session = new SurfaceSession();
         final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                 .setName("drag surface")
+                .setParent(root.getSurfaceControl())
                 .setBufferSize(shadowSize.x, shadowSize.y)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .build();
@@ -27763,7 +27825,7 @@
         /**
          * Show where the margins, bounds and layout bounds are for each view.
          */
-        boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);
+        boolean mDebugLayout = DisplayProperties.debug_layout().orElse(false);
 
         /**
          * Point used to compute visible regions.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8372032..0986cfa 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -51,6 +51,7 @@
 import android.util.Pools.SynchronizedPool;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.view.WindowInsetsAnimationListener.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -7111,6 +7112,14 @@
     @Override
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
         insets = super.dispatchApplyWindowInsets(insets);
+        if (View.sBrokenInsetsDispatch) {
+            return brokenDispatchApplyWindowInsets(insets);
+        } else {
+            return newDispatchApplyWindowInsets(insets);
+        }
+    }
+
+    private WindowInsets brokenDispatchApplyWindowInsets(WindowInsets insets) {
         if (!insets.isConsumed()) {
             final int count = getChildCount();
             for (int i = 0; i < count; i++) {
@@ -7123,6 +7132,42 @@
         return insets;
     }
 
+    private WindowInsets newDispatchApplyWindowInsets(WindowInsets insets) {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).dispatchApplyWindowInsets(insets);
+        }
+        return insets;
+    }
+
+    @Override
+    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
+        super.dispatchWindowInsetsAnimationStarted(animation);
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).dispatchWindowInsetsAnimationStarted(animation);
+        }
+    }
+
+    @Override
+    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
+        insets = super.dispatchWindowInsetsAnimationProgress(insets);
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).dispatchWindowInsetsAnimationProgress(insets);
+        }
+        return insets;
+    }
+
+    @Override
+    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
+        super.dispatchWindowInsetsAnimationFinished(animation);
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).dispatchWindowInsetsAnimationFinished(animation);
+        }
+    }
+
     /**
      * Returns the animation listener to which layout animation events are
      * sent.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3f7a512..27d4ea4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -75,6 +75,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.sysprop.DisplayProperties;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -432,6 +433,7 @@
     // Surface can never be reassigned or cleared (use Surface.clear()).
     @UnsupportedAppUsage
     public final Surface mSurface = new Surface();
+    private final SurfaceControl mSurfaceControl = new SurfaceControl();
 
     /**
      * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds
@@ -1526,7 +1528,7 @@
      */
     public void createBoundsSurface(int zOrderLayer) {
         if (mSurfaceSession == null) {
-            mSurfaceSession = new SurfaceSession(mSurface);
+            mSurfaceSession = new SurfaceSession();
         }
         if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) {
             return; // surface control for bounds surface already exists.
@@ -1534,6 +1536,7 @@
 
         mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                 .setName("Bounds for - " + getTitle().toString())
+                .setParent(mSurfaceControl)
                 .build();
 
         setBoundsSurfaceCrop();
@@ -1567,6 +1570,8 @@
 
     private void destroySurface() {
         mSurface.release();
+        mSurfaceControl.release();
+
         mSurfaceSession = null;
 
         if (mBoundsSurfaceControl != null) {
@@ -1855,8 +1860,7 @@
                         mContext.getResources().getConfiguration().isScreenRound(),
                         mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
             } else {
-                mLastWindowInsets = new WindowInsets(contentInsets,
-                        null /* windowDecorInsets */, stableInsets,
+                mLastWindowInsets = new WindowInsets(contentInsets, stableInsets,
                         mContext.getResources().getConfiguration().isScreenRound(),
                         mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
             }
@@ -6801,7 +6805,12 @@
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
-                mPendingMergedConfiguration, mSurface, mTempInsets);
+                mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
+        if (mSurfaceControl.isValid()) {
+            mSurface.copyFrom(mSurfaceControl);
+        } else {
+            destroySurface();
+        }
 
         mPendingAlwaysConsumeNavBar =
                 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -7063,7 +7072,7 @@
                 }
 
                 // Layout debugging
-                boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
+                boolean layout = DisplayProperties.debug_layout().orElse(false);
                 if (layout != mAttachInfo.mDebugLayout) {
                     mAttachInfo.mDebugLayout = layout;
                     if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
@@ -8483,6 +8492,10 @@
         mActivityRelaunched = true;
     }
 
+    public SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
     /**
      * Class for managing the accessibility interaction connection
      * based on the global accessibility state.
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 572d331..b3da727 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,18 +17,33 @@
 
 package android.view;
 
-import android.annotation.NonNull;
+import static android.view.WindowInsets.Type.FIRST;
+import static android.view.WindowInsets.Type.IME;
+import static android.view.WindowInsets.Type.LAST;
+import static android.view.WindowInsets.Type.SIDE_BARS;
+import static android.view.WindowInsets.Type.SIZE;
+import static android.view.WindowInsets.Type.TOP_BAR;
+import static android.view.WindowInsets.Type.all;
+import static android.view.WindowInsets.Type.compatSystemInsets;
+import static android.view.WindowInsets.Type.indexOf;
+
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsState.InternalInsetType;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethod;
 
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -49,9 +64,9 @@
  */
 public final class WindowInsets {
 
-    @NonNull private final Insets mSystemWindowInsets;
-    @NonNull private final Insets mWindowDecorInsets;
-    @NonNull private final Insets mStableInsets;
+    private final Insets[] mTypeInsetsMap;
+    private final Insets[] mTypeMaxInsetsMap;
+
     @Nullable private Rect mTempRect;
     private final boolean mIsRound;
     @Nullable private final DisplayCutout mDisplayCutout;
@@ -64,7 +79,6 @@
     private final boolean mAlwaysConsumeNavBar;
 
     private final boolean mSystemWindowInsetsConsumed;
-    private final boolean mWindowDecorInsetsConsumed;
     private final boolean mStableInsetsConsumed;
     private final boolean mDisplayCutoutConsumed;
 
@@ -78,7 +92,7 @@
     public static final WindowInsets CONSUMED;
 
     static {
-        CONSUMED = new WindowInsets((Insets) null, null, null, false, false, null);
+        CONSUMED = new WindowInsets((Rect) null, null, false, false, null);
     }
 
     /**
@@ -87,24 +101,38 @@
      * A {@code null} inset indicates that the respective inset is consumed.
      *
      * @hide
+     * @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
      */
-    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
+    public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
             boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
-        this(insetsOrNull(systemWindowInsets), insetsOrNull(windowDecorInsets),
-                insetsOrNull(stableInsets), isRound, alwaysConsumeNavBar, displayCutout);
+        this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
+                isRound, alwaysConsumeNavBar, displayCutout);
     }
 
-    private WindowInsets(Insets systemWindowInsets, Insets windowDecorInsets,
-            Insets stableInsets, boolean isRound, boolean alwaysConsumeNavBar,
-            DisplayCutout displayCutout) {
-        mSystemWindowInsetsConsumed = systemWindowInsets == null;
-        mSystemWindowInsets = mSystemWindowInsetsConsumed ? Insets.NONE : systemWindowInsets;
+    /**
+     * Construct a new WindowInsets from individual insets.
+     *
+     * {@code typeInsetsMap} and {@code typeMaxInsetsMap} are a map of indexOf(type) -> insets that
+     * contain the information what kind of system bars causes how much insets. The insets in this
+     * map are non-additive; i.e. they have the same origin. In other words: If two system bars
+     * overlap on one side, the insets of the larger bar will also include the insets of the smaller
+     * bar.
+     *
+     * {@code null} type inset map indicates that the respective inset is fully consumed.
+     * @hide
+     */
+    public WindowInsets(@Nullable Insets[] typeInsetsMap,
+            @Nullable Insets[] typeMaxInsetsMap, boolean isRound,
+            boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
+        mSystemWindowInsetsConsumed = typeInsetsMap == null;
+        mTypeInsetsMap = mSystemWindowInsetsConsumed
+                ? new Insets[SIZE]
+                : typeInsetsMap.clone();
 
-        mWindowDecorInsetsConsumed = windowDecorInsets == null;
-        mWindowDecorInsets = mWindowDecorInsetsConsumed ? Insets.NONE : windowDecorInsets;
-
-        mStableInsetsConsumed = stableInsets == null;
-        mStableInsets = mStableInsetsConsumed ? Insets.NONE : stableInsets;
+        mStableInsetsConsumed = typeMaxInsetsMap == null;
+        mTypeMaxInsetsMap = mStableInsetsConsumed
+                ? new Insets[SIZE]
+                : typeMaxInsetsMap.clone();
 
         mIsRound = isRound;
         mAlwaysConsumeNavBar = alwaysConsumeNavBar;
@@ -120,10 +148,7 @@
      * @param src Source to copy insets from
      */
     public WindowInsets(WindowInsets src) {
-        this(src.mSystemWindowInsetsConsumed ? null : src.mSystemWindowInsets,
-                src.mWindowDecorInsetsConsumed ? null : src.mWindowDecorInsets,
-                src.mStableInsetsConsumed ? null : src.mStableInsets,
-                src.mIsRound, src.mAlwaysConsumeNavBar,
+        this(src.mTypeInsetsMap, src.mTypeMaxInsetsMap, src.mIsRound, src.mAlwaysConsumeNavBar,
                 displayCutoutCopyConstructorArgument(src));
     }
 
@@ -137,10 +162,64 @@
         }
     }
 
+    /**
+     * @return The insets that include system bars indicated by {@code typeMask}, taken from
+     *         {@code typeInsetMap}.
+     */
+    private static Insets getInsets(Insets[] typeInsetsMap, @InsetType int typeMask) {
+        Insets result = null;
+        for (int i = FIRST; i <= LAST; i = i << 1) {
+            if ((typeMask & i) == 0) {
+                continue;
+            }
+            Insets insets = typeInsetsMap[indexOf(i)];
+            if (insets == null) {
+                continue;
+            }
+            if (result == null) {
+                result = insets;
+            } else {
+                result = Insets.max(result, insets);
+            }
+        }
+        return result == null ? Insets.NONE : result;
+    }
+
+    /**
+     * Sets all entries in {@code typeInsetsMap} that belong to {@code typeMask} to {@code insets},
+     */
+    private static void setInsets(Insets[] typeInsetsMap, @InsetType int typeMask, Insets insets) {
+        for (int i = FIRST; i <= LAST; i = i << 1) {
+            if ((typeMask & i) == 0) {
+                continue;
+            }
+            typeInsetsMap[indexOf(i)] = insets;
+        }
+    }
+
     /** @hide */
     @UnsupportedAppUsage
     public WindowInsets(Rect systemWindowInsets) {
-        this(systemWindowInsets, null, null, false, false, null);
+        this(createCompatTypeMap(systemWindowInsets), null, false, false, null);
+    }
+
+    /**
+     * Creates a indexOf(type) -> inset map for which the {@code insets} is just mapped to
+     * {@link InsetType#topBar()} and {@link InsetType#sideBars()}, depending on the location of the
+     * inset.
+     */
+    private static Insets[] createCompatTypeMap(@Nullable Rect insets) {
+        if (insets == null) {
+            return null;
+        }
+        Insets[] typeInsetMap = new Insets[SIZE];
+        assignCompatInsets(typeInsetMap, insets);
+        return typeInsetMap;
+    }
+
+    private static void assignCompatInsets(Insets[] typeInsetMap, Rect insets) {
+        typeInsetMap[indexOf(TOP_BAR)] = Insets.of(0, insets.top, 0, 0);
+        typeInsetMap[indexOf(SIDE_BARS)] = Insets.of(insets.left, 0, insets.right, insets.bottom);
     }
 
     /**
@@ -156,8 +235,8 @@
         if (mTempRect == null) {
             mTempRect = new Rect();
         }
-        mTempRect.set(mSystemWindowInsets.left, mSystemWindowInsets.top,
-                mSystemWindowInsets.right, mSystemWindowInsets.bottom);
+        Insets insets = getSystemWindowInsets();
+        mTempRect.set(insets.left, insets.top, insets.right, insets.bottom);
         return mTempRect;
     }
 
@@ -172,7 +251,46 @@
      */
     @NonNull
     public Insets getSystemWindowInsets() {
-        return mSystemWindowInsets;
+        return getInsets(mTypeInsetsMap, compatSystemInsets());
+    }
+
+    /**
+     * Returns the insets of a specific set of windows causing insets, denoted by the
+     * {@code typeMask} bit mask of {@link InsetType}s.
+     *
+     * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
+     * @return The insets.
+     *
+     * @hide pending unhide
+     */
+    public Insets getInsets(@InsetType int typeMask) {
+        return getInsets(mTypeInsetsMap, typeMask);
+    }
+
+    /**
+     * Returns the maximum amount of insets a specific set of windows can cause, denoted by the
+     * {@code typeMask} bit mask of {@link InsetType}s.
+     *
+     * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
+     * or fully obscured by the system window identified by {@code type}. This value does not
+     * change based on the visibility state of those elements. for example, if the status bar is
+     * normally shown, but temporarily hidden, the maximum inset will still provide the inset
+     * associated with the status bar being shown.</p>
+     *
+     * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
+     * @return The insets.
+     *
+     * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Maximum
+     *                                  insets are not available for this type as the height of the
+     *                                  IME is dynamic depending on the {@link EditorInfo} of the
+     *                                  currently focused view, as well as the UI state of the IME.
+     * @hide pending unhide
+     */
+    public Insets getMaxInsets(@InsetType int typeMask) throws IllegalArgumentException {
+        if ((typeMask & IME) != 0) {
+            throw new IllegalArgumentException("Unable to query the maximum insets for IME");
+        }
+        return getInsets(mTypeMaxInsetsMap, typeMask);
     }
 
     /**
@@ -185,7 +303,7 @@
      * @return The left system window inset
      */
     public int getSystemWindowInsetLeft() {
-        return mSystemWindowInsets.left;
+        return getSystemWindowInsets().left;
     }
 
     /**
@@ -198,7 +316,7 @@
      * @return The top system window inset
      */
     public int getSystemWindowInsetTop() {
-        return mSystemWindowInsets.top;
+        return getSystemWindowInsets().top;
     }
 
     /**
@@ -211,7 +329,7 @@
      * @return The right system window inset
      */
     public int getSystemWindowInsetRight() {
-        return mSystemWindowInsets.right;
+        return getSystemWindowInsets().right;
     }
 
     /**
@@ -224,63 +342,7 @@
      * @return The bottom system window inset
      */
     public int getSystemWindowInsetBottom() {
-        return mSystemWindowInsets.bottom;
-    }
-
-    /**
-     * Returns the left window decor inset in pixels.
-     *
-     * <p>The window decor inset represents the area of the window content area that is
-     * partially or fully obscured by decorations within the window provided by the framework.
-     * This can include action bars, title bars, toolbars, etc.</p>
-     *
-     * @return The left window decor inset
-     * @hide pending API
-     */
-    public int getWindowDecorInsetLeft() {
-        return mWindowDecorInsets.left;
-    }
-
-    /**
-     * Returns the top window decor inset in pixels.
-     *
-     * <p>The window decor inset represents the area of the window content area that is
-     * partially or fully obscured by decorations within the window provided by the framework.
-     * This can include action bars, title bars, toolbars, etc.</p>
-     *
-     * @return The top window decor inset
-     * @hide pending API
-     */
-    public int getWindowDecorInsetTop() {
-        return mWindowDecorInsets.top;
-    }
-
-    /**
-     * Returns the right window decor inset in pixels.
-     *
-     * <p>The window decor inset represents the area of the window content area that is
-     * partially or fully obscured by decorations within the window provided by the framework.
-     * This can include action bars, title bars, toolbars, etc.</p>
-     *
-     * @return The right window decor inset
-     * @hide pending API
-     */
-    public int getWindowDecorInsetRight() {
-        return mWindowDecorInsets.right;
-    }
-
-    /**
-     * Returns the bottom window decor inset in pixels.
-     *
-     * <p>The window decor inset represents the area of the window content area that is
-     * partially or fully obscured by decorations within the window provided by the framework.
-     * This can include action bars, title bars, toolbars, etc.</p>
-     *
-     * @return The bottom window decor inset
-     * @hide pending API
-     */
-    public int getWindowDecorInsetBottom() {
-        return mWindowDecorInsets.bottom;
+        return getSystemWindowInsets().bottom;
     }
 
     /**
@@ -293,23 +355,7 @@
      * @return true if any of the system window inset values are nonzero
      */
     public boolean hasSystemWindowInsets() {
-        return mSystemWindowInsets.left != 0 || mSystemWindowInsets.top != 0 ||
-                mSystemWindowInsets.right != 0 || mSystemWindowInsets.bottom != 0;
-    }
-
-    /**
-     * Returns true if this WindowInsets has nonzero window decor insets.
-     *
-     * <p>The window decor inset represents the area of the window content area that is
-     * partially or fully obscured by decorations within the window provided by the framework.
-     * This can include action bars, title bars, toolbars, etc.</p>
-     *
-     * @return true if any of the window decor inset values are nonzero
-     * @hide pending API
-     */
-    public boolean hasWindowDecorInsets() {
-        return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 ||
-                mWindowDecorInsets.right != 0 || mWindowDecorInsets.bottom != 0;
+        return !getSystemWindowInsets().equals(Insets.NONE);
     }
 
     /**
@@ -318,7 +364,8 @@
      * @return true if any inset values are nonzero
      */
     public boolean hasInsets() {
-        return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets()
+        return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
+                || !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
                 || mDisplayCutout != null;
     }
 
@@ -340,9 +387,7 @@
      */
     @NonNull
     public WindowInsets consumeDisplayCutout() {
-        return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
-                mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
-                mStableInsetsConsumed ? null : mStableInsets,
+        return new WindowInsets(mTypeInsetsMap, mTypeMaxInsetsMap,
                 mIsRound, mAlwaysConsumeNavBar,
                 null /* displayCutout */);
     }
@@ -362,7 +407,7 @@
      * @return true if the insets have been fully consumed.
      */
     public boolean isConsumed() {
-        return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed
+        return mSystemWindowInsetsConsumed && mStableInsetsConsumed
                 && mDisplayCutoutConsumed;
     }
 
@@ -387,9 +432,7 @@
      */
     @NonNull
     public WindowInsets consumeSystemWindowInsets() {
-        return new WindowInsets(null /* systemWindowInsets */,
-                mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
-                mStableInsetsConsumed ? null : mStableInsets,
+        return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
                 mIsRound, mAlwaysConsumeNavBar,
                 displayCutoutCopyConstructorArgument(this));
     }
@@ -449,18 +492,6 @@
     }
 
     /**
-     * @hide
-     */
-    @NonNull
-    public WindowInsets consumeWindowDecorInsets() {
-        return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
-                null /* windowDecorInsets */,
-                mStableInsetsConsumed ? null : mStableInsets,
-                mIsRound, mAlwaysConsumeNavBar,
-                displayCutoutCopyConstructorArgument(this));
-    }
-
-    /**
      * Returns the stable insets in pixels.
      *
      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
@@ -473,7 +504,7 @@
      */
     @NonNull
     public Insets getStableInsets() {
-        return mStableInsets;
+        return getInsets(mTypeMaxInsetsMap, compatSystemInsets());
     }
 
     /**
@@ -488,7 +519,7 @@
      * @return The top stable inset
      */
     public int getStableInsetTop() {
-        return mStableInsets.top;
+        return getStableInsets().top;
     }
 
     /**
@@ -503,7 +534,7 @@
      * @return The left stable inset
      */
     public int getStableInsetLeft() {
-        return mStableInsets.left;
+        return getStableInsets().left;
     }
 
     /**
@@ -518,7 +549,7 @@
      * @return The right stable inset
      */
     public int getStableInsetRight() {
-        return mStableInsets.right;
+        return getStableInsets().right;
     }
 
     /**
@@ -533,7 +564,7 @@
      * @return The bottom stable inset
      */
     public int getStableInsetBottom() {
-        return mStableInsets.bottom;
+        return getStableInsets().bottom;
     }
 
     /**
@@ -548,8 +579,7 @@
      * @return true if any of the stable inset values are nonzero
      */
     public boolean hasStableInsets() {
-        return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
-                || mStableInsets.bottom != 0;
+        return !getStableInsets().equals(Insets.NONE);
     }
 
     /**
@@ -559,9 +589,7 @@
      */
     @NonNull
     public WindowInsets consumeStableInsets() {
-        return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
-                mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
-                null /* stableInsets */,
+        return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null,
                 mIsRound, mAlwaysConsumeNavBar,
                 displayCutoutCopyConstructorArgument(this));
     }
@@ -575,9 +603,8 @@
 
     @Override
     public String toString() {
-        return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
-                + " windowDecorInsets=" + mWindowDecorInsets
-                + " stableInsets=" + mStableInsets
+        return "WindowInsets{systemWindowInsets=" + getSystemWindowInsets()
+                + " stableInsets=" + getStableInsets()
                 + (mDisplayCutout != null ? " cutout=" + mDisplayCutout : "")
                 + (isRound() ? " round" : "")
                 + "}";
@@ -634,16 +661,16 @@
         Preconditions.checkArgumentNonnegative(bottom);
 
         return new WindowInsets(
-                mSystemWindowInsetsConsumed ? null :
-                        insetInsets(mSystemWindowInsets, left, top, right, bottom),
-                mWindowDecorInsetsConsumed ? null :
-                        insetInsets(mWindowDecorInsets, left, top, right, bottom),
-                mStableInsetsConsumed ? null :
-                        insetInsets(mStableInsets, left, top, right, bottom),
+                mSystemWindowInsetsConsumed
+                        ? null
+                        : insetInsets(mTypeInsetsMap, left, top, right, bottom),
+                mStableInsetsConsumed
+                        ? null
+                        : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
                 mIsRound, mAlwaysConsumeNavBar,
                 mDisplayCutoutConsumed
-                        ? null :
-                        mDisplayCutout == null
+                        ? null
+                        : mDisplayCutout == null
                                 ? DisplayCutout.NO_CUTOUT
                                 : mDisplayCutout.inset(left, top, right, bottom));
     }
@@ -653,23 +680,49 @@
         if (this == o) return true;
         if (o == null || !(o instanceof WindowInsets)) return false;
         WindowInsets that = (WindowInsets) o;
+
         return mIsRound == that.mIsRound
                 && mAlwaysConsumeNavBar == that.mAlwaysConsumeNavBar
                 && mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
-                && mWindowDecorInsetsConsumed == that.mWindowDecorInsetsConsumed
                 && mStableInsetsConsumed == that.mStableInsetsConsumed
                 && mDisplayCutoutConsumed == that.mDisplayCutoutConsumed
-                && Objects.equals(mSystemWindowInsets, that.mSystemWindowInsets)
-                && Objects.equals(mWindowDecorInsets, that.mWindowDecorInsets)
-                && Objects.equals(mStableInsets, that.mStableInsets)
+                && Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
+                && Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
                 && Objects.equals(mDisplayCutout, that.mDisplayCutout);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSystemWindowInsets, mWindowDecorInsets, mStableInsets, mIsRound,
-                mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
-                mWindowDecorInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed);
+        return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
+                mIsRound, mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
+                mStableInsetsConsumed, mDisplayCutoutConsumed);
+    }
+
+
+    /**
+     * Insets every inset in {@code typeInsetsMap} by the specified left, top, right, bottom.
+     *
+     * @return {@code typeInsetsMap} if no inset was modified; a copy of the map with the modified
+     *          insets otherwise.
+     */
+    private static Insets[] insetInsets(
+            Insets[] typeInsetsMap, int left, int top, int right, int bottom) {
+        boolean cloned = false;
+        for (int i = 0; i < SIZE; i++) {
+            Insets insets = typeInsetsMap[i];
+            if (insets == null) {
+                continue;
+            }
+            Insets insetInsets = insetInsets(insets, left, top, right, bottom);
+            if (insetInsets != insets) {
+                if (!cloned) {
+                    typeInsetsMap = typeInsetsMap.clone();
+                    cloned = true;
+                }
+                typeInsetsMap[i] = insetInsets;
+            }
+        }
+        return typeInsetsMap;
     }
 
     private static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
@@ -683,10 +736,6 @@
         return Insets.of(newLeft, newTop, newRight, newBottom);
     }
 
-    private static Insets insetsOrNull(Rect insets) {
-        return insets != null ? Insets.of(insets) : null;
-    }
-
     /**
      * @return whether system window insets have been consumed.
      */
@@ -699,11 +748,13 @@
      */
     public static class Builder {
 
-        private Insets mSystemWindowInsets;
-        private Insets mStableInsets;
+        private final Insets[] mTypeInsetsMap;
+        private final Insets[] mTypeMaxInsetsMap;
+        private boolean mSystemInsetsConsumed = true;
+        private boolean mStableInsetsConsumed = true;
+
         private DisplayCutout mDisplayCutout;
 
-        private Insets mWindowDecorInsets;
         private boolean mIsRound;
         private boolean mAlwaysConsumeNavBar;
 
@@ -711,6 +762,8 @@
          * Creates a builder where all insets are initially consumed.
          */
         public Builder() {
+            mTypeInsetsMap = new Insets[SIZE];
+            mTypeMaxInsetsMap = new Insets[SIZE];
         }
 
         /**
@@ -719,12 +772,11 @@
          * @param insets the instance to initialize from.
          */
         public Builder(WindowInsets insets) {
-            mSystemWindowInsets = insets.mSystemWindowInsetsConsumed ? null
-                    : insets.mSystemWindowInsets;
-            mStableInsets = insets.mStableInsetsConsumed ? null : insets.mStableInsets;
+            mTypeInsetsMap = insets.mTypeInsetsMap.clone();
+            mTypeMaxInsetsMap = insets.mTypeMaxInsetsMap.clone();
+            mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
+            mStableInsetsConsumed = insets.mStableInsetsConsumed;
             mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
-            mWindowDecorInsets =  insets.mWindowDecorInsetsConsumed ? null
-                    : insets.mWindowDecorInsets;
             mIsRound = insets.mIsRound;
             mAlwaysConsumeNavBar = insets.mAlwaysConsumeNavBar;
         }
@@ -742,7 +794,66 @@
         @NonNull
         public Builder setSystemWindowInsets(@NonNull Insets systemWindowInsets) {
             Preconditions.checkNotNull(systemWindowInsets);
-            mSystemWindowInsets = systemWindowInsets;
+            assignCompatInsets(mTypeInsetsMap, systemWindowInsets.toRect());
+            mSystemInsetsConsumed = false;
+            return this;
+        }
+
+        /**
+         * Sets the insets of a specific window type in pixels.
+         *
+         * <p>The insets represents the area of a a window that is partially or fully obscured by
+         * the system windows identified by {@code typeMask}.
+         * </p>
+         *
+         * @see #getInsets(int)
+         *
+         * @param typeMask The bitmask of {@link InsetType} to set the insets for.
+         * @param insets The insets to set.
+         *
+         * @return itself
+         * @hide pending unhide
+         */
+        @NonNull
+        public Builder setInsets(@InsetType int typeMask, @NonNull Insets insets) {
+            Preconditions.checkNotNull(insets);
+            WindowInsets.setInsets(mTypeInsetsMap, typeMask, insets);
+            mSystemInsetsConsumed = false;
+            return this;
+        }
+
+        /**
+         * Sets the maximum amount of insets a specific window type in pixels.
+         *
+         * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
+         * or fully obscured by the system windows identified by {@code typeMask}. This value does
+         * not change based on the visibility state of those elements. for example, if the status
+         * bar is normally shown, but temporarily hidden, the maximum inset will still provide the
+         * inset associated with the status bar being shown.</p>
+         *
+         * @see #getMaxInsets(int)
+         *
+         * @param typeMask The bitmask of {@link InsetType} to set the insets for.
+         * @param insets The insets to set.
+         *
+         * @return itself
+         *
+         * @throws IllegalArgumentException If {@code typeMask} contains {@link Type#ime()}. Maximum
+         *                                  insets are not available for this type as the height of
+         *                                  the IME is dynamic depending on the {@link EditorInfo}
+         *                                  of the currently focused view, as well as the UI
+         *                                  state of the IME.
+         * @hide pending unhide
+         */
+        @NonNull
+        public Builder setMaxInsets(@InsetType int typeMask, @NonNull Insets insets)
+                throws IllegalArgumentException{
+            if (typeMask == IME) {
+                throw new IllegalArgumentException("Maximum inset not available for IME");
+            }
+            Preconditions.checkNotNull(insets);
+            WindowInsets.setInsets(mTypeMaxInsetsMap, typeMask, insets);
+            mStableInsetsConsumed = false;
             return this;
         }
 
@@ -761,7 +872,8 @@
         @NonNull
         public Builder setStableInsets(@NonNull Insets stableInsets) {
             Preconditions.checkNotNull(stableInsets);
-            mStableInsets = stableInsets;
+            assignCompatInsets(mTypeInsetsMap, stableInsets.toRect());
+            mStableInsetsConsumed = false;
             return this;
         }
 
@@ -780,14 +892,6 @@
 
         /** @hide */
         @NonNull
-        public Builder setWindowDecorInsets(@NonNull Insets windowDecorInsets) {
-            Preconditions.checkNotNull(windowDecorInsets);
-            mWindowDecorInsets = windowDecorInsets;
-            return this;
-        }
-
-        /** @hide */
-        @NonNull
         public Builder setRound(boolean round) {
             mIsRound = round;
             return this;
@@ -807,8 +911,9 @@
          */
         @NonNull
         public WindowInsets build() {
-            return new WindowInsets(mSystemWindowInsets, mWindowDecorInsets, mStableInsets,
-                    mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
+            return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
+                    mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mIsRound,
+                    mAlwaysConsumeNavBar, mDisplayCutout);
         }
     }
 
@@ -818,10 +923,31 @@
      */
     public static final class Type {
 
-        static final int TOP_BAR = 0x1;
+        static final int FIRST = 0x1;
+        static final int TOP_BAR = FIRST;
+
         static final int IME = 0x2;
         static final int SIDE_BARS = 0x4;
-        static final int WINDOW_DECOR = 0x8;
+
+        static final int LAST = 0x8;
+        static final int SIZE = 4;
+        static final int WINDOW_DECOR = LAST;
+
+        static int indexOf(@InsetType int type) {
+            switch (type) {
+                case TOP_BAR:
+                    return 0;
+                case IME:
+                    return 1;
+                case SIDE_BARS:
+                    return 2;
+                case WINDOW_DECOR:
+                    return 3;
+                default:
+                    throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
+                            + " type=" + type);
+            }
+        }
 
         private Type() {
         }
@@ -870,6 +996,15 @@
         }
 
         /**
+         * @return Inset types representing the list of bars that traditionally were denoted as
+         *         system insets.
+         * @hide
+         */
+        static @InsetType int compatSystemInsets() {
+            return TOP_BAR | SIDE_BARS | IME;
+        }
+
+        /**
          * @return All inset types combined.
          */
         public static @InsetType int all() {
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 9de517d..cf4415d 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.graphics.Insets;
 import android.view.WindowInsets.Type.InsetType;
+import android.view.WindowInsetsAnimationListener.InsetsAnimation;
 
 /**
  * Interface to control a window inset animation frame-by-frame.
@@ -28,8 +29,13 @@
 
     /**
      * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
+     * <p>
+     * If there are any animation listeners registered, this value is the same as
+     * {@link InsetsAnimation#getLowerBound()} that will be passed into the callbacks.
      *
      * @return Insets when the windows this animation is controlling are fully hidden.
+     *
+     * @see InsetsAnimation#getLowerBound()
      */
     @NonNull Insets getHiddenStateInsets();
 
@@ -38,8 +44,13 @@
      * <p>
      * In case the size of a window causing insets is changing in the middle of the animation, we
      * execute that height change after this animation has finished.
+     * <p>
+     * If there are any animation listeners registered, this value is the same as
+     * {@link InsetsAnimation#getUpperBound()} that will be passed into the callbacks.
      *
      * @return Insets when the windows this animation is controlling are fully shown.
+     *
+     * @see InsetsAnimation#getUpperBound()
      */
     @NonNull Insets getShownStateInsets();
 
@@ -59,8 +70,11 @@
      * <p>
      * Note that this will <b>not</b> inform the view system of a full inset change via
      * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
-     * animation. If you'd like to animate views during a window inset animation, use
-     * TODO add link to animation listeners.
+     * animation. If you'd like to animate views during a window inset animation, register a
+     * {@link WindowInsetsAnimationListener} by calling
+     * {@link View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)} that will be
+     * notified about any insets change via {@link WindowInsetsAnimationListener#onProgress} during
+     * the animation.
      * <p>
      * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
      * finished, i.e. once {@link #finish} has been called.
@@ -70,6 +84,9 @@
      *               the resulting insets of that configuration will match the passed in parameter.
      *               Note that these insets are being clamped to the range from
      *               {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
+     *
+     * @see WindowInsetsAnimationListener
+     * @see View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)
      */
     void changeInsets(@NonNull Insets insets);
 
diff --git a/core/java/android/view/WindowInsetsAnimationListener.java b/core/java/android/view/WindowInsetsAnimationListener.java
new file mode 100644
index 0000000..682ab5b
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationListener.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Insets;
+
+/**
+ * Interface that allows the application to listen to animation events for windows that cause
+ * insets.
+ * @hide pending unhide
+ */
+public interface WindowInsetsAnimationListener {
+
+    /**
+     * Called when an inset animation gets started.
+     *
+     * @param animation The animation that is about to start.
+     */
+    void onStarted(InsetsAnimation animation);
+
+    /**
+     * Called when the insets change as part of running an animation. Note that even if multiple
+     * animations for different types are running, there will only be one progress callback per
+     * frame. The {@code insets} passed as an argument represents the overall state and will include
+     * all types, regardless of whether they are animating or not.
+     * <p>
+     * Note that insets dispatch is hierarchical: It will start at the root of the view hierarchy,
+     * and then traverse it and invoke the callback of the specific {@link View} being traversed.
+     * The callback may return a modified instance by calling {@link WindowInsets#inset(int, int, int, int)}
+     * to indicate that a part of the insets have been used to offset or clip its children, and the
+     * children shouldn't worry about that part anymore.
+     *
+     * @param insets The current insets.
+     * @return The insets to dispatch to the subtree of the hierarchy.
+     */
+    WindowInsets onProgress(WindowInsets insets);
+
+    /**
+     * Called when an inset animation has finished.
+     *
+     * @param animation The animation that has finished running.
+     */
+    void onFinished(InsetsAnimation animation);
+
+    /**
+     * Class representing an animation of a set of windows that cause insets.
+     */
+    class InsetsAnimation {
+
+        private final @WindowInsets.Type.InsetType int mTypeMask;
+        private final Insets mLowerBound;
+        private final Insets mUpperBound;
+
+        /**
+         * @hide
+         */
+        InsetsAnimation(int typeMask, Insets lowerBound, Insets upperBound) {
+            mTypeMask = typeMask;
+            mLowerBound = lowerBound;
+            mUpperBound = upperBound;
+        }
+
+        /**
+         * @return The bitmask of {@link WindowInsets.Type.InsetType}s that are animating.
+         */
+        public @WindowInsets.Type.InsetType int getTypeMask() {
+            return mTypeMask;
+        }
+
+        /**
+         * Queries the lower inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
+         * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This
+         * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
+         * invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationController}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * <p>
+         * There are no overlapping animations for a specific type, but there may be two animations
+         * running at the same time for different inset types.
+         *
+         * @see #getUpperBound()
+         * @see WindowInsetsAnimationController#getHiddenStateInsets
+         * TODO: It's a bit weird that these are global per window but onProgress is hierarchical.
+         * TODO: If multiple types are animating, querying the bound per type isn't possible. Should
+         * we:
+         * 1. Offer bounds by type here?
+         * 2. Restrict one animation to one single type only?
+         * Returning WindowInsets here isn't feasible in case of overlapping animations: We can't
+         * fill in the insets for the types from the other animation into the WindowInsets object
+         * as it's changing as well.
+         */
+        public Insets getLowerBound() {
+            return mLowerBound;
+        }
+
+        /**
+         * @see #getLowerBound()
+         * @see WindowInsetsAnimationController#getShownStateInsets
+         */
+        public Insets getUpperBound() {
+            return mUpperBound;
+        }
+    }
+}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index d7c8aed..793c315 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -43,6 +43,7 @@
 import android.text.style.URLSpan;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.LongArray;
 import android.util.Pools.SynchronizedPool;
 import android.view.TouchDelegate;
@@ -91,6 +92,8 @@
 
     private static final boolean DEBUG = false;
 
+    private static final String TAG = "AccessibilityNodeInfo";
+
     /** @hide */
     public static final int UNDEFINED_CONNECTION_ID = -1;
 
@@ -990,6 +993,7 @@
      * <strong>Note:</strong> Cannot be called from an
      * {@link android.accessibilityservice.AccessibilityService}.
      * This class is made immutable before being delivered to an AccessibilityService.
+     * Note that a view cannot be made its own child.
      * </p>
      *
      * @param child The child.
@@ -1037,6 +1041,7 @@
      * hierarchy for accessibility purposes. This enables custom views that draw complex
      * content to report them selves as a tree of virtual views, thus conveying their
      * logical structure.
+     * Note that a view cannot be made its own child.
      * </p>
      *
      * @param root The root of the virtual subtree.
@@ -1054,6 +1059,11 @@
         final int rootAccessibilityViewId =
             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
+        if (childNodeId == mSourceNodeId) {
+            Log.e(TAG, "Rejecting attempt to make a View its own child");
+            return;
+        }
+
         // If we're checking uniqueness and the ID already exists, abort.
         if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) {
             return;
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 95a346f..dfd9a2e 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -187,6 +187,7 @@
     /**
      * An animation listener to be notified when the animation starts, ends or repeats.
      */
+    @UnsupportedAppUsage
     private AnimationListener mListener;
 
     /**
@@ -794,7 +795,7 @@
      * @deprecated All window animations are running with detached wallpaper.
      */
     public boolean getDetachWallpaper() {
-        return false;
+        return true;
     }
 
     /**
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index cb1d89c..9c935af 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,6 +15,7 @@
  */
 package android.view.autofill;
 
+import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -25,33 +26,47 @@
  */
 public final class AutofillId implements Parcelable {
 
+    /** @hide */
+    public static final int NO_SESSION = 0;
+
+    private static final int FLAG_IS_VIRTUAL = 0x1;
+    private static final int FLAG_HAS_SESSION = 0x2;
+
     private final int mViewId;
-    private final boolean mVirtual;
+    private final int mFlags;
     private final int mVirtualId;
+    private final int mSessionId;
 
     /** @hide */
     @TestApi
     public AutofillId(int id) {
-        mVirtual = false;
-        mViewId = id;
-        mVirtualId = View.NO_ID;
+        this(/* flags= */ 0, id, View.NO_ID, NO_SESSION);
     }
 
     /** @hide */
     @TestApi
-    public AutofillId(AutofillId parent, int virtualChildId) {
-        mVirtual = true;
-        mViewId = parent.mViewId;
-        mVirtualId = virtualChildId;
+    public AutofillId(@NonNull AutofillId parent, int virtualChildId) {
+        this(FLAG_IS_VIRTUAL, parent.mViewId, virtualChildId, NO_SESSION);
     }
 
     /** @hide */
     public AutofillId(int parentId, int virtualChildId) {
-        mVirtual = true;
+        this(FLAG_IS_VIRTUAL, parentId, virtualChildId, NO_SESSION);
+    }
+
+    /** @hide */
+    public AutofillId(@NonNull AutofillId parent, int virtualChildId, int sessionId) {
+        this(FLAG_IS_VIRTUAL | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId);
+    }
+
+    private AutofillId(int flags, int parentId, int virtualChildId, int sessionId) {
+        mFlags = flags;
         mViewId = parentId;
         mVirtualId = virtualChildId;
+        mSessionId = sessionId;
     }
 
+
     /** @hide */
     public int getViewId() {
         return mViewId;
@@ -64,7 +79,16 @@
 
     /** @hide */
     public boolean isVirtual() {
-        return mVirtual;
+        return (mFlags & FLAG_IS_VIRTUAL) != 0;
+    }
+
+    private boolean hasSession() {
+        return (mFlags & FLAG_HAS_SESSION) != 0;
+    }
+
+    /** @hide */
+    public int getSessionId() {
+        return mSessionId;
     }
 
     /////////////////////////////////
@@ -77,6 +101,7 @@
         int result = 1;
         result = prime * result + mViewId;
         result = prime * result + mVirtualId;
+        result = prime * result + mSessionId;
         return result;
     }
 
@@ -88,15 +113,19 @@
         final AutofillId other = (AutofillId) obj;
         if (mViewId != other.mViewId) return false;
         if (mVirtualId != other.mVirtualId) return false;
+        if (mSessionId != other.mSessionId) return false;
         return true;
     }
 
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder().append(mViewId);
-        if (mVirtual) {
+        if (isVirtual()) {
             builder.append(':').append(mVirtualId);
         }
+        if (hasSession()) {
+            builder.append('@').append(mSessionId);
+        }
         return builder.toString();
     }
 
@@ -108,21 +137,24 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mViewId);
-        parcel.writeInt(mVirtual ? 1 : 0);
-        parcel.writeInt(mVirtualId);
-    }
-
-    private AutofillId(Parcel parcel) {
-        mViewId = parcel.readInt();
-        mVirtual = parcel.readInt() == 1;
-        mVirtualId = parcel.readInt();
+        parcel.writeInt(mFlags);
+        if (isVirtual()) {
+            parcel.writeInt(mVirtualId);
+        }
+        if (hasSession()) {
+            parcel.writeInt(mSessionId);
+        }
     }
 
     public static final Parcelable.Creator<AutofillId> CREATOR =
             new Parcelable.Creator<AutofillId>() {
         @Override
         public AutofillId createFromParcel(Parcel source) {
-            return new AutofillId(source);
+            final int viewId = source.readInt();
+            final int flags = source.readInt();
+            final int virtualId = (flags & FLAG_IS_VIRTUAL) != 0 ? source.readInt() : View.NO_ID;
+            final int sessionId = (flags & FLAG_HAS_SESSION) != 0 ? source.readInt() : NO_SESSION;
+            return new AutofillId(flags, viewId, virtualId, sessionId);
         }
 
         @Override
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 56f973e..888a4c5 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -62,6 +63,7 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.SyncResultReceiver;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -75,8 +77,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+
 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
 
@@ -324,6 +325,17 @@
     public static final int FC_SERVICE_TIMEOUT = 5000;
 
     /**
+     * Timeout for calls to system_server.
+     */
+    private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+    /**
      * Makes an authentication id from a request id and a dataset id.
      *
      * @param requestId The request id.
@@ -612,7 +624,8 @@
 
                 final AutofillClient client = getClient();
                 if (client != null) {
-                    final SyncResultReceiver receiver = new SyncResultReceiver();
+                    final SyncResultReceiver receiver = new SyncResultReceiver(
+                            SYNC_CALLS_TIMEOUT_MS);
                     try {
                         mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
                                 mServiceClient.asBinder(), receiver);
@@ -732,9 +745,9 @@
      */
     @Nullable public FillEventHistory getFillEventHistory() {
         try {
-            final SyncResultReceiver receiver = new SyncResultReceiver();
+            final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
             mService.getFillEventHistory(receiver);
-            return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
+            return receiver.getParcelableResult();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1287,7 +1300,7 @@
     public boolean hasEnabledAutofillServices() {
         if (mService == null) return false;
 
-        final SyncResultReceiver receiver = new SyncResultReceiver();
+        final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         try {
             mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
             return receiver.getIntResult() == 1;
@@ -1304,10 +1317,10 @@
     public ComponentName getAutofillServiceComponentName() {
         if (mService == null) return null;
 
-        final SyncResultReceiver receiver = new SyncResultReceiver();
+        final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         try {
             mService.getAutofillServiceComponentName(receiver);
-            return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
+            return receiver.getParcelableResult();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1330,9 +1343,9 @@
      */
     @Nullable public String getUserDataId() {
         try {
-            final SyncResultReceiver receiver = new SyncResultReceiver();
+            final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
             mService.getUserDataId(receiver);
-            return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
+            return receiver.getStringResult();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1352,9 +1365,9 @@
      */
     @Nullable public UserData getUserData() {
         try {
-            final SyncResultReceiver receiver = new SyncResultReceiver();
+            final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
             mService.getUserData(receiver);
-            return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
+            return receiver.getParcelableResult();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1390,7 +1403,7 @@
      * the user.
      */
     public boolean isFieldClassificationEnabled() {
-        final SyncResultReceiver receiver = new SyncResultReceiver();
+        final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         try {
             mService.isFieldClassificationEnabled(receiver);
             return receiver.getIntResult() == 1;
@@ -1413,10 +1426,10 @@
      */
     @Nullable
     public String getDefaultFieldClassificationAlgorithm() {
-        final SyncResultReceiver receiver = new SyncResultReceiver();
+        final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         try {
             mService.getDefaultFieldClassificationAlgorithm(receiver);
-            return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
+            return receiver.getStringResult();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1433,11 +1446,10 @@
      */
     @NonNull
     public List<String> getAvailableFieldClassificationAlgorithms() {
-        final SyncResultReceiver receiver = new SyncResultReceiver();
+        final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         try {
             mService.getAvailableFieldClassificationAlgorithms(receiver);
-            final String[] algorithms = receiver
-                .getObjectResult(SyncResultReceiver.TYPE_STRING_ARRAY);
+            final String[] algorithms = receiver.getStringArrayResult();
             return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
@@ -1458,7 +1470,7 @@
     public boolean isAutofillSupported() {
         if (mService == null) return false;
 
-        final SyncResultReceiver receiver = new SyncResultReceiver();
+        final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         try {
             mService.isServiceSupported(mContext.getUserId(), receiver);
             return receiver.getIntResult() == 1;
@@ -1582,7 +1594,7 @@
             final AutofillClient client = getClient();
             if (client == null) return; // NOTE: getClient() already logged it..
 
-            final SyncResultReceiver receiver = new SyncResultReceiver();
+            final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
             mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, client.autofillClientGetComponentName(),
@@ -1665,7 +1677,7 @@
             mServiceClient = new AutofillManagerClient(this);
             try {
                 final int userId = mContext.getUserId();
-                final SyncResultReceiver receiver = new SyncResultReceiver();
+                final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
                 mService.addClient(mServiceClient, userId, receiver);
                 final int flags = receiver.getIntResult();
                 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
@@ -2985,105 +2997,23 @@
                 afm.post(() -> afm.autofill(sessionId, ids, values));
             }
         }
-    }
 
-    /**
-     * @hide
-     */
-    public static final class SyncResultReceiver extends IResultReceiver.Stub {
-
-        private static final String EXTRA = "EXTRA";
-
-        /**
-         * How long to block waiting for {@link IResultReceiver} callbacks when calling server.
-         */
-        private static final long BINDER_TIMEOUT_MS = 5000;
-
-        private static final int TYPE_STRING = 0;
-        private static final int TYPE_STRING_ARRAY = 1;
-        private static final int TYPE_PARCELABLE = 2;
-
-        private final CountDownLatch mLatch  = new CountDownLatch(1);
-        private int mResult;
-        private Bundle mBundle;
-
-        private void waitResult() {
-            try {
-                if (!mLatch.await(BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                    throw new IllegalStateException("Not called in " + BINDER_TIMEOUT_MS + "ms");
-                }
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-            }
-        }
-
-        /**
-         * Gets the result from an operation that returns an {@code int}.
-         */
-        int getIntResult() {
-            waitResult();
-            return mResult;
-        }
-
-        /**
-         * Gets the result from an operation that returns an {@code Object}.
-         *
-         * @param type type of expected object.
-         */
-        @Nullable
-        @SuppressWarnings("unchecked")
-        <T> T getObjectResult(int type) {
-            waitResult();
-            if (mBundle == null) {
-                return null;
-            }
-            switch (type) {
-                case TYPE_STRING:
-                    return (T) mBundle.getString(EXTRA);
-                case TYPE_STRING_ARRAY:
-                    return (T) mBundle.getStringArray(EXTRA);
-                case TYPE_PARCELABLE:
-                    return (T) mBundle.getParcelable(EXTRA);
-                default:
-                    throw new IllegalArgumentException("unsupported type: " + type);
+        @Override
+        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
+                Rect anchorBounds, IAutofillWindowPresenter presenter) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
+                        presenter));
             }
         }
 
         @Override
-        public void send(int resultCode, Bundle resultData) {
-            mResult = resultCode;
-            mBundle = resultData;
-            mLatch.countDown();
-        }
-
-        /**
-         * Creates a bundle for a {@code String} value.
-         */
-        @NonNull
-        public static Bundle bundleFor(@Nullable String value) {
-            final Bundle bundle = new Bundle();
-            bundle.putString(EXTRA, value);
-            return bundle;
-        }
-
-        /**
-         * Creates a bundle for a {@code String[]} value.
-         */
-        @NonNull
-        public static Bundle bundleFor(@Nullable String[] value) {
-            final Bundle bundle = new Bundle();
-            bundle.putStringArray(EXTRA, value);
-            return bundle;
-        }
-
-        /**
-         * Creates a bundle for a {@code Parcelable} value.
-         */
-        @NonNull
-        public static Bundle bundleFor(@Nullable Parcelable value) {
-            final Bundle bundle = new Bundle();
-            bundle.putParcelable(EXTRA, value);
-            return bundle;
+        public void requestHideFillUi(int sessionId, AutofillId id) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.requestHideFillUi(id, false));
+            }
         }
     }
 }
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 67cd0bf..140507c 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
+import android.view.autofill.IAutofillWindowPresenter;
 
 /**
  * Object running in the application process and responsible to provide the functionalities
@@ -29,6 +30,24 @@
  * @hide
  */
 interface IAugmentedAutofillManagerClient {
-   Rect getViewCoordinates(in AutofillId id);
-   void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+    /**
+      * Gets the coordinates of the input field view.
+      */
+    Rect getViewCoordinates(in AutofillId id);
+
+    /**
+     * Autofills the activity with the contents of the values.
+     */
+    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+
+    /**
+      * Requests showing the fill UI.
+      */
+    void requestShowFillUi(int sessionId, in AutofillId id, int width, int height,
+            in Rect anchorBounds, in IAutofillWindowPresenter presenter);
+
+    /**
+      * Requests hiding the fill UI.
+      */
+    void requestHideFillUi(int sessionId, in AutofillId id);
 }
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 5166831..63c21f3 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -33,7 +33,7 @@
 final class ChildContentCaptureSession extends ContentCaptureSession {
 
     @NonNull
-    private final MainContentCaptureSession mParent;
+    private final ContentCaptureSession mParent;
 
     /**
      * {@link ContentCaptureContext} set by client, or {@code null} when it's the
@@ -46,46 +46,55 @@
     private final ContentCaptureContext mClientContext;
 
     /** @hide */
-    protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent,
+    protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent,
             @NonNull ContentCaptureContext clientContext) {
         mParent = parent;
         mClientContext = Preconditions.checkNotNull(clientContext);
     }
 
     @Override
-    ContentCaptureSession newChild(@NonNull ContentCaptureContext context) {
-        // TODO(b/121033016): implement it
-        throw new UnsupportedOperationException("grand-children not implemented yet");
+    MainContentCaptureSession getMainCaptureSession() {
+        if (mParent instanceof MainContentCaptureSession) {
+            return (MainContentCaptureSession) mParent;
+        }
+        return mParent.getMainCaptureSession();
     }
 
     @Override
-    void flush() {
-        mParent.flush();
+    ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
+        final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
+        getMainCaptureSession().notifyChildSessionStarted(mId, child.mId, clientContext);
+        return child;
+    }
+
+    @Override
+    void flush(@FlushReason int reason) {
+        mParent.flush(reason);
     }
 
     @Override
     void onDestroy() {
-        mParent.notifyChildSessionFinished(mParent.mId, mId);
+        getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
     }
 
     @Override
     void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
-        mParent.notifyViewAppeared(mId, node);
+        getMainCaptureSession().notifyViewAppeared(mId, node);
     }
 
     @Override
     void internalNotifyViewDisappeared(@NonNull AutofillId id) {
-        mParent.notifyViewDisappeared(mId, id);
+        getMainCaptureSession().notifyViewDisappeared(mId, id);
     }
 
     @Override
-    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags) {
-        mParent.notifyViewTextChanged(mId, id, text, flags);
+    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
+        getMainCaptureSession().notifyViewTextChanged(mId, id, text);
     }
+
     @Override
     boolean isContentCaptureEnabled() {
-        return mParent.isContentCaptureEnabled();
+        return getMainCaptureSession().isContentCaptureEnabled();
     }
 
     @Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 9e3da92..43963c3 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 import android.view.autofill.AutofillId;
 
 import com.android.internal.util.Preconditions;
@@ -28,11 +29,15 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 /** @hide */
 @SystemApi
 public final class ContentCaptureEvent implements Parcelable {
 
+    private static final String TAG = ContentCaptureEvent.class.getSimpleName();
+
     /** @hide */
     public static final int TYPE_SESSION_FINISHED = -2;
     /** @hide */
@@ -46,9 +51,11 @@
     public static final int TYPE_VIEW_APPEARED = 1;
 
     /**
-     * Called when a node has been removed from the screen and is not visible to the user anymore.
+     * Called when one or more nodes have been removed from the screen and is not visible to the
+     * user anymore.
      *
-     * <p>The id of the node is available through {@link #getId()}.
+     * <p>To get the id(s), first call {@link #getIds()} - if it returns {@code null}, then call
+     * {@link #getId()}.
      */
     public static final int TYPE_VIEW_DISAPPEARED = 2;
 
@@ -74,29 +81,23 @@
     private final @NonNull String mSessionId;
     private final int mType;
     private final long mEventTime;
-    private final int mFlags;
     private @Nullable AutofillId mId;
+    private @Nullable ArrayList<AutofillId> mIds;
     private @Nullable ViewNode mNode;
     private @Nullable CharSequence mText;
     private @Nullable String mParentSessionId;
     private @Nullable ContentCaptureContext mClientContext;
 
     /** @hide */
-    public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) {
+    public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime) {
         mSessionId = sessionId;
         mType = type;
         mEventTime = eventTime;
-        mFlags = flags;
-    }
-
-    /** @hide */
-    public ContentCaptureEvent(@NonNull String sessionId, int type, int flags) {
-        this(sessionId, type, System.currentTimeMillis(), flags);
     }
 
     /** @hide */
     public ContentCaptureEvent(@NonNull String sessionId, int type) {
-        this(sessionId, type, /* flags= */ 0);
+        this(sessionId, type, System.currentTimeMillis());
     }
 
     /** @hide */
@@ -105,6 +106,27 @@
         return this;
     }
 
+    private void setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
+        mIds = Preconditions.checkNotNull(ids);
+    }
+
+    /**
+     * Adds an autofill id to the this event, merging the single id into a list if necessary.
+     * @hide */
+    public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) {
+        if (mIds == null) {
+            mIds = new ArrayList<>();
+            if (mId == null) {
+                Log.w(TAG, "addAutofillId(" + id + ") called without an initial id");
+            } else {
+                mIds.add(mId);
+                mId = null;
+            }
+        }
+        mIds.add(id);
+        return this;
+    }
+
     /**
      * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}.
      *
@@ -183,16 +205,6 @@
     }
 
     /**
-     * Gets optional flags associated with the event.
-     *
-     * @return either {@code 0} or
-     * {@link android.view.contentcapture.ContentCaptureSession#FLAG_USER_INPUT}.
-     */
-    public int getFlags() {
-        return mFlags;
-    }
-
-    /**
      * Gets the whole metadata of the node associated with the event.
      *
      * <p>Only set on {@link #TYPE_VIEW_APPEARED} events.
@@ -205,7 +217,9 @@
     /**
      * Gets the {@link AutofillId} of the node associated with the event.
      *
-     * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED} and {@link #TYPE_VIEW_TEXT_CHANGED} events.
+     * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED} (when the event contains just one node - if
+     * it contains more than one, this method returns {@code null} and the actual ids should be
+     * retrived by {@link #getIds()}) and {@link #TYPE_VIEW_TEXT_CHANGED} events.
      */
     @Nullable
     public AutofillId getId() {
@@ -213,6 +227,17 @@
     }
 
     /**
+     * Gets the {@link AutofillId AutofillIds} of the nodes associated with the event.
+     *
+     * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED}, when the event contains more than one node
+     * (if it contains just one node, it's returned by {@link #getId()} instead.
+     */
+    @Nullable
+    public List<AutofillId> getIds() {
+        return mIds;
+    }
+
+    /**
      * Gets the current text of the node associated with the event.
      *
      * <p>Only set on {@link #TYPE_VIEW_TEXT_CHANGED} events.
@@ -226,12 +251,12 @@
     public void dump(@NonNull PrintWriter pw) {
         pw.print("type="); pw.print(getTypeAsString(mType));
         pw.print(", time="); pw.print(mEventTime);
-        if (mFlags > 0) {
-            pw.print(", flags="); pw.print(mFlags);
-        }
         if (mId != null) {
             pw.print(", id="); pw.print(mId);
         }
+        if (mIds != null) {
+            pw.print(", ids="); pw.print(mIds);
+        }
         if (mNode != null) {
             pw.print(", mNode.id="); pw.print(mNode.getAutofillId());
         }
@@ -251,12 +276,16 @@
     public String toString() {
         final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
                 .append(getTypeAsString(mType));
-        if (mFlags > 0) {
-            string.append(", flags=").append(mFlags);
+        string.append(", session=").append(mSessionId);
+        if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) {
+            string.append(", parent=").append(mParentSessionId);
         }
         if (mId != null) {
             string.append(", id=").append(mId);
         }
+        if (mIds != null) {
+            string.append(", ids=").append(mIds);
+        }
         if (mNode != null) {
             final String className = mNode.getClassName();
             if (mNode != null) {
@@ -277,8 +306,8 @@
         parcel.writeString(mSessionId);
         parcel.writeInt(mType);
         parcel.writeLong(mEventTime);
-        parcel.writeInt(mFlags);
         parcel.writeParcelable(mId, flags);
+        parcel.writeTypedList(mIds);
         ViewNode.writeToParcel(parcel, mNode, flags);
         parcel.writeCharSequence(mText);
         if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) {
@@ -297,13 +326,15 @@
             final String sessionId = parcel.readString();
             final int type = parcel.readInt();
             final long eventTime  = parcel.readLong();
-            final int flags = parcel.readInt();
-            final ContentCaptureEvent event =
-                    new ContentCaptureEvent(sessionId, type, eventTime, flags);
+            final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type, eventTime);
             final AutofillId id = parcel.readParcelable(null);
             if (id != null) {
                 event.setAutofillId(id);
             }
+            final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR);
+            if (ids != null) {
+                event.setAutofillIds(ids);
+            }
             final ViewNode node = ViewNode.readFromParcel(parcel);
             if (node != null) {
                 event.setViewNode(node);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 9830790..413f1a5 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -15,6 +15,8 @@
  */
 package android.view.contentcapture;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
@@ -22,14 +24,18 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
 import android.util.Log;
+import android.view.contentcapture.ContentCaptureSession.FlushReason;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.SyncResultReceiver;
 
 import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /*
  * NOTE: all methods in this class should return right away, or do the real work in a handler
@@ -46,14 +52,19 @@
 
     private static final String TAG = ContentCaptureManager.class.getSimpleName();
 
-    private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
+    /**
+     * Timeout for calls to system_server.
+     */
+    private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
 
     // TODO(b/121044306): define a way to dynamically set them(for example, using settings?)
     static final boolean VERBOSE = false;
     static final boolean DEBUG = true; // STOPSHIP if not set to false
 
-    @NonNull
-    private final AtomicBoolean mDisabled = new AtomicBoolean();
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private boolean mDisabled;
 
     @NonNull
     private final Context mContext;
@@ -61,35 +72,29 @@
     @Nullable
     private final IContentCaptureManager mService;
 
+    // Flags used for starting session.
+    @GuardedBy("mLock")
+    private int mFlags;
+
     // TODO(b/119220549): use UI Thread directly (as calls are one-way) or a shared thread / handler
     // held at the Application level
     @NonNull
     private final Handler mHandler;
 
+    @GuardedBy("mLock")
     private MainContentCaptureSession mMainSession;
 
     /** @hide */
     public ContentCaptureManager(@NonNull Context context,
             @Nullable IContentCaptureManager service) {
         mContext = Preconditions.checkNotNull(context, "context cannot be null");
-        if (VERBOSE) {
-            Log.v(TAG, "Constructor for " + context.getPackageName());
-        }
-        mService = service;
-        // TODO(b/119220549): use an existing bg thread instead...
-        final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
-        bgThread.start();
-        mHandler = Handler.createAsync(bgThread.getLooper());
-    }
+        if (VERBOSE) Log.v(TAG, "Constructor for " + context.getPackageName());
 
-    @NonNull
-    private static Handler newHandler() {
-        // TODO(b/119220549): use an existing bg thread instead...
-        // TODO(b/119220549): use UI Thread directly (as calls are one-way) or an existing bgThread
-        // or a shared thread / handler held at the Application level
-        final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
-        bgThread.start();
-        return Handler.createAsync(bgThread.getLooper());
+        mService = service;
+        // TODO(b/119220549): we might not even need a handler, as the IPCs are oneway. But if we
+        // do, then we should optimize it to run the tests after the Choreographer finishes the most
+        // important steps of the frame.
+        mHandler = Handler.createAsync(Looper.getMainLooper());
     }
 
     /**
@@ -104,26 +109,29 @@
     @NonNull
     @UiThread
     public MainContentCaptureSession getMainContentCaptureSession() {
-        if (mMainSession == null) {
-            mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
-                    mDisabled);
-            if (VERBOSE) {
-                Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
+        synchronized (mLock) {
+            if (mMainSession == null) {
+                mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
+                        mDisabled);
+                if (VERBOSE) {
+                    Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
+                }
             }
+            return mMainSession;
         }
-        return mMainSession;
     }
 
     /** @hide */
     public void onActivityStarted(@NonNull IBinder applicationToken,
-            @NonNull ComponentName activityComponent) {
-        // TODO(b/121033016): must start all sessions
-        getMainContentCaptureSession().start(applicationToken, activityComponent);
+            @NonNull ComponentName activityComponent, int flags) {
+        synchronized (mLock) {
+            mFlags |= flags;
+            getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags);
+        }
     }
 
     /** @hide */
     public void onActivityStopped() {
-        // TODO(b/121033016): must finish all sessions
         getMainContentCaptureSession().destroy();
     }
 
@@ -134,9 +142,8 @@
      *
      * @hide
      */
-    public void flush() {
-        // TODO(b/121033016): must flush all sessions
-        getMainContentCaptureSession().flush();
+    public void flush(@FlushReason int reason) {
+        getMainContentCaptureSession().flush(reason);
     }
 
     /**
@@ -145,15 +152,30 @@
      */
     @Nullable
     public ComponentName getServiceComponentName() {
-        //TODO(b/121047489): implement
-        return null;
+        if (!isContentCaptureEnabled()) {
+            return null;
+        }
+        // Wait for system server to return the component name.
+        final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
+        mHandler.sendMessage(obtainMessage(
+                ContentCaptureManager::handleReceiverServiceComponentName,
+                this, mContext.getUserId(), resultReceiver));
+
+        try {
+            return resultReceiver.getParcelableResult();
+        } catch (RemoteException e) {
+            // Unable to retrieve component name in a reasonable amount of time.
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * Checks whether content capture is enabled for this activity.
      */
     public boolean isContentCaptureEnabled() {
-        return mService != null && !mDisabled.get();
+        synchronized (mLock) {
+            return mService != null && !mDisabled;
+        }
     }
 
     /**
@@ -163,7 +185,9 @@
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
      */
     public void setContentCaptureEnabled(boolean enabled) {
-        //TODO(b/111276913): implement (need to finish / disable all sessions)
+        synchronized (mLock) {
+            mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP;
+        }
     }
 
     /**
@@ -178,20 +202,32 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.println("ContentCaptureManager");
-
-        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled.get());
-        pw.print(prefix); pw.print("Context: "); pw.println(mContext);
-        pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
-        if (mService != null) {
-            pw.print(prefix); pw.print("Service: "); pw.println(mService);
+        synchronized (mLock) {
+            pw.print(prefix); pw.println("ContentCaptureManager");
+            pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+            pw.print(prefix); pw.print("Context: "); pw.println(mContext);
+            pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
+            if (mService != null) {
+                pw.print(prefix); pw.print("Service: "); pw.println(mService);
+            }
+            pw.print(prefix); pw.print("Flags: "); pw.println(mFlags);
+            if (mMainSession != null) {
+                final String prefix2 = prefix + "  ";
+                pw.print(prefix); pw.println("Main session:");
+                mMainSession.dump(prefix2, pw);
+            } else {
+                pw.print(prefix); pw.println("No sessions");
+            }
         }
-        if (mMainSession != null) {
-            final String prefix2 = prefix + "  ";
-            pw.print(prefix); pw.println("Main session:");
-            mMainSession.dump(prefix2, pw);
-        } else {
-            pw.print(prefix); pw.println("No sessions");
+    }
+
+
+    /** Retrieves the component name of the target content capture service through system_server. */
+    private void handleReceiverServiceComponentName(int userId, IResultReceiver resultReceiver) {
+        try {
+            mService.getReceiverServiceComponentName(userId, resultReceiver);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Unable to retrieve service component name: " + e);
         }
     }
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 46e6882..b620ab1 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -19,22 +19,28 @@
 import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
 
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.DebugUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
 import dalvik.system.CloseGuard;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Session used to notify a system-provided Content Capture service about events associated with
@@ -45,62 +51,120 @@
     private static final String TAG = ContentCaptureSession.class.getSimpleName();
 
     /**
-     * Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the
-     *
-     * thext change was caused by user input (for example, through IME).
-     */
-    public static final int FLAG_USER_INPUT = 0x1;
-
-    /**
      * Initial state, when there is no session.
      *
      * @hide
      */
-    public static final int STATE_UNKNOWN = 0;
+    // NOTE: not prefixed by STATE_ so it's not printed on getStateAsString()
+    public static final int UNKNWON_STATE = 0x0;
 
     /**
      * Service's startSession() was called, but server didn't confirm it was created yet.
      *
      * @hide
      */
-    public static final int STATE_WAITING_FOR_SERVER = 1;
+    public static final int STATE_WAITING_FOR_SERVER = 0x1;
 
     /**
      * Session is active.
      *
      * @hide
      */
-    public static final int STATE_ACTIVE = 2;
+    public static final int STATE_ACTIVE = 0x2;
 
     /**
-     * Session is disabled.
+     * Session is disabled because there is no service for this user.
      *
      * @hide
      */
-    public static final int STATE_DISABLED = 3;
+    public static final int STATE_DISABLED = 0x4;
 
     /**
      * Session is disabled because its id already existed on server.
      *
      * @hide
      */
-    public static final int STATE_DISABLED_DUPLICATED_ID = 4;
+    public static final int STATE_DUPLICATED_ID = 0x8;
+
+    /**
+     * Session is disabled because service is not set for user.
+     *
+     * @hide
+     */
+    public static final int STATE_NO_SERVICE = 0x10;
+
+    /**
+     * Session is disabled by FLAG_SECURE
+     *
+     * @hide
+     */
+    public static final int STATE_FLAG_SECURE = 0x20;
+
+    /**
+     * Session is disabled manually by the specific app.
+     *
+     * @hide
+     */
+    public static final int STATE_BY_APP = 0x40;
+
+    /**
+     * Session is disabled because session start was never replied.
+     *
+     * @hide
+     */
+    public static final int STATE_NO_RESPONSE = 0x80;
+
+    /**
+     * Session is disabled because an internal error.
+     *
+     * @hide
+     */
+    public static final int STATE_INTERNAL_ERROR = 0x100;
 
     private static final int INITIAL_CHILDREN_CAPACITY = 5;
 
+    /** @hide */
+    public static final int FLUSH_REASON_FULL = 1;
+    /** @hide */
+    public static final int FLUSH_REASON_ACTIVITY_PAUSED = 2;
+    /** @hide */
+    public static final int FLUSH_REASON_ACTIVITY_RESUMED = 3;
+    /** @hide */
+    public static final int FLUSH_REASON_SESSION_STARTED = 4;
+    /** @hide */
+    public static final int FLUSH_REASON_SESSION_FINISHED = 5;
+    /** @hide */
+    public static final int FLUSH_REASON_IDLE_TIMEOUT = 6;
+
+    /** @hide */
+    @IntDef(prefix = { "FLUSH_REASON_" }, value = {
+            FLUSH_REASON_FULL,
+            FLUSH_REASON_ACTIVITY_PAUSED,
+            FLUSH_REASON_ACTIVITY_RESUMED,
+            FLUSH_REASON_SESSION_STARTED,
+            FLUSH_REASON_SESSION_FINISHED,
+            FLUSH_REASON_IDLE_TIMEOUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface FlushReason{}
+
+
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
+    private final Object mLock = new Object();
+
     /**
      * Guard use to ignore events after it's destroyed.
      */
     @NonNull
-    private final AtomicBoolean mDestroyed = new AtomicBoolean();
+    @GuardedBy("mLock")
+    private boolean mDestroyed;
 
     /** @hide */
     @Nullable
-    protected final String mId = UUID.randomUUID().toString();
+    protected final String mId;
 
-    private int mState = STATE_UNKNOWN;
+    private int mState = UNKNWON_STATE;
 
     // Lazily created on demand.
     private ContentCaptureSessionId mContentCaptureSessionId;
@@ -108,18 +172,26 @@
     /**
      * List of children session.
      */
-    // TODO(b/121033016): need to synchonize access, either by changing on handler or UI thread
-    // (for now there's no handler on this class, so we need to wait for the next refactoring),
-    // most likely the former (as we have no guarantee that createContentCaptureSession()
-    // it will be called in the UiThread; for example, WebView most likely won't call on it)
     @Nullable
+    @GuardedBy("mLock")
     private ArrayList<ContentCaptureSession> mChildren;
 
     /** @hide */
     protected ContentCaptureSession() {
+        this(UUID.randomUUID().toString());
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public ContentCaptureSession(@NonNull String id) {
+        mId = Preconditions.checkNotNull(id);
         mCloseGuard.open("destroy");
     }
 
+    /** @hide */
+    @NonNull
+    abstract MainContentCaptureSession getMainCaptureSession();
+
     /**
      * Gets the id used to identify this session.
      */
@@ -130,6 +202,13 @@
         return mContentCaptureSessionId;
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public int getIdAsInt() {
+        // TODO(b/121197119): use sessionId instead of hashcode once it's changed to int
+        return mId.hashCode();
+    }
+
     /**
      * Creates a new {@link ContentCaptureSession}.
      *
@@ -143,10 +222,12 @@
             Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
                     + child.mId);
         }
-        if (mChildren == null) {
-            mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY);
+        synchronized (mLock) {
+            if (mChildren == null) {
+                mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY);
+            }
+            mChildren.add(child);
         }
-        mChildren.add(child);
         return child;
     }
 
@@ -155,7 +236,7 @@
     /**
      * Flushes the buffered events to the service.
      */
-    abstract void flush();
+    abstract void flush(@FlushReason int reason);
 
     /**
      * Destroys this session, flushing out all pending notifications to the service.
@@ -163,35 +244,37 @@
      * <p>Once destroyed, any new notification will be dropped.
      */
     public final void destroy() {
-        if (!mDestroyed.compareAndSet(false, true)) {
-            Log.e(TAG, "destroy(): already destroyed");
-            return;
-        }
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Log.e(TAG, "destroy(" + mId + "): already destroyed");
+                return;
+            }
+            mDestroyed = true;
 
-        mCloseGuard.close();
+            mCloseGuard.close();
 
-        //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
-        // id) and send it to the cache of batched commands
-        if (VERBOSE) {
-            Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
-        }
-
-        // Finish children first
-        if (mChildren != null) {
-            final int numberChildren = mChildren.size();
-            if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
-            for (int i = 0; i < numberChildren; i++) {
-                final ContentCaptureSession child = mChildren.get(i);
-                try {
-                    child.destroy();
-                } catch (Exception e) {
-                    Log.w(TAG, "exception destroying child session #" + i + ": " + e);
+            // TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+            // id) and send it to the cache of batched commands
+            if (VERBOSE) {
+                Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
+            }
+            // Finish children first
+            if (mChildren != null) {
+                final int numberChildren = mChildren.size();
+                if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
+                for (int i = 0; i < numberChildren; i++) {
+                    final ContentCaptureSession child = mChildren.get(i);
+                    try {
+                        child.destroy();
+                    } catch (Exception e) {
+                        Log.w(TAG, "exception destroying child session #" + i + ": " + e);
+                    }
                 }
             }
         }
 
         try {
-            flush();
+            flush(FLUSH_REASON_SESSION_FINISHED);
         } finally {
             onDestroy();
         }
@@ -257,12 +340,36 @@
     abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
 
     /**
+     * Notifies the Content Capture Service that many nodes has been removed from a virtual view
+     * structure.
+     *
+     * <p>Should only be called by views that handle their own virtual view hierarchy.
+     *
+     * @param hostId id of the view hosting the virtual hierarchy.
+     * @param virtualIds ids of the virtual children.
+     *
+     * @throws IllegalArgumentException if the {@code hostId} is an autofill id for a virtual view.
+     * @throws IllegalArgumentException if {@code virtualIds} is empty
+     */
+    public final void notifyViewsDisappeared(@NonNull AutofillId hostId,
+            @NonNull int[] virtualIds) {
+        Preconditions.checkArgument(!hostId.isVirtual(), "parent cannot be virtual");
+        Preconditions.checkArgument(!ArrayUtils.isEmpty(virtualIds), "virtual ids cannot be empty");
+        if (!isContentCaptureEnabled()) return;
+
+        // TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is
+        // parcelized
+        for (int id : virtualIds) {
+            internalNotifyViewDisappeared(new AutofillId(hostId, id, getIdAsInt()));
+        }
+    }
+
+    /**
      * Notifies the Intelligence Service that the value of a text node has been changed.
      *
      * @param id of the node.
      * @param text new text.
-     * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
-     * changed by the user (for example, through the keyboard).
+     * @param flags currently ignored.
      */
     public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
             int flags) {
@@ -270,11 +377,11 @@
 
         if (!isContentCaptureEnabled()) return;
 
-        internalNotifyViewTextChanged(id, text, flags);
+        internalNotifyViewTextChanged(id, text);
     }
 
-    abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags);
+    abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
+            @Nullable CharSequence text);
 
     /**
      * Creates a {@link ViewStructure} for a "standard" view.
@@ -287,6 +394,24 @@
     }
 
     /**
+     * Creates a new {@link AutofillId} for a virtual child, so it can be used to uniquely identify
+     * the children in the session.
+     *
+     * @param parentId id of the virtual view parent (it can be obtained by calling
+     * {@link ViewStructure#getAutofillId()} on the parent).
+     * @param virtualChildId id of the virtual child, relative to the parent.
+     *
+     * @return if for the virtual child
+     *
+     * @throws IllegalArgumentException if the {@code parentId} is a virtual child id.
+     */
+    public @NonNull AutofillId newAutofillId(@NonNull AutofillId parentId, int virtualChildId) {
+        Preconditions.checkNotNull(parentId);
+        Preconditions.checkArgument(!parentId.isVirtual(), "virtual ids cannot have children");
+        return new AutofillId(parentId, virtualChildId, getIdAsInt());
+    }
+
+    /**
      * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
      * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
      *
@@ -295,33 +420,34 @@
      * @param virtualId id of the virtual child, relative to the parent.
      *
      * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
-     *
-     * @hide
      */
     @NonNull
     public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
             int virtualId) {
-        return new ViewNode.ViewStructureImpl(parentId, virtualId);
+        return new ViewNode.ViewStructureImpl(parentId, virtualId, getIdAsInt());
     }
 
     boolean isContentCaptureEnabled() {
-        return !mDestroyed.get();
+        synchronized (mLock) {
+            return !mDestroyed;
+        }
     }
 
     @CallSuper
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("id: "); pw.println(mId);
-        pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed.get());
-        if (mChildren != null && !mChildren.isEmpty()) {
-            final String prefix2 = prefix + "  ";
-            final int numberChildren = mChildren.size();
-            pw.print(prefix); pw.print("number children: "); pw.println(numberChildren);
-            for (int i = 0; i < numberChildren; i++) {
-                final ContentCaptureSession child = mChildren.get(i);
-                pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
+        synchronized (mLock) {
+            pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+            if (mChildren != null && !mChildren.isEmpty()) {
+                final String prefix2 = prefix + "  ";
+                final int numberChildren = mChildren.size();
+                pw.print(prefix); pw.print("number children: "); pw.println(numberChildren);
+                for (int i = 0; i < numberChildren; i++) {
+                    final ContentCaptureSession child = mChildren.get(i);
+                    pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
+                }
             }
         }
-
     }
 
     @Override
@@ -329,24 +455,31 @@
         return mId;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @NonNull
     protected static String getStateAsString(int state) {
-        switch (state) {
-            case STATE_UNKNOWN:
-                return "UNKNOWN";
-            case STATE_WAITING_FOR_SERVER:
-                return "WAITING_FOR_SERVER";
-            case STATE_ACTIVE:
-                return "ACTIVE";
-            case STATE_DISABLED:
-                return "DISABLED";
-            case STATE_DISABLED_DUPLICATED_ID:
-                return "DISABLED_DUPLICATED_ID";
+        return state + " (" + (state == UNKNWON_STATE ? "UNKNOWN"
+                : DebugUtils.flagsToString(ContentCaptureSession.class, "STATE_", state)) + ")";
+    }
+
+    /** @hide */
+    @NonNull
+    static String getflushReasonAsString(@FlushReason int reason) {
+        switch (reason) {
+            case FLUSH_REASON_FULL:
+                return "FULL";
+            case FLUSH_REASON_ACTIVITY_PAUSED:
+                return "PAUSED";
+            case FLUSH_REASON_ACTIVITY_RESUMED:
+                return "RESUMED";
+            case FLUSH_REASON_SESSION_STARTED:
+                return "STARTED";
+            case FLUSH_REASON_SESSION_FINISHED:
+                return "FINISHED";
+            case FLUSH_REASON_IDLE_TIMEOUT:
+                return "IDLE";
             default:
-                return "INVALID:" + state;
+                return "UNKOWN-" + reason;
         }
     }
 }
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 01776f8..be9c00f 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -32,7 +32,28 @@
   * @hide
   */
 oneway interface IContentCaptureManager {
+    /**
+     * Starts a new session for the provided {@code userId} running as part of the
+     * app's activity identified by {@code activityToken}/{@code componentName}.
+     *
+     * @param sessionId Unique session id as provided by the app.
+     * @param flags Meta flags that enable or disable content capture (see
+     *     {@link IContentCaptureContext#flags}).
+     */
     void startSession(int userId, IBinder activityToken, in ComponentName componentName,
                       String sessionId, int flags, in IResultReceiver result);
+
+    /**
+     * Marks the end of a session for the provided {@code userId} identified by
+     * the corresponding {@code startSession}'s {@code sessionId}.
+     */
     void finishSession(int userId, String sessionId);
+
+    /**
+     * Returns the content capture service's component name (if enabled and
+     * connected).
+     * @param Receiver of the content capture service's @{code ComponentName}
+     *     provided {@code Bundle} with key "{@code EXTRA}".
+     */
+    void getReceiverServiceComponentName(int userId, in IResultReceiver result);
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index baf4a35..103d7e6 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -35,7 +35,7 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
-import android.os.SystemClock;
+import android.util.LocalLog;
 import android.util.Log;
 import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
@@ -88,6 +88,7 @@
      */
     public static final String EXTRA_BINDER = "binder";
 
+    // TODO(b/111276913): make sure disabled state is in sync with manager's disabled
     @NonNull
     private final AtomicBoolean mDisabled;
 
@@ -113,7 +114,7 @@
     @Nullable
     private DeathRecipient mDirectServiceVulture;
 
-    private int mState = STATE_UNKNOWN;
+    private int mState = UNKNWON_STATE;
 
     @Nullable
     private IBinder mApplicationToken;
@@ -130,14 +131,22 @@
     // Used just for debugging purposes (on dump)
     private long mNextFlush;
 
+    // TODO(b/121044064): use settings to set size
+    private final LocalLog mFlushHistory = new LocalLog(10);
+
     /** @hide */
     protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
             @Nullable IContentCaptureManager systemServerInterface,
-            @NonNull AtomicBoolean disabled) {
+            @NonNull boolean disabled) {
         mContext = context;
         mHandler = handler;
         mSystemServerInterface = systemServerInterface;
-        mDisabled = disabled;
+        mDisabled = new AtomicBoolean(disabled);
+    }
+
+    @Override
+    MainContentCaptureSession getMainCaptureSession() {
+        return this;
     }
 
     @Override
@@ -152,7 +161,8 @@
      *
      * @hide
      */
-    void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
+    void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent,
+            int flags) {
         if (!isContentCaptureEnabled()) return;
 
         if (VERBOSE) {
@@ -161,25 +171,31 @@
         }
 
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleStartSession, this,
-                applicationToken, activityComponent));
+                applicationToken, activityComponent, flags));
     }
 
     @Override
-    void flush() {
-        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleForceFlush, this));
+    void flush(@FlushReason int reason) {
+        mHandler.sendMessage(
+                obtainMessage(MainContentCaptureSession::handleForceFlush, this, reason));
     }
 
     @Override
     void onDestroy() {
+        mHandler.removeMessages(MSG_FLUSH);
         mHandler.sendMessage(
                 obtainMessage(MainContentCaptureSession::handleDestroySession, this));
     }
 
-    private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
-        if (mState != STATE_UNKNOWN) {
-            // TODO(b/111276913): revisit this scenario
-            Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
-                    + getStateAsString(mState));
+    private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName,
+            int flags) {
+        if (handleHasStarted()) {
+            // TODO(b/122959591): make sure this is expected (and when), or use Log.w
+            if (DEBUG) {
+                Log.d(TAG, "ignoring handleStartSession(" + token + "/"
+                        + ComponentName.flattenToShortString(componentName) + " while on state "
+                        + getStateAsString(mState));
+            }
             return;
         }
         mState = STATE_WAITING_FOR_SERVER;
@@ -188,11 +204,12 @@
 
         if (VERBOSE) {
             Log.v(TAG, "handleStartSession(): token=" + token + ", act="
-                    + getActivityDebugName() + ", id=" + mId);
+                    + getDebugState() + ", id=" + mId);
         }
-        final int flags = 0; // TODO(b/111276913): get proper flags
 
         try {
+            if (mSystemServerInterface == null) return;
+
             mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
                     componentName, mId, flags, new IResultReceiver.Stub() {
                         @Override
@@ -202,7 +219,7 @@
                                 binder = resultData.getBinder(EXTRA_BINDER);
                                 if (binder == null) {
                                     Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
-                                    handleResetState();
+                                    handleResetSession(STATE_DISABLED | STATE_INTERNAL_ERROR);
                                     return;
                                 }
                             }
@@ -224,7 +241,6 @@
      * @param binder handle to {@code IContentCaptureDirectManager}
      */
     private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
-        mState = resultCode;
         if (binder != null) {
             mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
             mDirectServiceVulture = () -> {
@@ -237,28 +253,43 @@
                 Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
             }
         }
-        if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
-            mDisabled.set(true);
-            handleResetSession(/* resetState= */ false);
+
+        if ((resultCode & STATE_DISABLED) != 0) {
+            handleResetSession(resultCode);
         } else {
+            mState = resultCode;
             mDisabled.set(false);
         }
         if (VERBOSE) {
-            Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+            Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
                     + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
-                    + ", binder=" + binder);
+                    + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
         }
     }
 
     private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+        final int eventType = event.getType();
+        if (!handleHasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
+            // TODO(b/120494182): comment when this could happen (dialogs?)
+            Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+                    + ContentCaptureEvent.getTypeAsString(eventType)
+                    + "): session not started yet");
+            return;
+        }
+        if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
         if (mEvents == null) {
             if (VERBOSE) {
-                Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+                Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+                        + ContentCaptureEvent.getTypeAsString(eventType)
+                        + "): creating buffer for " + MAX_BUFFER_SIZE + " events");
             }
             mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
         }
 
-        if (!mEvents.isEmpty() && event.getType() == TYPE_VIEW_TEXT_CHANGED) {
+        // Some type of events can be merged together
+        boolean addEvent = true;
+
+        if (!mEvents.isEmpty() && eventType == TYPE_VIEW_TEXT_CHANGED) {
             final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
 
             // TODO(b/121045053): check if flags match
@@ -269,10 +300,24 @@
                             + event.getText());
                 }
                 lastEvent.setText(event.getText());
-            } else {
-                mEvents.add(event);
+                addEvent = false;
             }
-        } else {
+        }
+
+        if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) {
+            final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
+            if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED
+                    && event.getSessionId().equals(lastEvent.getSessionId())) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session "
+                            + lastEvent.getSessionId());
+                }
+                lastEvent.addAutofillId(event.getId());
+                addEvent = false;
+            }
+        }
+
+        if (addEvent) {
             mEvents.add(event);
         }
 
@@ -281,74 +326,104 @@
         final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
 
         if (bufferEvent && !forceFlush) {
-            handleScheduleFlush(/* checkExisting= */ true);
+            handleScheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true);
             return;
         }
 
-        if (mState != STATE_ACTIVE) {
+        if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
             // Callback from startSession hasn't been called yet - typically happens on system
             // apps that are started before the system service
-            // TODO(b/111276913): try to ignore session while system is not ready / boot
+            // TODO(b/122959591): try to ignore session while system is not ready / boot
             // not complete instead. Similarly, the manager service should return right away
             // when the user does not have a service set
-            if (VERBOSE) {
-                Log.v(TAG, "Closing session for " + getActivityDebugName()
-                        + " after " + numberEvents + " delayed events and state "
-                        + getStateAsString(mState));
+            if (DEBUG) {
+                Log.d(TAG, "Closing session for " + getDebugState()
+                        + " after " + numberEvents + " delayed events");
             }
-            handleResetState();
+            handleResetSession(STATE_DISABLED | STATE_NO_RESPONSE);
             // TODO(b/111276913): blacklist activity / use special flag to indicate that
             // when it's launched again
             return;
         }
+        final int flushReason;
+        switch (eventType) {
+            case ContentCaptureEvent.TYPE_SESSION_STARTED:
+                flushReason = FLUSH_REASON_SESSION_STARTED;
+                break;
+            case ContentCaptureEvent.TYPE_SESSION_FINISHED:
+                flushReason = FLUSH_REASON_SESSION_FINISHED;
+                break;
+            default:
+                flushReason = FLUSH_REASON_FULL;
+        }
 
-        handleForceFlush();
+        handleForceFlush(flushReason);
     }
 
-    private void handleScheduleFlush(boolean checkExisting) {
+    private boolean handleHasStarted() {
+        return mState != UNKNWON_STATE;
+    }
+
+    private void handleScheduleFlush(@FlushReason int reason, boolean checkExisting) {
+        if (!handleHasStarted()) {
+            Log.v(TAG, "handleScheduleFlush(" + getDebugState() + "): session not started yet");
+            return;
+        }
         if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
             // "Renew" the flush message by removing the previous one
             mHandler.removeMessages(MSG_FLUSH);
         }
-        mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
+        mNextFlush = System.currentTimeMillis() + FLUSHING_FREQUENCY_MS;
         if (VERBOSE) {
-            Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+            Log.v(TAG, "handleScheduleFlush(" + getDebugState()
+                    + ", reason=" + getflushReasonAsString(reason) + "): scheduled to flush in "
+                    + FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
         }
         mHandler.sendMessageDelayed(
-                obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this)
+                obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this, reason)
                 .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS);
     }
 
-    private void handleFlushIfNeeded() {
+    private void handleFlushIfNeeded(@FlushReason int reason) {
         if (mEvents.isEmpty()) {
             if (VERBOSE) Log.v(TAG, "Nothing to flush");
             return;
         }
-        handleForceFlush();
+        handleForceFlush(reason);
     }
 
-    private void handleForceFlush() {
+    private void handleForceFlush(@FlushReason int reason) {
         if (mEvents == null) return;
 
         if (mDirectServiceInterface == null) {
-            if (DEBUG) Log.d(TAG, "handleForceFlush(): hold your horses, client not ready yet!");
+            if (VERBOSE) {
+                Log.v(TAG, "handleForceFlush(" + getDebugState()
+                        + ", reason=" + getflushReasonAsString(reason)
+                        + "): hold your horses, client not ready: " + mEvents);
+            }
             if (!mHandler.hasMessages(MSG_FLUSH)) {
-                handleScheduleFlush(/* checkExisting= */ false);
+                handleScheduleFlush(reason, /* checkExisting= */ false);
             }
             return;
         }
 
         final int numberEvents = mEvents.size();
+        final String reasonString = getflushReasonAsString(reason);
+        if (DEBUG) {
+            Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState()
+                    + ". Reason: " + reasonString);
+        }
+        // Logs reason, size, max size, idle timeout
+        final String logRecord = "r=" + reasonString + " s=" + numberEvents
+                + " m=" + MAX_BUFFER_SIZE + " i=" + FLUSHING_FREQUENCY_MS;
         try {
-            if (DEBUG) {
-                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
-            }
+            mFlushHistory.log(logRecord);
             mHandler.removeMessages(MSG_FLUSH);
 
             final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
             mDirectServiceInterface.sendEvents(events);
         } catch (RemoteException e) {
-            Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+            Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
                     + ": " + e);
         }
     }
@@ -371,29 +446,29 @@
         if (DEBUG) {
             Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
                     + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
-                    + getActivityDebugName());
+                    + getDebugState());
         }
 
         try {
+            if (mSystemServerInterface == null) return;
+
             mSystemServerInterface.finishSession(mContext.getUserId(), mId);
         } catch (RemoteException e) {
             Log.e(TAG, "Error destroying system-service session " + mId + " for "
-                    + getActivityDebugName() + ": " + e);
+                    + getDebugState() + ": " + e);
         }
     }
 
-    private void handleResetState() {
-        handleResetSession(/* resetState= */ true);
-    }
-
-    // TODO(b/121033016): once we support multiple sessions, we might need to move some of these
+    // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
     // clearings out.
-    private void handleResetSession(boolean resetState) {
-        if (resetState) {
-            mState = STATE_UNKNOWN;
+    private void handleResetSession(int newState) {
+        if (VERBOSE) {
+            Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
+                    + getStateAsString(mState) + " to " + getStateAsString(newState));
         }
-
-        // TODO(b/121033016): must reset children (which currently is owned by superclass)
+        mState = newState;
+        mDisabled.set((newState & STATE_DISABLED) != 0);
+        // TODO(b/122454205): must reset children (which currently is owned by superclass)
         mApplicationToken = null;
         mComponentName = null;
         mEvents = null;
@@ -415,9 +490,8 @@
     }
 
     @Override
-    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags) {
-        notifyViewTextChanged(mId, id, text, flags);
+    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
+        notifyViewTextChanged(mId, id, text);
     }
 
     @Override
@@ -426,7 +500,7 @@
                 && !mDisabled.get();
     }
 
-    // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is
+    // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
     // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
     // change should also get get rid of the "internalNotifyXXXX" methods above
     void notifyChildSessionStarted(@NonNull String parentSessionId,
@@ -435,14 +509,14 @@
                 new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
                         .setParentSessionId(parentSessionId)
                         .setClientContext(clientContext),
-                        /* forceFlush= */ false));
+                        /* forceFlush= */ true));
     }
 
     void notifyChildSessionFinished(@NonNull String parentSessionId,
             @NonNull String childSessionId) {
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
                 new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
-                        .setParentSessionId(parentSessionId), /* forceFlush= */ false));
+                        .setParentSessionId(parentSessionId), /* forceFlush= */ true));
     }
 
     void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
@@ -458,15 +532,14 @@
     }
 
     void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
-            @Nullable CharSequence text, int flags) {
+            @Nullable CharSequence text) {
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
-                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id)
                         .setText(text), /* forceFlush= */ false));
     }
 
     @Override
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        pw.print(prefix); pw.print("id: "); pw.println(mId);
         pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
         pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
         if (mSystemServerInterface != null) {
@@ -479,8 +552,7 @@
         }
         pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
         pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
-        pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
-        pw.print(getStateAsString(mState)); pw.println(")");
+        pw.print(prefix); pw.print("state: "); pw.println(getStateAsString(mState));
         if (mApplicationToken != null) {
             pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
         }
@@ -502,16 +574,25 @@
             }
             pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
             pw.print(prefix); pw.print("next flush: ");
-            TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
+            TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw);
+            pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")");
         }
+        pw.print(prefix); pw.println("flush history:");
+        mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
+
         super.dump(prefix, pw);
     }
 
     /**
      * Gets a string that can be used to identify the activity on logging statements.
      */
-    private String getActivityDebugName() {
-        return mComponentName == null ? mContext.getPackageName()
-                : mComponentName.flattenToShortString();
+    private String getActivityName() {
+        return mComponentName == null
+                ? "pkg:" + mContext.getPackageName()
+                : "act:" + mComponentName.flattenToShortString();
+    }
+
+    private String getDebugState() {
+        return getActivityName() + " (state=" + getStateAsString(mState) + ")";
     }
 }
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index 86b89adb..ddfecb0 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -24,6 +24,7 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
@@ -32,30 +33,188 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
-//TODO(b/111276913): add javadocs / implement Parcelable / implement
-//TODO(b/111276913): for now it's extending ViewNode directly as it needs most of its properties,
+//TODO(b/122484602): add javadocs / implement Parcelable / implement
+//TODO(b/122484602): for now it's extending ViewNode directly as it needs most of its properties,
 // but it might be better to create a common, abstract android.view.ViewNode class that both extend
 // instead
 /** @hide */
 @SystemApi
 public final class ViewNode extends AssistStructure.ViewNode {
 
-    private static final String TAG = "ViewNode";
+    private static final String TAG = ViewNode.class.getSimpleName();
+    private static final boolean VERBOSE = false;
+
+    private static final long FLAGS_HAS_TEXT = 1L << 0;
+    private static final long FLAGS_HAS_COMPLEX_TEXT = 1L << 1;
+    private static final long FLAGS_VISIBILITY_MASK = View.VISIBLE | View.INVISIBLE | View.GONE;
+    private static final long FLAGS_HAS_CLASSNAME = 1L << 4;
+    private static final long FLAGS_HAS_AUTOFILL_ID = 1L << 5;
+    private static final long FLAGS_HAS_AUTOFILL_PARENT_ID = 1L << 6;
+    private static final long FLAGS_HAS_ID = 1L << 7;
+    private static final long FLAGS_HAS_LARGE_COORDS = 1L << 8;
+    private static final long FLAGS_HAS_SCROLL = 1L << 9;
+    private static final long FLAGS_ASSIST_BLOCKED = 1L << 10;
+    private static final long FLAGS_DISABLED = 1L << 11;
+    private static final long FLAGS_CLICKABLE = 1L << 12;
+    private static final long FLAGS_LONG_CLICKABLE = 1L << 13;
+    private static final long FLAGS_CONTEXT_CLICKABLE = 1L << 14;
+    private static final long FLAGS_FOCUSABLE = 1L << 15;
+    private static final long FLAGS_FOCUSED = 1L << 16;
+    private static final long FLAGS_ACCESSIBILITY_FOCUSED = 1L << 17;
+    private static final long FLAGS_CHECKABLE = 1L << 18;
+    private static final long FLAGS_CHECKED = 1L << 19;
+    private static final long FLAGS_SELECTED = 1L << 20;
+    private static final long FLAGS_ACTIVATED = 1L << 21;
+    private static final long FLAGS_OPAQUE = 1L << 22;
+    private static final long FLAGS_HAS_MATRIX = 1L << 23;
+    private static final long FLAGS_HAS_ELEVATION = 1L << 24;
+    private static final long FLAGS_HAS_ALPHA = 1L << 25;
+    private static final long FLAGS_HAS_CONTENT_DESCRIPTION = 1L << 26;
+    private static final long FLAGS_HAS_EXTRAS = 1L << 27;
+    private static final long FLAGS_HAS_LOCALE_LIST = 1L << 28;
+    private static final long FLAGS_HAS_INPUT_TYPE = 1L << 29;
+    private static final long FLAGS_HAS_MIN_TEXT_EMS = 1L << 30;
+    private static final long FLAGS_HAS_MAX_TEXT_EMS = 1L << 31;
+    private static final long FLAGS_HAS_MAX_TEXT_LENGTH = 1L << 32;
+    private static final long FLAGS_HAS_TEXT_ID_ENTRY = 1L << 33;
+    private static final long FLAGS_HAS_AUTOFILL_TYPE = 1L << 34;
+    private static final long FLAGS_HAS_AUTOFILL_VALUE = 1L << 35;
+    private static final long FLAGS_HAS_AUTOFILL_HINTS = 1L << 36;
+    private static final long FLAGS_HAS_AUTOFILL_OPTIONS = 1L << 37;
+
+    /** Flags used to optimize what's written to the parcel */
+    private long mFlags;
 
     private AutofillId mParentAutofillId;
 
-    // TODO(b/111276913): temporarily setting some fields here while they're not accessible from the
-    // superclass
     private AutofillId mAutofillId;
-    private CharSequence mText;
+    private ViewNodeText mText;
     private String mClassName;
+    private int mId = View.NO_ID;
+    private String mIdPackage;
+    private String mIdType;
+    private String mIdEntry;
+    private int mX;
+    private int mY;
+    private int mScrollX;
+    private int mScrollY;
+    private int mWidth;
+    private int mHeight;
+    private Matrix mMatrix;
+    private float mElevation;
+    private float mAlpha = 1.0f;
+    private CharSequence mContentDescription;
+    private Bundle mExtras;
+    private LocaleList mLocaleList;
+    private int mInputType;
+    private int mMinEms = -1;
+    private int mMaxEms = -1;
+    private int mMaxLength = -1;
+    private String mTextIdEntry;
+    private @View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE;
+    private String[] mAutofillHints;
+    private AutofillValue mAutofillValue;
+    private CharSequence[] mAutofillOptions;
 
     /** @hide */
     public ViewNode() {
     }
 
+    private ViewNode(long nodeFlags, @NonNull Parcel parcel) {
+        mFlags = nodeFlags;
+
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_ID) != 0) {
+            mAutofillId = parcel.readParcelable(null);
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_PARENT_ID) != 0) {
+            mParentAutofillId = parcel.readParcelable(null);
+        }
+        if ((nodeFlags & FLAGS_HAS_TEXT) != 0) {
+            mText = new ViewNodeText(parcel, (nodeFlags & FLAGS_HAS_COMPLEX_TEXT) == 0);
+        }
+        if ((nodeFlags & FLAGS_HAS_CLASSNAME) != 0) {
+            mClassName = parcel.readString();
+        }
+        if ((nodeFlags & FLAGS_HAS_ID) != 0) {
+            mId = parcel.readInt();
+            if (mId != View.NO_ID) {
+                mIdEntry = parcel.readString();
+                if (mIdEntry != null) {
+                    mIdType = parcel.readString();
+                    mIdPackage = parcel.readString();
+                }
+            }
+        }
+        if ((nodeFlags & FLAGS_HAS_LARGE_COORDS) != 0) {
+            mX = parcel.readInt();
+            mY = parcel.readInt();
+            mWidth = parcel.readInt();
+            mHeight = parcel.readInt();
+        } else {
+            int val = parcel.readInt();
+            mX = val & 0x7fff;
+            mY = (val >> 16) & 0x7fff;
+            val = parcel.readInt();
+            mWidth = val & 0x7fff;
+            mHeight = (val >> 16) & 0x7fff;
+        }
+        if ((nodeFlags & FLAGS_HAS_SCROLL) != 0) {
+            mScrollX = parcel.readInt();
+            mScrollY = parcel.readInt();
+        }
+        if ((nodeFlags & FLAGS_HAS_MATRIX) != 0) {
+            mMatrix = new Matrix();
+            final float[] tmpMatrix = new float[9];
+            parcel.readFloatArray(tmpMatrix);
+            mMatrix.setValues(tmpMatrix);
+        }
+        if ((nodeFlags & FLAGS_HAS_ELEVATION) != 0) {
+            mElevation = parcel.readFloat();
+        }
+        if ((nodeFlags & FLAGS_HAS_ALPHA) != 0) {
+            mAlpha = parcel.readFloat();
+        }
+        if ((nodeFlags & FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
+            mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        }
+        if ((nodeFlags & FLAGS_HAS_EXTRAS) != 0) {
+            mExtras = parcel.readBundle();
+        }
+        if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) {
+            mLocaleList = parcel.readParcelable(null);
+        }
+        if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) {
+            mInputType = parcel.readInt();
+        }
+        if ((nodeFlags & FLAGS_HAS_MIN_TEXT_EMS) != 0) {
+            mMinEms = parcel.readInt();
+        }
+        if ((nodeFlags & FLAGS_HAS_MAX_TEXT_EMS) != 0) {
+            mMaxEms = parcel.readInt();
+        }
+        if ((nodeFlags & FLAGS_HAS_MAX_TEXT_LENGTH) != 0) {
+            mMaxLength = parcel.readInt();
+        }
+        if ((nodeFlags & FLAGS_HAS_TEXT_ID_ENTRY) != 0) {
+            mTextIdEntry = parcel.readString();
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_TYPE) != 0) {
+            mAutofillType = parcel.readInt();
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_HINTS) != 0) {
+            mAutofillHints = parcel.readStringArray();
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_VALUE) != 0) {
+            mAutofillValue = parcel.readParcelable(null);
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_OPTIONS) != 0) {
+            mAutofillOptions = parcel.readCharSequenceArray();
+        }
+    }
+
     /**
      * Returns the {@link AutofillId} of this view's parent, if the parent is also part of the
      * screen observation tree.
@@ -65,53 +224,446 @@
         return mParentAutofillId;
     }
 
-    // TODO(b/111276913): temporarily overwriting some methods
     @Override
     public AutofillId getAutofillId() {
         return mAutofillId;
     }
+
     @Override
     public CharSequence getText() {
-        return mText;
+        return mText != null ? mText.mText : null;
     }
+
     @Override
     public String getClassName() {
         return mClassName;
     }
 
+    @Override
+    public int getId() {
+        return mId;
+    }
+
+    @Override
+    public String getIdPackage() {
+        return mIdPackage;
+    }
+
+    @Override
+    public String getIdType() {
+        return mIdType;
+    }
+
+    @Override
+    public String getIdEntry() {
+        return mIdEntry;
+    }
+
+    @Override
+    public int getLeft() {
+        return mX;
+    }
+
+    @Override
+    public int getTop() {
+        return mY;
+    }
+
+    @Override
+    public int getScrollX() {
+        return mScrollX;
+    }
+
+    @Override
+    public int getScrollY() {
+        return mScrollY;
+    }
+
+    @Override
+    public int getWidth() {
+        return mWidth;
+    }
+
+    @Override
+    public int getHeight() {
+        return mHeight;
+    }
+
+    @Override
+    public boolean isAssistBlocked() {
+        return (mFlags & FLAGS_ASSIST_BLOCKED) != 0;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return (mFlags & FLAGS_DISABLED) == 0;
+    }
+
+    @Override
+    public boolean isClickable() {
+        return (mFlags & FLAGS_CLICKABLE) != 0;
+    }
+
+    @Override
+    public boolean isLongClickable() {
+        return (mFlags & FLAGS_LONG_CLICKABLE) != 0;
+    }
+
+    @Override
+    public boolean isContextClickable() {
+        return (mFlags & FLAGS_CONTEXT_CLICKABLE) != 0;
+    }
+
+    @Override
+    public boolean isFocusable() {
+        return (mFlags & FLAGS_FOCUSABLE) != 0;
+    }
+
+    @Override
+    public boolean isFocused() {
+        return (mFlags & FLAGS_FOCUSED) != 0;
+    }
+
+    @Override
+    public boolean isAccessibilityFocused() {
+        return (mFlags & FLAGS_ACCESSIBILITY_FOCUSED) != 0;
+    }
+
+    @Override
+    public boolean isCheckable() {
+        return (mFlags & FLAGS_CHECKABLE) != 0;
+    }
+
+    @Override
+    public boolean isChecked() {
+        return (mFlags & FLAGS_CHECKED) != 0;
+    }
+
+    @Override
+    public boolean isSelected() {
+        return (mFlags & FLAGS_SELECTED) != 0;
+    }
+
+    @Override
+    public boolean isActivated() {
+        return (mFlags & FLAGS_ACTIVATED) != 0;
+    }
+
+    @Override
+    public boolean isOpaque() {
+        return (mFlags & FLAGS_OPAQUE) != 0;
+    }
+
+    @Override
+    public Matrix getTransformation() {
+        return mMatrix;
+    }
+
+    @Override
+    public float getElevation() {
+        return mElevation;
+    }
+
+    @Override
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    @Override
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    @Override
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public String getHint() {
+        return mText != null ? mText.mHint : null;
+    }
+
+    @Override
+    public int getTextSelectionStart() {
+        return mText != null ? mText.mTextSelectionStart : -1;
+    }
+
+    @Override
+    public int getTextSelectionEnd() {
+        return mText != null ? mText.mTextSelectionEnd : -1;
+    }
+
+    @Override
+    public int getTextColor() {
+        return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
+    }
+
+    @Override
+    public int getTextBackgroundColor() {
+        return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
+    }
+
+    @Override
+    public float getTextSize() {
+        return mText != null ? mText.mTextSize : 0;
+    }
+
+    @Override
+    public int getTextStyle() {
+        return mText != null ? mText.mTextStyle : 0;
+    }
+
+    @Override
+    public int[] getTextLineCharOffsets() {
+        return mText != null ? mText.mLineCharOffsets : null;
+    }
+
+    @Override
+    public int[] getTextLineBaselines() {
+        return mText != null ? mText.mLineBaselines : null;
+    }
+
+    @Override
+    public int getVisibility() {
+        return (int) (mFlags & FLAGS_VISIBILITY_MASK);
+    }
+
+    @Override
+    public int getInputType() {
+        return mInputType;
+    }
+
+    @Override
+    public int getMinTextEms() {
+        return mMinEms;
+    }
+
+    @Override
+    public int getMaxTextEms() {
+        return mMaxEms;
+    }
+
+    @Override
+    public int getMaxTextLength() {
+        return mMaxLength;
+    }
+
+    @Override
+    public String getTextIdEntry() {
+        return mTextIdEntry;
+    }
+
+    @Override
+    public @View.AutofillType int getAutofillType() {
+        return mAutofillType;
+    }
+
+    @Override
+    @Nullable public String[] getAutofillHints() {
+        return mAutofillHints;
+    }
+
+    @Override
+    @Nullable public AutofillValue getAutofillValue() {
+        return mAutofillValue;
+    }
+
+    @Override
+    @Nullable public CharSequence[] getAutofillOptions() {
+        return mAutofillOptions;
+    }
+
+    @Override
+    public LocaleList getLocaleList() {
+        return mLocaleList;
+    }
+
+    private void writeSelfToParcel(@NonNull Parcel parcel, int parcelFlags) {
+        long nodeFlags = mFlags;
+
+        if (mAutofillId != null) {
+            nodeFlags |= FLAGS_HAS_AUTOFILL_ID;
+        }
+
+        if (mParentAutofillId != null) {
+            nodeFlags |= FLAGS_HAS_AUTOFILL_PARENT_ID;
+        }
+
+        if (mText != null) {
+            nodeFlags |= FLAGS_HAS_TEXT;
+            if (!mText.isSimple()) {
+                nodeFlags |= FLAGS_HAS_COMPLEX_TEXT;
+            }
+        }
+        if (mClassName != null) {
+            nodeFlags |= FLAGS_HAS_CLASSNAME;
+        }
+        if (mId != View.NO_ID) {
+            nodeFlags |= FLAGS_HAS_ID;
+        }
+        if ((mX & ~0x7fff) != 0 || (mY & ~0x7fff) != 0
+                || (mWidth & ~0x7fff) != 0 | (mHeight & ~0x7fff) != 0) {
+            nodeFlags |= FLAGS_HAS_LARGE_COORDS;
+        }
+        if (mScrollX != 0 || mScrollY != 0) {
+            nodeFlags |= FLAGS_HAS_SCROLL;
+        }
+        if (mMatrix != null) {
+            nodeFlags |= FLAGS_HAS_MATRIX;
+        }
+        if (mElevation != 0) {
+            nodeFlags |= FLAGS_HAS_ELEVATION;
+        }
+        if (mAlpha != 1.0f) {
+            nodeFlags |= FLAGS_HAS_ALPHA;
+        }
+        if (mContentDescription != null) {
+            nodeFlags |= FLAGS_HAS_CONTENT_DESCRIPTION;
+        }
+        if (mExtras != null) {
+            nodeFlags |= FLAGS_HAS_EXTRAS;
+        }
+        if (mLocaleList != null) {
+            nodeFlags |= FLAGS_HAS_LOCALE_LIST;
+        }
+        if (mInputType != 0) {
+            nodeFlags |= FLAGS_HAS_INPUT_TYPE;
+        }
+        if (mMinEms > -1) {
+            nodeFlags |= FLAGS_HAS_MIN_TEXT_EMS;
+        }
+        if (mMaxEms > -1) {
+            nodeFlags |= FLAGS_HAS_MAX_TEXT_EMS;
+        }
+        if (mMaxLength > -1) {
+            nodeFlags |= FLAGS_HAS_MAX_TEXT_LENGTH;
+        }
+        if (mTextIdEntry != null) {
+            nodeFlags |= FLAGS_HAS_TEXT_ID_ENTRY;
+        }
+        if (mAutofillValue != null) {
+            nodeFlags |= FLAGS_HAS_AUTOFILL_VALUE;
+        }
+        if (mAutofillType != View.AUTOFILL_TYPE_NONE) {
+            nodeFlags |= FLAGS_HAS_AUTOFILL_TYPE;
+        }
+        if (mAutofillHints != null) {
+            nodeFlags |= FLAGS_HAS_AUTOFILL_HINTS;
+        }
+        if (mAutofillOptions != null) {
+            nodeFlags |= FLAGS_HAS_AUTOFILL_OPTIONS;
+        }
+        parcel.writeLong(nodeFlags);
+
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_ID) != 0) {
+            parcel.writeParcelable(mAutofillId, parcelFlags);
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_PARENT_ID) != 0) {
+            parcel.writeParcelable(mParentAutofillId, parcelFlags);
+        }
+        if ((nodeFlags & FLAGS_HAS_TEXT) != 0) {
+            mText.writeToParcel(parcel, (nodeFlags & FLAGS_HAS_COMPLEX_TEXT) == 0);
+        }
+        if ((nodeFlags & FLAGS_HAS_CLASSNAME) != 0) {
+            parcel.writeString(mClassName);
+        }
+        if ((nodeFlags & FLAGS_HAS_ID) != 0) {
+            parcel.writeInt(mId);
+            if (mId != View.NO_ID) {
+                parcel.writeString(mIdEntry);
+                if (mIdEntry != null) {
+                    parcel.writeString(mIdType);
+                    parcel.writeString(mIdPackage);
+                }
+            }
+        }
+        if ((nodeFlags & FLAGS_HAS_LARGE_COORDS) != 0) {
+            parcel.writeInt(mX);
+            parcel.writeInt(mY);
+            parcel.writeInt(mWidth);
+            parcel.writeInt(mHeight);
+        } else {
+            parcel.writeInt((mY << 16) | mX);
+            parcel.writeInt((mHeight << 16) | mWidth);
+        }
+        if ((nodeFlags & FLAGS_HAS_SCROLL) != 0) {
+            parcel.writeInt(mScrollX);
+            parcel.writeInt(mScrollY);
+        }
+        if ((nodeFlags & FLAGS_HAS_MATRIX) != 0) {
+            //TODO(b/122484602): use a singleton tmpMatrix (if logic is not moved to superclass)
+            final float[] tmpMatrix = new float[9];
+            mMatrix.getValues(tmpMatrix);
+            parcel.writeFloatArray(tmpMatrix);
+        }
+        if ((nodeFlags & FLAGS_HAS_ELEVATION) != 0) {
+            parcel.writeFloat(mElevation);
+        }
+        if ((nodeFlags & FLAGS_HAS_ALPHA) != 0) {
+            parcel.writeFloat(mAlpha);
+        }
+        if ((nodeFlags & FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
+            TextUtils.writeToParcel(mContentDescription, parcel, 0);
+        }
+        if ((nodeFlags & FLAGS_HAS_EXTRAS) != 0) {
+            parcel.writeBundle(mExtras);
+        }
+        if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) {
+            parcel.writeParcelable(mLocaleList, 0);
+        }
+        if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) {
+            parcel.writeInt(mInputType);
+        }
+        if ((nodeFlags & FLAGS_HAS_MIN_TEXT_EMS) != 0) {
+            parcel.writeInt(mMinEms);
+        }
+        if ((nodeFlags & FLAGS_HAS_MAX_TEXT_EMS) != 0) {
+            parcel.writeInt(mMaxEms);
+        }
+        if ((nodeFlags & FLAGS_HAS_MAX_TEXT_LENGTH) != 0) {
+            parcel.writeInt(mMaxLength);
+        }
+        if ((nodeFlags & FLAGS_HAS_TEXT_ID_ENTRY) != 0) {
+            parcel.writeString(mTextIdEntry);
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_TYPE) != 0) {
+            parcel.writeInt(mAutofillType);
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_HINTS) != 0) {
+            parcel.writeStringArray(mAutofillHints);
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_VALUE) != 0) {
+            parcel.writeParcelable(mAutofillValue, 0);
+        }
+        if ((nodeFlags & FLAGS_HAS_AUTOFILL_OPTIONS) != 0) {
+            parcel.writeCharSequenceArray(mAutofillOptions);
+        }
+    }
+
     /** @hide */
     public static void writeToParcel(@NonNull Parcel parcel, @Nullable ViewNode node, int flags) {
         if (node == null) {
-            parcel.writeParcelable(null, flags);
-            return;
+            parcel.writeLong(0);
+        } else {
+            node.writeSelfToParcel(parcel, flags);
         }
-        parcel.writeParcelable(node.mAutofillId, flags);
-        parcel.writeParcelable(node.mParentAutofillId, flags);
-        parcel.writeCharSequence(node.mText);
-        parcel.writeString(node.mClassName);
     }
 
     /** @hide */
     public static @Nullable ViewNode readFromParcel(@NonNull Parcel parcel) {
-        final AutofillId id = parcel.readParcelable(null);
-        if (id == null) return null;
-
-        final ViewNode node = new ViewNode();
-
-        node.mAutofillId = id;
-        node.mParentAutofillId = parcel.readParcelable(null);
-        node.mText = parcel.readCharSequence();
-        node.mClassName = parcel.readString();
-
-        return node;
+        final long nodeFlags = parcel.readLong();
+        return nodeFlags == 0 ? new ViewNode() : new ViewNode(nodeFlags, parcel);
     }
 
     /** @hide */
-    static final class ViewStructureImpl extends ViewStructure {
+    @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
+    public static final class ViewStructureImpl extends ViewStructure {
 
         final ViewNode mNode = new ViewNode();
 
-        ViewStructureImpl(@NonNull View view) {
+        @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
+        public ViewStructureImpl(@NonNull View view) {
             mNode.mAutofillId = Preconditions.checkNotNull(view).getAutofillId();
             final ViewParent parent = view.getParent();
             if (parent instanceof View) {
@@ -119,179 +671,212 @@
             }
         }
 
-        ViewStructureImpl(@NonNull AutofillId parentId, int virtualId) {
+        @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
+        public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId, int sessionId) {
             mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
-            mNode.mAutofillId = new AutofillId(parentId, virtualId);
+            mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
+        }
+
+        @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
+        public ViewNode getNode() {
+            return mNode;
         }
 
         @Override
         public void setId(int id, String packageName, String typeName, String entryName) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mId = id;
+            mNode.mIdPackage = packageName;
+            mNode.mIdType = typeName;
+            mNode.mIdEntry = entryName;
         }
 
         @Override
         public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mX = left;
+            mNode.mY = top;
+            mNode.mScrollX = scrollX;
+            mNode.mScrollY = scrollY;
+            mNode.mWidth = width;
+            mNode.mHeight = height;
         }
 
         @Override
         public void setTransformation(Matrix matrix) {
-            // TODO(b/111276913): implement or move to superclass
+            if (matrix == null) {
+                mNode.mMatrix = null;
+            } else {
+                mNode.mMatrix = new Matrix(matrix);
+            }
         }
 
         @Override
         public void setElevation(float elevation) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mElevation = elevation;
         }
 
         @Override
         public void setAlpha(float alpha) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mAlpha = alpha;
         }
 
         @Override
         public void setVisibility(int visibility) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_VISIBILITY_MASK)
+                    | (visibility & FLAGS_VISIBILITY_MASK);
         }
 
         @Override
         public void setAssistBlocked(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_ASSIST_BLOCKED)
+                    | (state ? FLAGS_ASSIST_BLOCKED : 0);
         }
 
         @Override
         public void setEnabled(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_DISABLED) | (state ? 0 : FLAGS_DISABLED);
         }
 
         @Override
         public void setClickable(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_CLICKABLE) | (state ? FLAGS_CLICKABLE : 0);
         }
 
         @Override
         public void setLongClickable(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_LONG_CLICKABLE)
+                    | (state ? FLAGS_LONG_CLICKABLE : 0);
         }
 
         @Override
         public void setContextClickable(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_CONTEXT_CLICKABLE)
+                    | (state ? FLAGS_CONTEXT_CLICKABLE : 0);
         }
 
         @Override
         public void setFocusable(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_FOCUSABLE) | (state ? FLAGS_FOCUSABLE : 0);
         }
 
         @Override
         public void setFocused(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_FOCUSED) | (state ? FLAGS_FOCUSED : 0);
         }
 
         @Override
         public void setAccessibilityFocused(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_ACCESSIBILITY_FOCUSED)
+                    | (state ? FLAGS_ACCESSIBILITY_FOCUSED : 0);
         }
 
         @Override
         public void setCheckable(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_CHECKABLE) | (state ? FLAGS_CHECKABLE : 0);
         }
 
         @Override
         public void setChecked(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_CHECKED) | (state ? FLAGS_CHECKED : 0);
         }
 
         @Override
         public void setSelected(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_SELECTED) | (state ? FLAGS_SELECTED : 0);
         }
 
         @Override
         public void setActivated(boolean state) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_ACTIVATED) | (state ? FLAGS_ACTIVATED : 0);
         }
 
         @Override
         public void setOpaque(boolean opaque) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mFlags = (mNode.mFlags & ~FLAGS_OPAQUE) | (opaque ? FLAGS_OPAQUE : 0);
         }
 
         @Override
         public void setClassName(String className) {
-            // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
             mNode.mClassName = className;
         }
 
         @Override
         public void setContentDescription(CharSequence contentDescription) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mContentDescription = contentDescription;
         }
 
         @Override
         public void setText(CharSequence text) {
-            // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
-            mNode.mText = text;
+            final ViewNodeText t = getNodeText();
+            t.mText = TextUtils.trimNoCopySpans(text);
+            t.mTextSelectionStart = t.mTextSelectionEnd = -1;
         }
 
         @Override
         public void setText(CharSequence text, int selectionStart, int selectionEnd) {
-            // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
-            mNode.mText = text;
-            // TODO(b/111276913): implement or move to superclass
+            final ViewNodeText t = getNodeText();
+            t.mText = TextUtils.trimNoCopySpans(text);
+            t.mTextSelectionStart = selectionStart;
+            t.mTextSelectionEnd = selectionEnd;
         }
 
         @Override
         public void setTextStyle(float size, int fgColor, int bgColor, int style) {
-            // TODO(b/111276913): implement or move to superclass
+            final ViewNodeText t = getNodeText();
+            t.mTextColor = fgColor;
+            t.mTextBackgroundColor = bgColor;
+            t.mTextSize = size;
+            t.mTextStyle = style;
         }
 
         @Override
         public void setTextLines(int[] charOffsets, int[] baselines) {
-            // TODO(b/111276913): implement or move to superclass
+            final ViewNodeText t = getNodeText();
+            t.mLineCharOffsets = charOffsets;
+            t.mLineBaselines = baselines;
+        }
+
+        @Override
+        public void setTextIdEntry(String entryName) {
+            mNode.mTextIdEntry = Preconditions.checkNotNull(entryName);
         }
 
         @Override
         public void setHint(CharSequence hint) {
-            // TODO(b/111276913): implement or move to superclass
+            getNodeText().mHint = hint != null ? hint.toString() : null;
         }
 
         @Override
         public CharSequence getText() {
-            // TODO(b/111276913): temporarily getting directly; should be done on superclass instead
-            return mNode.mText;
+            return mNode.getText();
         }
 
         @Override
         public int getTextSelectionStart() {
-            // TODO(b/111276913): implement or move to superclass
-            return 0;
+            return mNode.getTextSelectionStart();
         }
 
         @Override
         public int getTextSelectionEnd() {
-            // TODO(b/111276913): implement or move to superclass
-            return 0;
+            return mNode.getTextSelectionEnd();
         }
 
         @Override
         public CharSequence getHint() {
-            // TODO(b/111276913): implement or move to superclass
-            return null;
+            return mNode.getHint();
         }
 
         @Override
         public Bundle getExtras() {
-            // TODO(b/111276913): implement or move to superclass
-            return null;
+            if (mNode.mExtras != null) {
+                return mNode.mExtras;
+            }
+            mNode.mExtras = new Bundle();
+            return mNode.mExtras;
         }
 
         @Override
         public boolean hasExtras() {
-            // TODO(b/111276913): implement or move to superclass
-            return false;
+            return mNode.mExtras != null;
         }
 
         @Override
@@ -325,50 +910,64 @@
 
         @Override
         public AutofillId getAutofillId() {
-            // TODO(b/111276913): temporarily getting directly; should be done on superclass instead
             return mNode.mAutofillId;
         }
 
         @Override
         public void setAutofillId(AutofillId id) {
-            // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
-            mNode.mAutofillId = id;
+            mNode.mAutofillId = Preconditions.checkNotNull(id);
         }
 
+
         @Override
         public void setAutofillId(AutofillId parentId, int virtualId) {
-            // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+            mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
             mNode.mAutofillId = new AutofillId(parentId, virtualId);
         }
 
         @Override
-        public void setAutofillType(int type) {
-            // TODO(b/111276913): implement or move to superclass
+        public void setAutofillType(@View.AutofillType int type) {
+            mNode.mAutofillType = type;
         }
 
         @Override
-        public void setAutofillHints(String[] hint) {
-            // TODO(b/111276913): implement or move to superclass
+        public void setAutofillHints(String[] hints) {
+            mNode.mAutofillHints = hints;
         }
 
         @Override
         public void setAutofillValue(AutofillValue value) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mAutofillValue = value;
         }
 
         @Override
         public void setAutofillOptions(CharSequence[] options) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mAutofillOptions = options;
         }
 
         @Override
         public void setInputType(int inputType) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mInputType = inputType;
+        }
+
+        @Override
+        public void setMinTextEms(int minEms) {
+            mNode.mMinEms = minEms;
+        }
+
+        @Override
+        public void setMaxTextEms(int maxEms) {
+            mNode.mMaxEms = maxEms;
+        }
+
+        @Override
+        public void setMaxTextLength(int maxLength) {
+            mNode.mMaxLength = maxLength;
         }
 
         @Override
         public void setDataIsSensitive(boolean sensitive) {
-            // TODO(b/111276913): implement or move to superclass
+            Log.w(TAG, "setDataIsSensitive() is not supported");
         }
 
         @Override
@@ -378,7 +977,7 @@
 
         @Override
         public Rect getTempRect() {
-            // TODO(b/111276913): implement or move to superclass
+            Log.w(TAG, "getTempRect() is not supported");
             return null;
         }
 
@@ -389,7 +988,7 @@
 
         @Override
         public void setLocaleList(LocaleList localeList) {
-            // TODO(b/111276913): implement or move to superclass
+            mNode.mLocaleList = localeList;
         }
 
         @Override
@@ -402,5 +1001,66 @@
         public void setHtmlInfo(HtmlInfo htmlInfo) {
             Log.w(TAG, "setHtmlInfo() is not supported");
         }
+
+        private ViewNodeText getNodeText() {
+            if (mNode.mText != null) {
+                return mNode.mText;
+            }
+            mNode.mText = new ViewNodeText();
+            return mNode.mText;
+        }
+    }
+
+    //TODO(b/122484602): copied 'as-is' from AssistStructure, except for writeSensitive
+    static final class ViewNodeText {
+        CharSequence mText;
+        float mTextSize;
+        int mTextStyle;
+        int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED;
+        int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED;
+        int mTextSelectionStart;
+        int mTextSelectionEnd;
+        int[] mLineCharOffsets;
+        int[] mLineBaselines;
+        String mHint;
+
+        ViewNodeText() {
+        }
+
+        boolean isSimple() {
+            return mTextBackgroundColor == ViewNode.TEXT_COLOR_UNDEFINED
+                    && mTextSelectionStart == 0 && mTextSelectionEnd == 0
+                    && mLineCharOffsets == null && mLineBaselines == null && mHint == null;
+        }
+
+        ViewNodeText(Parcel in, boolean simple) {
+            mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            mTextSize = in.readFloat();
+            mTextStyle = in.readInt();
+            mTextColor = in.readInt();
+            if (!simple) {
+                mTextBackgroundColor = in.readInt();
+                mTextSelectionStart = in.readInt();
+                mTextSelectionEnd = in.readInt();
+                mLineCharOffsets = in.createIntArray();
+                mLineBaselines = in.createIntArray();
+                mHint = in.readString();
+            }
+        }
+
+        void writeToParcel(Parcel out, boolean simple) {
+            TextUtils.writeToParcel(mText, out, 0);
+            out.writeFloat(mTextSize);
+            out.writeInt(mTextStyle);
+            out.writeInt(mTextColor);
+            if (!simple) {
+                out.writeInt(mTextBackgroundColor);
+                out.writeInt(mTextSelectionStart);
+                out.writeInt(mTextSelectionEnd);
+                out.writeIntArray(mLineCharOffsets);
+                out.writeIntArray(mLineBaselines);
+                out.writeString(mHint);
+            }
+        }
     }
 }
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 28d9fcf..f99afe6 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -16,11 +16,15 @@
 
 package android.view.inputmethod;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.Printer;
@@ -472,6 +476,26 @@
     public String[] contentMimeTypes = null;
 
     /**
+     * If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
+     * matter what user ID the calling process has.
+     *
+     * <p>Note: This field is silently ignored when:</p>
+     * <ul>
+     *     <li>{@link android.view.inputmethod.InputMethodSystemProperty#PER_PROFILE_IME_ENABLED} is
+     *     {@code false}.</li>
+     *     <li>{@link android.view.inputmethod.InputMethodSystemProperty#MULTI_CLIENT_IME_ENABLED}
+     *     is {@code true}.</li>
+     * </ul>
+     *
+     * <p>Note also that pseudo handles such as {@link UserHandle#ALL} are not supported.</p>
+     *
+     * @hide
+     */
+    @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
+    @Nullable
+    public UserHandle targetInputMethodUser = null;
+
+    /**
      * Ensure that the data in this EditorInfo is compatible with an application
      * that was developed against the given target API version.  This can
      * impact the following input types:
@@ -527,6 +551,9 @@
         pw.println(prefix + "extras=" + extras);
         pw.println(prefix + "hintLocales=" + hintLocales);
         pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
+        if (targetInputMethodUser != null) {
+            pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier());
+        }
     }
 
     /**
@@ -556,6 +583,7 @@
             LocaleList.getEmptyLocaleList().writeToParcel(dest, flags);
         }
         dest.writeStringArray(contentMimeTypes);
+        UserHandle.writeToParcel(targetInputMethodUser, dest);
     }
 
     /**
@@ -582,6 +610,7 @@
                     LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source);
                     res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
                     res.contentMimeTypes = source.readStringArray();
+                    res.targetInputMethodUser = UserHandle.readFromParcel(source);
                     return res;
                 }
 
diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
index b233b75..57ed7f9 100644
--- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java
+++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
@@ -41,6 +41,17 @@
      */
     private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
 
+    /**
+     * System property key for debugging purpose. The value must be empty, "1", or "0".
+     *
+     * <p>Values 'y', 'yes', '1', 'true' or 'on' are considered true.</p>
+     *
+     * <p>To set, run "adb root && adb shell setprop persist.debug.per_profile_ime 1".</p>
+     *
+     * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}.</p>
+     */
+    private static final String PROP_DEBUG_PER_PROFILE_IME = "persist.debug.per_profile_ime";
+
     @Nullable
     private static ComponentName getMultiClientImeComponentName() {
         if (Build.IS_DEBUGGABLE) {
@@ -59,6 +70,9 @@
     /**
      * {@link ComponentName} of multi-client IME to be used.
      *
+     * <p>TODO: Move this back to MultiClientInputMethodManagerService once
+     * {@link #PER_PROFILE_IME_ENABLED} always becomes {@code true}.</p>
+     *
      * @hide
      */
     @Nullable
@@ -68,7 +82,19 @@
     /**
      * {@code true} when multi-client IME is enabled.
      *
+     * <p>TODO: Move this back to MultiClientInputMethodManagerService once
+     * {@link #PER_PROFILE_IME_ENABLED} always becomes {@code true}.</p>
+     *
      * @hide
      */
     public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
+
+    /**
+     * {@code true} when per-profile IME is enabled.
+     * @hide
+     */
+    public static final boolean PER_PROFILE_IME_ENABLED = MULTI_CLIENT_IME_ENABLED
+            || Build.IS_DEBUGGABLE && SystemProperties.getBoolean(
+                    PROP_DEBUG_PER_PROFILE_IME, false);
+
 }
diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java
index ea94ad4..7b9a507 100644
--- a/core/java/android/view/inspector/InspectableNodeName.java
+++ b/core/java/android/view/inspector/InspectableNodeName.java
@@ -19,6 +19,8 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.TestApi;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
@@ -39,6 +41,7 @@
  */
 @Target({TYPE})
 @Retention(SOURCE)
+@TestApi
 public @interface InspectableNodeName {
     /**
      * The display name for nodes of this type.
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index 5b957156..355ff1d 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -20,7 +20,8 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.content.res.ResourceId;
+import android.annotation.TestApi;
+import android.content.res.Resources;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
@@ -40,6 +41,7 @@
  */
 @Target({METHOD})
 @Retention(SOURCE)
+@TestApi
 public @interface InspectableProperty {
     /**
      * The name of the property.
@@ -57,16 +59,16 @@
      * If left as {ID_NULL}, and {@link #hasAttributeId()} is true, the attribute ID will be
      * inferred from {@link #name()}.
      *
-     * @return The attribute ID of the property or {@link ResourceId#ID_NULL}
+     * @return The attribute ID of the property or {@link Resources#ID_NULL}
      */
-    int attributeId() default ResourceId.ID_NULL;
+    int attributeId() default Resources.ID_NULL;
 
     /**
      * If this property has an attribute ID.
      *
      * Set to false if the annotated property does not have an attribute ID, that is, it is not
      * inflated from an XML attribute. This will prevent the automatic inference of the attribute
-     * ID if {@link #attributeId()} is set to {@link ResourceId#ID_NULL}.
+     * ID if {@link #attributeId()} is set to {@link Resources#ID_NULL}.
      *
      * @return Whether to infer an attribute ID if not supplied
      */
@@ -86,7 +88,6 @@
      *
      * @return An array of {@link EnumMap}, empty if not applicable
      * @see android.annotation.IntDef
-     * @see IntEnumMapping
      */
     EnumMap[] enumMapping() default {};
 
@@ -105,11 +106,11 @@
     /**
      * One entry in an enumeration packed into a primitive {int}.
      *
-     * @see IntEnumMapping
      * @hide
      */
     @Target({TYPE})
     @Retention(SOURCE)
+    @TestApi
     @interface EnumMap {
         /**
          * The string name of this enumeration value.
@@ -134,6 +135,7 @@
      */
     @Target({TYPE})
     @Retention(SOURCE)
+    @TestApi
     @interface FlagMap {
         /**
          * The string name of this flag.
@@ -168,15 +170,22 @@
      *
      * @hide
      */
+    @TestApi
     enum ValueType {
         /**
          * No special handling, property is considered to be a numeric value.
+         *
+         * @hide
          */
+        @TestApi
         NONE,
 
         /**
          * The default the annotation processor infers the value type from context.
+         *
+         * @hide
          */
+        @TestApi
         INFERRED,
 
         /**
@@ -185,7 +194,9 @@
          * This is inferred if {@link #enumMapping()} is specified.
          *
          * @see EnumMap
+         * @hide
          */
+        @TestApi
         INT_ENUM,
 
         /**
@@ -194,7 +205,9 @@
          * This is inferred if {@link #flagMapping()} is specified.
          *
          * @see FlagMap
+         * @hide
          */
+        @TestApi
         INT_FLAG,
 
         /**
@@ -204,7 +217,9 @@
          * {@link android.annotation.ColorLong} on the getter method.
          *
          * @see android.graphics.Color
+         * @hide
          */
+        @TestApi
         COLOR,
 
         /**
@@ -213,7 +228,9 @@
          * This type is not inferred, and is non-trivial to represent using {@link FlagMap}.
          *
          * @see android.view.Gravity
+         * @hide
          */
+        @TestApi
         GRAVITY
     }
 }
diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java
deleted file mode 100644
index 69f6dce..0000000
--- a/core/java/android/view/inspector/IntEnumMapping.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.view.inspector;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.ArrayList;
-
-/**
- * Maps the values of an {int} property to string names for properties that encode enumerations.
- *
- * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
- * for enumerations packed into primitive {int} properties.
- *
- * This class is immutable, and must be constructed by a {@link Builder}.
- *
- * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping)
- */
-public final class IntEnumMapping {
-    private final Value[] mValues;
-
-    /**
-     * Map from a property value to a string name.
-     *
-     * @param value The value of a property
-     * @return The name of the enumeration value, null if the value is not mapped
-     */
-    @Nullable
-    public String nameOf(int value) {
-        for (Value valueTuple : mValues) {
-            if (valueTuple.mValue == value) {
-                return valueTuple.mName;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Create a new instance from a builder.
-     *
-     * This constructor is private, use {@link Builder#build()} instead.
-     *
-     * @param builder A builder to create from
-     */
-    private IntEnumMapping(Builder builder) {
-        mValues = builder.mValues.toArray(new Value[builder.mValues.size()]);
-    }
-
-    /**
-     * A builder for {@link IntEnumMapping}
-     */
-    public static final class Builder {
-        private final ArrayList<Value> mValues;
-
-        public Builder() {
-            mValues = new ArrayList<>();
-        }
-
-        /**
-         * Add a new entry to this mapping.
-         *
-         * @param name Name of the enumeration value
-         * @param value Int value of the enumeration value
-         * @return This builder
-         */
-        @NonNull
-        public Builder addValue(@NonNull String name, int value) {
-            mValues.add(new Value(name, value));
-            return this;
-        }
-
-        /**
-         * Clear the builder, allowing for recycling.
-         */
-        public void clear() {
-            mValues.clear();
-        }
-
-        /**
-         * Build a new {@link IntEnumMapping} from this builder
-         *
-         * @return A new mapping
-         */
-        @NonNull
-        public IntEnumMapping build() {
-            return new IntEnumMapping(this);
-        }
-    }
-
-    /**
-     * Inner class that holds the name and value of an enumeration value.
-     */
-    private static final class Value {
-        @NonNull private final String mName;
-        private final int mValue;
-
-        private Value(@NonNull String name, int value) {
-            mName = name;
-            mValue = value;
-        }
-    }
-}
diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java
index dcb87e1..8f7dfd5 100644
--- a/core/java/android/view/inspector/IntFlagMapping.java
+++ b/core/java/android/view/inspector/IntFlagMapping.java
@@ -19,14 +19,20 @@
 import android.annotation.NonNull;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
- * Maps the values of an {int} property to arrays of string for properties that encode flags.
+ * Maps the values of an {@code int} property to arrays of string for properties that encode flags.
  *
  * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
- * for flag values packed into primitive {int} properties.
+ * for flag values packed into primitive {@code int} properties.
  *
- * Each flag has a
+ * Each flag has a mask and a target value, for non-exclusive flags, the target can also be used as
+ * the mask. A given integer value is compared against each flag to find what flags are active for
+ * it by bitwise anding it with the mask and comparing the result against the target, that is,
+ * {@code (value & mask) == target}.
  *
  * This class is immutable, and must be constructed by a {@link Builder}.
  *
@@ -42,8 +48,8 @@
      * @return The names of the enabled flags
      */
     @NonNull
-    public String[] namesOf(int value) {
-        ArrayList<String> enabledFlagNames = new ArrayList<>(mFlags.length);
+    public Set<String> get(int value) {
+        final Set<String> enabledFlagNames = new HashSet<>(mFlags.length);
 
         for (Flag flag : mFlags) {
             if (flag.isEnabledFor(value)) {
@@ -51,7 +57,7 @@
             }
         }
 
-        return enabledFlagNames.toArray(new String[enabledFlagNames.size()]);
+        return Collections.unmodifiableSet(enabledFlagNames);
     }
 
     /**
@@ -81,7 +87,7 @@
          * The target value will be used as a mask, to handle the common case where flag values
          * are not mutually exclusive. The flag will be considered enabled for a property value if
          * the result of bitwise anding the target and the value equals the target, that is:
-         * {(value & target) == target}.
+         * {@code (value & target) == target}.
          *
          * @param name The name of the flag
          * @param target The value to compare against
@@ -97,7 +103,7 @@
          * Add a new flag with a mask.
          *
          * The flag will be considered enabled for a property value if the result of bitwise anding
-         * the value and the mask equals the target, that is: {(value & mask) == target}.
+         * the value and the mask equals the target, that is: {@code (value & mask) == target}.
          *
          * @param name The name of the flag
          * @param target The value to compare against
@@ -111,13 +117,6 @@
         }
 
         /**
-         * Clear the builder, allowing for recycling.
-         */
-        public void clear() {
-            mFlags.clear();
-        }
-
-        /**
          * Build a new {@link IntFlagMapping} from this builder.
          *
          * @return A new mapping
@@ -143,7 +142,7 @@
         }
 
         /**
-         * Compare the supplied property value against the mask and taget.
+         * Compare the supplied property value against the mask and target.
          *
          * @param value The value to check
          * @return True if this flag is enabled
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
index 5fb291b..e20582b 100644
--- a/core/java/android/view/inspector/PropertyMapper.java
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -18,6 +18,7 @@
 
 import android.annotation.AttrRes;
 import android.annotation.NonNull;
+import android.util.SparseArray;
 
 /**
  * An interface for mapping the string names of inspectable properties to integer identifiers.
@@ -154,7 +155,7 @@
     int mapIntEnum(
             @NonNull String name,
             @AttrRes int attributeId,
-            @NonNull IntEnumMapping mapping);
+            @NonNull SparseArray<String> mapping);
 
     /**
      * Map a string name to an integer ID for a flag set packed into an int property.
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index fd83e8d..a8b7ecc 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -25,7 +25,13 @@
 /**
  * An interface for reading the properties of an inspectable object.
  *
- * Used as the parameter for {@link InspectionCompanion#readProperties(Object, PropertyReader)}.
+ * {@code PropertyReader} is defined as an interface that will be called by
+ * {@link InspectionCompanion#readProperties(Object, PropertyReader)}. This approach allows a
+ * client inspector to read the values of primitive properties without the overhead of
+ * instantiating a class to hold the property values for each inspection pass. If an inspectable
+ * remains unchanged between reading passes, it should be possible for a {@code PropertyReader} to
+ * avoid new allocations for subsequent reading passes.
+ *
  * It has separate methods for all primitive types to avoid autoboxing overhead if a concrete
  * implementation is able to work with primitives. Implementations should be prepared to accept
  * {null} as the value of {@link PropertyReader#readObject(int, Object)}.
@@ -38,7 +44,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {boolean}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code boolean}
      */
     void readBoolean(int id, boolean value);
 
@@ -47,7 +53,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {byte}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code byte}
      */
     void readByte(int id, byte value);
 
@@ -56,7 +62,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {char}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code char}
      */
     void readChar(int id, char value);
 
@@ -65,7 +71,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {double}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code double}
      */
     void readDouble(int id, double value);
 
@@ -74,7 +80,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {float}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code float}
      */
     void readFloat(int id, float value);
 
@@ -83,7 +89,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as an {int}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as an {@code int}
      */
     void readInt(int id, int value);
 
@@ -92,7 +98,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {long}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code long}
      */
     void readLong(int id, long value);
 
@@ -101,7 +107,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {short}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code short}
      */
     void readShort(int id, short value);
 
diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java
new file mode 100644
index 0000000..1a6e5d8
--- /dev/null
+++ b/core/java/android/view/textclassifier/ConversationAction.java
@@ -0,0 +1,266 @@
+/*
+ * 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 java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.app.RemoteAction;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+
+/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+public final class ConversationAction implements Parcelable {
+
+    /** @hide */
+    @Retention(SOURCE)
+    @StringDef(
+            value = {
+                    TYPE_VIEW_CALENDAR,
+                    TYPE_VIEW_MAP,
+                    TYPE_TRACK_FLIGHT,
+                    TYPE_OPEN_URL,
+                    TYPE_SEND_SMS,
+                    TYPE_CALL_PHONE,
+                    TYPE_SEND_EMAIL,
+                    TYPE_TEXT_REPLY,
+                    TYPE_CREATE_REMINDER,
+                    TYPE_SHARE_LOCATION
+            },
+            prefix = "TYPE_")
+    public @interface ActionType {}
+
+    /**
+     * Indicates an action to view a calendar at a specified time.
+     */
+    public static final String TYPE_VIEW_CALENDAR = "view_calendar";
+    /**
+     * Indicates an action to view the map at a specified location.
+     */
+    public static final String TYPE_VIEW_MAP = "view_map";
+    /**
+     * Indicates an action to track a flight.
+     */
+    public static final String TYPE_TRACK_FLIGHT = "track_flight";
+    /**
+     * Indicates an action to open an URL.
+     */
+    public static final String TYPE_OPEN_URL = "open_url";
+    /**
+     * Indicates an action to send a SMS.
+     */
+    public static final String TYPE_SEND_SMS = "send_sms";
+    /**
+     * Indicates an action to call a phone number.
+     */
+    public static final String TYPE_CALL_PHONE = "call_phone";
+    /**
+     * Indicates an action to send an email.
+     */
+    public static final String TYPE_SEND_EMAIL = "send_email";
+    /**
+     * Indicates an action to reply with a text message.
+     */
+    public static final String TYPE_TEXT_REPLY = "text_reply";
+    /**
+     * Indicates an action to create a reminder.
+     */
+    public static final String TYPE_CREATE_REMINDER = "create_reminder";
+    /**
+     * Indicates an action to reply with a location.
+     */
+    public static final String TYPE_SHARE_LOCATION = "share_location";
+
+    public static final Creator<ConversationAction> CREATOR =
+            new Creator<ConversationAction>() {
+                @Override
+                public ConversationAction createFromParcel(Parcel in) {
+                    return new ConversationAction(in);
+                }
+
+                @Override
+                public ConversationAction[] newArray(int size) {
+                    return new ConversationAction[size];
+                }
+            };
+
+    @NonNull
+    @ActionType
+    private final String mType;
+    @NonNull
+    private final CharSequence mTextReply;
+    @Nullable
+    private final RemoteAction mAction;
+
+    @FloatRange(from = 0, to = 1)
+    private final float mScore;
+
+    @NonNull
+    private final Bundle mExtras;
+
+    private ConversationAction(
+            @NonNull String type,
+            @Nullable RemoteAction action,
+            @Nullable CharSequence textReply,
+            float score,
+            @NonNull Bundle extras) {
+        mType = Preconditions.checkNotNull(type);
+        mAction = action;
+        mTextReply = textReply;
+        mScore = score;
+        mExtras = Preconditions.checkNotNull(extras);
+    }
+
+    private ConversationAction(Parcel in) {
+        mType = in.readString();
+        mAction = in.readParcelable(null);
+        mTextReply = in.readCharSequence();
+        mScore = in.readFloat();
+        mExtras = in.readBundle();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mType);
+        parcel.writeParcelable(mAction, flags);
+        parcel.writeCharSequence(mTextReply);
+        parcel.writeFloat(mScore);
+        parcel.writeBundle(mExtras);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
+    @NonNull
+    @ActionType
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
+     * the specified action type.
+     */
+    @Nullable
+    public RemoteAction getAction() {
+        return mAction;
+    }
+
+    /**
+     * Returns the confidence score for the specified action. The value ranges from 0 (low
+     * confidence) to 1 (high confidence).
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getConfidenceScore() {
+        return mScore;
+    }
+
+    /**
+     * Returns the text reply that could be sent as a reply to the given conversation.
+     * <p>
+     * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
+     */
+    @Nullable
+    public CharSequence getTextReply() {
+        return mTextReply;
+    }
+
+    /**
+     * Returns the extended data related to this conversation action.
+     *
+     * <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 mExtras.deepCopy();
+    }
+
+    /** Builder class to construct {@link ConversationAction}. */
+    public static final class Builder {
+        @Nullable
+        @ActionType
+        private String mType;
+        @Nullable
+        private RemoteAction mAction;
+        @Nullable
+        private CharSequence mTextReply;
+        private float mScore;
+        @Nullable
+        private Bundle mExtras;
+
+        public Builder(@NonNull @ActionType String actionType) {
+            mType = Preconditions.checkNotNull(actionType);
+        }
+
+        /**
+         * Sets an action that may be performed on the given conversation.
+         */
+        @NonNull
+        public Builder setAction(@Nullable RemoteAction action) {
+            mAction = action;
+            return this;
+        }
+
+        /**
+         * Sets a text reply that may be performed on the given conversation.
+         */
+        @NonNull
+        public Builder setTextReply(@Nullable CharSequence textReply) {
+            mTextReply = textReply;
+            return this;
+        }
+
+        /** Sets the confident score. */
+        @NonNull
+        public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
+            mScore = score;
+            return this;
+        }
+
+        /**
+         * Sets the extended data for the conversation action object.
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /** Builds the {@link ConversationAction} object. */
+        @NonNull
+        public ConversationAction build() {
+            return new ConversationAction(
+                    mType,
+                    mAction,
+                    mTextReply,
+                    mScore,
+                    mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 3f690f7..f7c1a26 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -17,18 +17,15 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.annotation.FloatRange;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.app.Person;
-import android.app.RemoteAction;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.SpannedString;
-import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
@@ -37,10 +34,8 @@
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
@@ -62,83 +57,6 @@
                 }
             };
 
-    /** @hide */
-    @Retention(SOURCE)
-    @StringDef(
-            value = {
-                    TYPE_VIEW_CALENDAR,
-                    TYPE_VIEW_MAP,
-                    TYPE_TRACK_FLIGHT,
-                    TYPE_OPEN_URL,
-                    TYPE_SEND_SMS,
-                    TYPE_CALL_PHONE,
-                    TYPE_SEND_EMAIL,
-                    TYPE_TEXT_REPLY,
-                    TYPE_CREATE_REMINDER,
-                    TYPE_SHARE_LOCATION
-            },
-            prefix = "TYPE_")
-    public @interface ActionType {}
-
-    /**
-     * Indicates an action to view a calendar at a specified time.
-     */
-    public static final String TYPE_VIEW_CALENDAR = "view_calendar";
-    /**
-     * Indicates an action to view the map at a specified location.
-     */
-    public static final String TYPE_VIEW_MAP = "view_map";
-    /**
-     * Indicates an action to track a flight.
-     */
-    public static final String TYPE_TRACK_FLIGHT = "track_flight";
-    /**
-     * Indicates an action to open an URL.
-     */
-    public static final String TYPE_OPEN_URL = "open_url";
-    /**
-     * Indicates an action to send a SMS.
-     */
-    public static final String TYPE_SEND_SMS = "send_sms";
-    /**
-     * Indicates an action to call a phone number.
-     */
-    public static final String TYPE_CALL_PHONE = "call_phone";
-    /**
-     * Indicates an action to send an email.
-     */
-    public static final String TYPE_SEND_EMAIL = "send_email";
-    /**
-     * Indicates an action to reply with a text message.
-     */
-    public static final String TYPE_TEXT_REPLY = "text_reply";
-    /**
-     * Indicates an action to create a reminder.
-     */
-    public static final String TYPE_CREATE_REMINDER = "create_reminder";
-    /**
-     * Indicates an action to reply with a location.
-     */
-    public static final String TYPE_SHARE_LOCATION = "share_location";
-
-    /** @hide */
-    @Retention(SOURCE)
-    @StringDef(
-            value = {
-                    HINT_FOR_NOTIFICATION,
-                    HINT_FOR_IN_APP,
-            },
-            prefix = "HINT_")
-    public @interface Hint {}
-    /**
-     * To indicate the generated actions will be used within the app.
-     */
-    public static final String HINT_FOR_IN_APP = "in_app";
-    /**
-     * To indicate the generated actions will be used for notification.
-     */
-    public static final String HINT_FOR_NOTIFICATION = "notification";
-
     private final List<ConversationAction> mConversationActions;
     private final String mId;
 
@@ -184,182 +102,6 @@
         return mId;
     }
 
-    /** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
-    public static final class ConversationAction implements Parcelable {
-
-        public static final Creator<ConversationAction> CREATOR =
-                new Creator<ConversationAction>() {
-                    @Override
-                    public ConversationAction createFromParcel(Parcel in) {
-                        return new ConversationAction(in);
-                    }
-
-                    @Override
-                    public ConversationAction[] newArray(int size) {
-                        return new ConversationAction[size];
-                    }
-                };
-
-        @NonNull
-        @ActionType
-        private final String mType;
-        @NonNull
-        private final CharSequence mTextReply;
-        @Nullable
-        private final RemoteAction mAction;
-
-        @FloatRange(from = 0, to = 1)
-        private final float mScore;
-
-        @NonNull
-        private final Bundle mExtras;
-
-        private ConversationAction(
-                @NonNull String type,
-                @Nullable RemoteAction action,
-                @Nullable CharSequence textReply,
-                float score,
-                @NonNull Bundle extras) {
-            mType = Preconditions.checkNotNull(type);
-            mAction = action;
-            mTextReply = textReply;
-            mScore = score;
-            mExtras = Preconditions.checkNotNull(extras);
-        }
-
-        private ConversationAction(Parcel in) {
-            mType = in.readString();
-            mAction = in.readParcelable(null);
-            mTextReply = in.readCharSequence();
-            mScore = in.readFloat();
-            mExtras = in.readBundle();
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int flags) {
-            parcel.writeString(mType);
-            parcel.writeParcelable(mAction, flags);
-            parcel.writeCharSequence(mTextReply);
-            parcel.writeFloat(mScore);
-            parcel.writeBundle(mExtras);
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @NonNull
-        @ActionType
-        /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
-        public String getType() {
-            return mType;
-        }
-
-        @Nullable
-        /**
-         * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
-         * the specified action type.
-         */
-        public RemoteAction getAction() {
-            return mAction;
-        }
-
-        /**
-         * Returns the confidence score for the specified action. The value ranges from 0 (low
-         * confidence) to 1 (high confidence).
-         */
-        @FloatRange(from = 0, to = 1)
-        public float getConfidenceScore() {
-            return mScore;
-        }
-
-        /**
-         * Returns the text reply that could be sent as a reply to the given conversation.
-         * <p>
-         * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
-         */
-        @Nullable
-        public CharSequence getTextReply() {
-            return mTextReply;
-        }
-
-        /**
-         * Returns the extended data related to this conversation action.
-         *
-         * <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 mExtras.deepCopy();
-        }
-
-        /** Builder class to construct {@link ConversationAction}. */
-        public static final class Builder {
-            @Nullable
-            @ActionType
-            private String mType;
-            @Nullable
-            private RemoteAction mAction;
-            @Nullable
-            private CharSequence mTextReply;
-            private float mScore;
-            @Nullable
-            private Bundle mExtras;
-
-            public Builder(@NonNull @ActionType String actionType) {
-                mType = Preconditions.checkNotNull(actionType);
-            }
-
-            /**
-             * Sets an action that may be performed on the given conversation.
-             */
-            @NonNull
-            public Builder setAction(@Nullable RemoteAction action) {
-                mAction = action;
-                return this;
-            }
-
-            /**
-             * Sets a text reply that may be performed on the given conversation.
-             */
-            @NonNull
-            public Builder setTextReply(@Nullable CharSequence textReply) {
-                mTextReply = textReply;
-                return this;
-            }
-
-            /** Sets the confident score. */
-            @NonNull
-            public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
-                mScore = score;
-                return this;
-            }
-
-            /**
-             * Sets the extended data for the conversation action object.
-             */
-            @NonNull
-            public Builder setExtras(@Nullable Bundle extras) {
-                mExtras = extras;
-                return this;
-            }
-
-            /** Builds the {@link ConversationAction} object. */
-            @NonNull
-            public ConversationAction build() {
-                return new ConversationAction(
-                        mType,
-                        mAction,
-                        mTextReply,
-                        mScore,
-                        mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
-            }
-        }
-    }
-
     /** Represents a message in the conversation. */
     public static final class Message implements Parcelable {
         /**
@@ -538,156 +280,36 @@
         }
     }
 
-    /** Configuration object for specifying what action types to identify. */
-    public static final class TypeConfig implements Parcelable {
-        @NonNull
-        @ActionType
-        private final Set<String> mExcludedTypes;
-        @NonNull
-        @ActionType
-        private final Set<String> mIncludedTypes;
-        private final boolean mIncludeTypesFromTextClassifier;
-
-        private TypeConfig(
-                @NonNull Set<String> includedTypes,
-                @NonNull Set<String> excludedTypes,
-                boolean includeTypesFromTextClassifier) {
-            mIncludedTypes = Preconditions.checkNotNull(includedTypes);
-            mExcludedTypes = Preconditions.checkNotNull(excludedTypes);
-            mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
-        }
-
-        private TypeConfig(Parcel in) {
-            mIncludedTypes = new ArraySet<>(in.createStringArrayList());
-            mExcludedTypes = new ArraySet<>(in.createStringArrayList());
-            mIncludeTypesFromTextClassifier = in.readByte() != 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int flags) {
-            parcel.writeStringList(new ArrayList<>(mIncludedTypes));
-            parcel.writeStringList(new ArrayList<>(mExcludedTypes));
-            parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        public static final Creator<TypeConfig> CREATOR =
-                new Creator<TypeConfig>() {
-                    @Override
-                    public TypeConfig createFromParcel(Parcel in) {
-                        return new TypeConfig(in);
-                    }
-
-                    @Override
-                    public TypeConfig[] newArray(int size) {
-                        return new TypeConfig[size];
-                    }
-                };
-
-        /**
-         * Returns a final list of types that the text classifier should look for.
-         *
-         * <p>NOTE: This method is intended for use by a text classifier.
-         *
-         * @param defaultTypes types the text classifier thinks should be included before factoring
-         *    in the included/excluded types given by the client.
-         */
-        @NonNull
-        public Collection<String> resolveTypes(@Nullable Collection<String> defaultTypes) {
-            Set<String> types = new ArraySet<>();
-            if (mIncludeTypesFromTextClassifier && defaultTypes != null) {
-                types.addAll(defaultTypes);
-            }
-            types.addAll(mIncludedTypes);
-            types.removeAll(mExcludedTypes);
-            return Collections.unmodifiableCollection(types);
-        }
-
-        /**
-         * Return whether the client allows the text classifier to include its own list of default
-         * types. If this function returns {@code true}, the text classifier can consider specifying
-         * a default list of entity types in {@link #resolveTypes(Collection)}.
-         *
-         * <p>NOTE: This method is intended for use by a text classifier.
-         *
-         * @see #resolveTypes(Collection)
-         */
-        public boolean shouldIncludeTypesFromTextClassifier() {
-            return mIncludeTypesFromTextClassifier;
-        }
-
-        /** Builder class to construct the {@link TypeConfig} object. */
-        public static final class Builder {
-            @Nullable
-            private Collection<String> mExcludedTypes;
-            @Nullable
-            private Collection<String> mIncludedTypes;
-            private boolean mIncludeTypesFromTextClassifier = true;
-
-            /**
-             * Sets a collection of types that are explicitly included, for example, {@link
-             * #TYPE_VIEW_CALENDAR}.
-             */
-            @NonNull
-            public Builder setIncludedTypes(
-                    @Nullable @ActionType Collection<String> includedTypes) {
-                mIncludedTypes = includedTypes;
-                return this;
-            }
-
-            /**
-             * Sets a collection of types that are explicitly excluded, for example, {@link
-             * #TYPE_VIEW_CALENDAR}.
-             */
-            @NonNull
-            public Builder setExcludedTypes(
-                    @Nullable @ActionType Collection<String> excludedTypes) {
-                mExcludedTypes = excludedTypes;
-                return this;
-            }
-
-            /**
-             * Specifies whether or not to include the types suggested by the text classifier. By
-             * default, it is included.
-             */
-            @NonNull
-            public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
-                mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
-                return this;
-            }
-
-            /**
-             * Combines all of the options that have been set and returns a new {@link TypeConfig}
-             * object.
-             */
-            @NonNull
-            public TypeConfig build() {
-                return new TypeConfig(
-                        mIncludedTypes == null
-                                ? Collections.emptySet()
-                                : new ArraySet<>(mIncludedTypes),
-                        mExcludedTypes == null
-                                ? Collections.emptySet()
-                                : new ArraySet<>(mExcludedTypes),
-                        mIncludeTypesFromTextClassifier);
-            }
-        }
-    }
-
     /**
      * A request object for generating conversation action suggestions.
      *
      * @see TextClassifier#suggestConversationActions(Request)
      */
     public static final class Request implements Parcelable {
+
+        /** @hide */
+        @Retention(SOURCE)
+        @StringDef(
+                value = {
+                        HINT_FOR_NOTIFICATION,
+                        HINT_FOR_IN_APP,
+                },
+                prefix = "HINT_")
+        public @interface Hint {}
+
+        /**
+         * To indicate the generated actions will be used within the app.
+         */
+        public static final String HINT_FOR_IN_APP = "in_app";
+        /**
+         * To indicate the generated actions will be used for notification.
+         */
+        public static final String HINT_FOR_NOTIFICATION = "notification";
+
         @NonNull
         private final List<Message> mConversation;
         @NonNull
-        private final TypeConfig mTypeConfig;
+        private final TextClassifier.EntityConfig mTypeConfig;
         private final int mMaxSuggestions;
         @NonNull
         @Hint
@@ -699,7 +321,7 @@
 
         private Request(
                 @NonNull List<Message> conversation,
-                @NonNull TypeConfig typeConfig,
+                @NonNull TextClassifier.EntityConfig typeConfig,
                 int maxSuggestions,
                 String conversationId,
                 @Nullable @Hint List<String> hints) {
@@ -713,7 +335,7 @@
         private static Request readFromParcel(Parcel in) {
             List<Message> conversation = new ArrayList<>();
             in.readParcelableList(conversation, null);
-            TypeConfig typeConfig = in.readParcelable(null);
+            TextClassifier.EntityConfig typeConfig = in.readParcelable(null);
             int maxSuggestions = in.readInt();
             String conversationId = in.readString();
             List<String> hints = new ArrayList<>();
@@ -760,7 +382,7 @@
 
         /** Returns the type config. */
         @NonNull
-        public TypeConfig getTypeConfig() {
+        public TextClassifier.EntityConfig getTypeConfig() {
             return mTypeConfig;
         }
 
@@ -820,7 +442,7 @@
             @NonNull
             private List<Message> mConversation;
             @Nullable
-            private TypeConfig mTypeConfig;
+            private TextClassifier.EntityConfig mTypeConfig;
             private int mMaxSuggestions;
             @Nullable
             private String mConversationId;
@@ -849,7 +471,7 @@
 
             /** Sets the type config. */
             @NonNull
-            public Builder setTypeConfig(@Nullable TypeConfig typeConfig) {
+            public Builder setTypeConfig(@Nullable TextClassifier.EntityConfig typeConfig) {
                 mTypeConfig = typeConfig;
                 return this;
             }
@@ -879,7 +501,9 @@
             public Request build() {
                 return new Request(
                         Collections.unmodifiableList(mConversation),
-                        mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
+                        mTypeConfig == null
+                                ? new TextClassifier.EntityConfig.Builder().build()
+                                : mTypeConfig,
                         mMaxSuggestions,
                         mConversationId,
                         mHints == null
diff --git a/core/java/android/view/textclassifier/GenerateLinksLogger.java b/core/java/android/view/textclassifier/GenerateLinksLogger.java
index 067513f..3996b27 100644
--- a/core/java/android/view/textclassifier/GenerateLinksLogger.java
+++ b/core/java/android/view/textclassifier/GenerateLinksLogger.java
@@ -39,7 +39,6 @@
 public final class GenerateLinksLogger {
 
     private static final String LOG_TAG = "GenerateLinksLogger";
-    private static final boolean DEBUG_LOG_ENABLED = false;
     private static final String ZERO = "0";
 
     private final MetricsLogger mMetricsLogger;
@@ -128,8 +127,9 @@
     }
 
     private static void debugLog(LogMaker log) {
-        if (!DEBUG_LOG_ENABLED) return;
-
+        if (!Log.ENABLE_FULL_LOGGING) {
+            return;
+        }
         final String callId = Objects.toString(
                 log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
         final String entityType = Objects.toString(
@@ -143,7 +143,7 @@
         final int latencyMs = Integer.parseInt(
                 Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
 
-        Log.d(LOG_TAG,
+        Log.v(LOG_TAG,
                 String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
                         numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
     }
diff --git a/core/java/android/view/textclassifier/Log.java b/core/java/android/view/textclassifier/Log.java
index ef19ee5..5c60c09 100644
--- a/core/java/android/view/textclassifier/Log.java
+++ b/core/java/android/view/textclassifier/Log.java
@@ -16,10 +16,12 @@
 
 package android.view.textclassifier;
 
-import android.util.Slog;
-
 /**
  * Logging for android.view.textclassifier package.
+ * <p>
+ * To enable full log:
+ * 1. adb shell setprop log.tag.androidtc VERBOSE
+ * 2. adb shell stop && adb shell start
  */
 final class Log {
 
@@ -27,24 +29,32 @@
      * true: Enables full logging.
      * false: Limits logging to debug level.
      */
-    private static final boolean ENABLE_FULL_LOGGING = false;
+    static final boolean ENABLE_FULL_LOGGING =
+            android.util.Log.isLoggable(TextClassifier.DEFAULT_LOG_TAG, android.util.Log.VERBOSE);
 
-    private Log() {}
+    private Log() {
+    }
+
+    public static void v(String tag, String msg) {
+        if (ENABLE_FULL_LOGGING) {
+            android.util.Log.v(tag, msg);
+        }
+    }
 
     public static void d(String tag, String msg) {
-        Slog.d(tag, msg);
+        android.util.Log.d(tag, msg);
     }
 
     public static void w(String tag, String msg) {
-        Slog.w(tag, msg);
+        android.util.Log.w(tag, msg);
     }
 
     public static void e(String tag, String msg, Throwable tr) {
         if (ENABLE_FULL_LOGGING) {
-            Slog.e(tag, msg, tr);
+            android.util.Log.e(tag, msg, tr);
         } else {
             final String trString = (tr != null) ? tr.getClass().getSimpleName() : "??";
-            Slog.d(tag, String.format("%s (%s)", msg, trString));
+            android.util.Log.d(tag, String.format("%s (%s)", msg, trString));
         }
     }
 }
diff --git a/core/java/android/view/textclassifier/SelectionSessionLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
index cdacdd5..48a568a 100644
--- a/core/java/android/view/textclassifier/SelectionSessionLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -39,7 +39,6 @@
 public final class SelectionSessionLogger {
 
     private static final String LOG_TAG = "SelectionSessionLogger";
-    private static final boolean DEBUG_LOG_ENABLED = false;
     static final String CLASSIFIER_ID = "androidtc";
 
     private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
@@ -195,8 +194,9 @@
     }
 
     private static void debugLog(LogMaker log) {
-        if (!DEBUG_LOG_ENABLED) return;
-
+        if (!Log.ENABLE_FULL_LOGGING) {
+            return;
+        }
         final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
         final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
         final String widget = widgetVersion.isEmpty()
@@ -221,7 +221,7 @@
         final int eventEnd = Integer.parseInt(
                 Objects.toString(log.getTaggedData(EVENT_END), ZERO));
 
-        Log.d(LOG_TAG,
+        Log.v(LOG_TAG,
                 String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
                         index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
                         widget, model));
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 50801a2..ce680ec 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -117,15 +117,15 @@
             .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
     private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
             new StringJoiner(ENTITY_LIST_DELIMITER)
-                    .add(ConversationActions.TYPE_TEXT_REPLY)
-                    .add(ConversationActions.TYPE_CREATE_REMINDER)
-                    .add(ConversationActions.TYPE_CALL_PHONE)
-                    .add(ConversationActions.TYPE_OPEN_URL)
-                    .add(ConversationActions.TYPE_SEND_EMAIL)
-                    .add(ConversationActions.TYPE_SEND_SMS)
-                    .add(ConversationActions.TYPE_TRACK_FLIGHT)
-                    .add(ConversationActions.TYPE_VIEW_CALENDAR)
-                    .add(ConversationActions.TYPE_VIEW_MAP)
+                    .add(ConversationAction.TYPE_TEXT_REPLY)
+                    .add(ConversationAction.TYPE_CREATE_REMINDER)
+                    .add(ConversationAction.TYPE_CALL_PHONE)
+                    .add(ConversationAction.TYPE_OPEN_URL)
+                    .add(ConversationAction.TYPE_SEND_EMAIL)
+                    .add(ConversationAction.TYPE_SEND_SMS)
+                    .add(ConversationAction.TYPE_TRACK_FLIGHT)
+                    .add(ConversationAction.TYPE_VIEW_CALENDAR)
+                    .add(ConversationAction.TYPE_VIEW_MAP)
                     .toString();
 
     private final boolean mSystemTextClassifierEnabled;
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index 4c64198..45668c0 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -27,7 +27,6 @@
 @WorkerThread
 final class TextClassificationSession implements TextClassifier {
 
-    /* package */ static final boolean DEBUG_LOG_ENABLED = true;
     private static final String LOG_TAG = "TextClassificationSession";
 
     private final TextClassifier mDelegate;
@@ -133,9 +132,7 @@
 
             if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
                     && mStartEvent == null) {
-                if (DEBUG_LOG_ENABLED) {
-                    Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
-                }
+                Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
                 return false;
             }
 
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 8709e09..5a56136 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -32,7 +32,6 @@
 import android.text.util.Linkify;
 import android.text.util.Linkify.LinkifyMask;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -43,6 +42,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -324,7 +324,7 @@
     }
 
     /**
-     * Detects the language of the specified text.
+     * Detects the language of the text in the given request.
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
@@ -403,42 +403,59 @@
     default void dump(@NonNull IndentingPrintWriter printWriter) {}
 
     /**
-     * Configuration object for specifying what entities to identify.
+     * Configuration object for specifying what entity types to identify.
      *
      * Configs are initially based on a predefined preset, and can be modified from there.
      */
     final class EntityConfig implements Parcelable {
-        private final Collection<String> mHints;
-        private final Collection<String> mExcludedEntityTypes;
-        private final Collection<String> mIncludedEntityTypes;
-        private final boolean mUseHints;
+        private final List<String> mIncludedTypes;
+        private final List<String> mExcludedTypes;
+        private final List<String> mHints;
+        private final boolean mIncludeTypesFromTextClassifier;
 
-        private EntityConfig(boolean useHints, Collection<String> hints,
-                Collection<String> includedEntityTypes, Collection<String> excludedEntityTypes) {
-            mHints = hints == null
-                    ? Collections.EMPTY_LIST
-                    : Collections.unmodifiableCollection(new ArraySet<>(hints));
-            mExcludedEntityTypes = excludedEntityTypes == null
-                    ? Collections.EMPTY_LIST : new ArraySet<>(excludedEntityTypes);
-            mIncludedEntityTypes = includedEntityTypes == null
-                    ? Collections.EMPTY_LIST : new ArraySet<>(includedEntityTypes);
-            mUseHints = useHints;
+        private EntityConfig(
+                List<String> includedEntityTypes,
+                List<String> excludedEntityTypes,
+                List<String> hints,
+                boolean includeTypesFromTextClassifier) {
+            mIncludedTypes = Preconditions.checkNotNull(includedEntityTypes);
+            mExcludedTypes = Preconditions.checkNotNull(excludedEntityTypes);
+            mHints = Preconditions.checkNotNull(hints);
+            mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+        }
+
+        private EntityConfig(Parcel in) {
+            mIncludedTypes = new ArrayList<>();
+            in.readStringList(mIncludedTypes);
+            mExcludedTypes = new ArrayList<>();
+            in.readStringList(mExcludedTypes);
+            List<String> tmpHints = new ArrayList<>();
+            in.readStringList(tmpHints);
+            mHints = Collections.unmodifiableList(tmpHints);
+            mIncludeTypesFromTextClassifier = in.readByte() != 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeStringList(mIncludedTypes);
+            parcel.writeStringList(mExcludedTypes);
+            parcel.writeStringList(mHints);
+            parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
         }
 
         /**
          * Creates an EntityConfig.
          *
          * @param hints Hints for the TextClassifier to determine what types of entities to find.
+         *
+         * @deprecated Use {@link Builder} instead.
          */
+        @Deprecated
         public static EntityConfig createWithHints(@Nullable Collection<String> hints) {
-            return new EntityConfig(/* useHints */ true, hints,
-                    /* includedEntityTypes */null, /* excludedEntityTypes */ null);
-        }
-
-        // TODO: Remove once apps can build against the latest sdk.
-        /** @hide */
-        public static EntityConfig create(@Nullable Collection<String> hints) {
-            return createWithHints(hints);
+            return new EntityConfig.Builder()
+                    .includeTypesFromTextClassifier(true)
+                    .setHints(hints)
+                    .build();
         }
 
         /**
@@ -450,12 +467,19 @@
          *
          *
          * Note that if an entity has been excluded, the exclusion will take precedence.
+         *
+         * @deprecated Use {@link Builder} instead.
          */
+        @Deprecated
         public static EntityConfig create(@Nullable Collection<String> hints,
                 @Nullable Collection<String> includedEntityTypes,
                 @Nullable Collection<String> excludedEntityTypes) {
-            return new EntityConfig(/* useHints */ true, hints,
-                    includedEntityTypes, excludedEntityTypes);
+            return new EntityConfig.Builder()
+                    .setIncludedTypes(includedEntityTypes)
+                    .setExcludedTypes(excludedEntityTypes)
+                    .setHints(hints)
+                    .includeTypesFromTextClassifier(true)
+                    .build();
         }
 
         /**
@@ -463,34 +487,33 @@
          *
          * @param entityTypes Complete set of entities, e.g. {@link #TYPE_URL} to find.
          *
+         * @deprecated Use {@link Builder} instead.
          */
+        @Deprecated
         public static EntityConfig createWithExplicitEntityList(
                 @Nullable Collection<String> entityTypes) {
-            return new EntityConfig(/* useHints */ false, /* hints */ null,
-                    /* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null);
-        }
-
-        // TODO: Remove once apps can build against the latest sdk.
-        /** @hide */
-        public static EntityConfig createWithEntityList(@Nullable Collection<String> entityTypes) {
-            return createWithExplicitEntityList(entityTypes);
+            return new EntityConfig.Builder()
+                    .setIncludedTypes(entityTypes)
+                    .includeTypesFromTextClassifier(false)
+                    .build();
         }
 
         /**
-         * Returns a list of the final set of entities to find.
+         * Returns a final list of entity types to find.
          *
-         * @param entities Entities we think should be found before factoring in includes/excludes
+         * @param entityTypes Entity types we think should be found before factoring in
+         *                    includes/excludes
          *
          * This method is intended for use by TextClassifier implementations.
          */
         public Collection<String> resolveEntityListModifications(
-                @NonNull Collection<String> entities) {
-            final Set<String> finalSet = new HashSet();
-            if (mUseHints) {
-                finalSet.addAll(entities);
+                @NonNull Collection<String> entityTypes) {
+            final Set<String> finalSet = new HashSet<>();
+            if (mIncludeTypesFromTextClassifier) {
+                finalSet.addAll(entityTypes);
             }
-            finalSet.addAll(mIncludedEntityTypes);
-            finalSet.removeAll(mExcludedEntityTypes);
+            finalSet.addAll(mIncludedTypes);
+            finalSet.removeAll(mExcludedTypes);
             return finalSet;
         }
 
@@ -503,17 +526,22 @@
             return mHints;
         }
 
-        @Override
-        public int describeContents() {
-            return 0;
+        /**
+         * Return whether the client allows the text classifier to include its own list of
+         * default types. If this function returns {@code true}, a default list of types suggested
+         * from a text classifier will be taking into account.
+         *
+         * <p>NOTE: This method is intended for use by a text classifier.
+         *
+         * @see #resolveEntityListModifications(Collection)
+         */
+        public boolean shouldIncludeTypesFromTextClassifier() {
+            return mIncludeTypesFromTextClassifier;
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeStringList(new ArrayList<>(mHints));
-            dest.writeStringList(new ArrayList<>(mExcludedEntityTypes));
-            dest.writeStringList(new ArrayList<>(mIncludedEntityTypes));
-            dest.writeInt(mUseHints ? 1 : 0);
+        public int describeContents() {
+            return 0;
         }
 
         public static final Parcelable.Creator<EntityConfig> CREATOR =
@@ -529,11 +557,75 @@
                     }
                 };
 
-        private EntityConfig(Parcel in) {
-            mHints = new ArraySet<>(in.createStringArrayList());
-            mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList());
-            mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList());
-            mUseHints = in.readInt() == 1;
+
+
+        /** Builder class to construct the {@link EntityConfig} object. */
+        public static final class Builder {
+            @Nullable
+            private Collection<String> mIncludedTypes;
+            @Nullable
+            private Collection<String> mExcludedTypes;
+            @Nullable
+            private Collection<String> mHints;
+            private boolean mIncludeTypesFromTextClassifier = true;
+
+            /**
+             * Sets a collection of types that are explicitly included.
+             */
+            @NonNull
+            public Builder setIncludedTypes(@Nullable Collection<String> includedTypes) {
+                mIncludedTypes = includedTypes;
+                return this;
+            }
+
+            /**
+             * Sets a collection of types that are explicitly excluded.
+             */
+            @NonNull
+            public Builder setExcludedTypes(@Nullable Collection<String> excludedTypes) {
+                mExcludedTypes = excludedTypes;
+                return this;
+            }
+
+            /**
+             * Specifies whether or not to include the types suggested by the text classifier. By
+             * default, it is included.
+             */
+            @NonNull
+            public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
+                mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+                return this;
+            }
+
+
+            /**
+             * Sets the hints for the TextClassifier to determine what types of entities to find.
+             * These hints will only be used if {@link #includeTypesFromTextClassifier} is
+             * set to be true.
+             */
+            public Builder setHints(Collection<String> hints) {
+                mHints = hints;
+                return this;
+            }
+
+            /**
+             * Combines all of the options that have been set and returns a new {@link EntityConfig}
+             * object.
+             */
+            @NonNull
+            public EntityConfig build() {
+                return new EntityConfig(
+                        mIncludedTypes == null
+                                ? Collections.emptyList()
+                                : new ArrayList<>(mIncludedTypes),
+                        mExcludedTypes == null
+                                ? Collections.emptyList()
+                                : new ArrayList<>(mExcludedTypes),
+                        mHints == null
+                                ? Collections.emptyList()
+                                : Collections.unmodifiableList(new ArrayList<>(mHints)),
+                        mIncludeTypesFromTextClassifier);
+            }
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index f2fea02..b84f6f0 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -116,6 +116,8 @@
     public static final int TYPE_SELECTION_RESET = 18;
     /** User composed a reply. */
     public static final int TYPE_MANUAL_REPLY = 19;
+    /** TextClassifier generated some actions */
+    public static final int TYPE_ACTIONS_GENERATED = 20;
 
     @Category private final int mEventCategory;
     @Type private final int mEventType;
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
new file mode 100644
index 0000000..439e594
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
@@ -0,0 +1,151 @@
+/*
+ * 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 com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_SESSION_ID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+
+import android.metrics.LogMaker;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
+
+
+/**
+ * Log {@link TextClassifierEvent} by using Tron, only support language detection and
+ * conversation actions.
+ *
+ * @hide
+ */
+public final class TextClassifierEventTronLogger {
+
+    private static final String TAG = "TCEventTronLogger";
+
+    private final MetricsLogger mMetricsLogger;
+
+    public TextClassifierEventTronLogger() {
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    @VisibleForTesting
+    public TextClassifierEventTronLogger(MetricsLogger metricsLogger) {
+        mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
+    }
+
+    /** Emits a text classifier event to the logs. */
+    public void writeEvent(TextClassifierEvent event) {
+        Preconditions.checkNotNull(event);
+        int category = getCategory(event);
+        if (category == -1) {
+            Log.w(TAG, "Unknown category: " + event.getEventCategory());
+            return;
+        }
+        final LogMaker log = new LogMaker(category)
+                .setType(getLogType(event))
+                .addTaggedData(FIELD_SELECTION_SESSION_ID, event.getResultId())
+                .addTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME, event.getEventTime())
+                .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL,
+                        SelectionSessionLogger.SignatureParser.getModelName(event.getResultId()))
+                .addTaggedData(FIELD_SELECTION_ENTITY_TYPE, event.getEntityType());
+        TextClassificationContext eventContext = event.getEventContext();
+        if (eventContext != null) {
+            log.addTaggedData(FIELD_SELECTION_WIDGET_TYPE, eventContext.getWidgetType());
+            log.addTaggedData(FIELD_SELECTION_WIDGET_VERSION, eventContext.getWidgetVersion());
+            log.setPackageName(eventContext.getPackageName());
+        }
+        mMetricsLogger.write(log);
+        debugLog(log);
+    }
+
+    private static int getCategory(TextClassifierEvent event) {
+        switch (event.getEventCategory()) {
+            case TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS:
+                return MetricsEvent.CONVERSATION_ACTIONS;
+            case TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION:
+                return MetricsEvent.LANGUAGE_DETECTION;
+        }
+        return -1;
+    }
+
+    private static int getLogType(TextClassifierEvent event) {
+        switch (event.getEventType()) {
+            case TextClassifierEvent.TYPE_SMART_ACTION:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
+            case TextClassifierEvent.TYPE_ACTIONS_SHOWN:
+                return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
+            case TextClassifierEvent.TYPE_MANUAL_REPLY:
+                return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
+            default:
+                return MetricsEvent.VIEW_UNKNOWN;
+        }
+    }
+
+    private String toCategoryName(int category) {
+        switch (category) {
+            case MetricsEvent.CONVERSATION_ACTIONS:
+                return "conversation_actions";
+            case MetricsEvent.LANGUAGE_DETECTION:
+                return "language_detection";
+        }
+        return "unknown";
+    }
+
+    private String toEventName(int logType) {
+        switch (logType) {
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
+                return "smart_share";
+            case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN:
+                return "actions_shown";
+            case MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY:
+                return "manual_reply";
+            case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED:
+                return "actions_generated";
+        }
+        return "unknown";
+    }
+
+    private void debugLog(LogMaker log) {
+        if (!Log.ENABLE_FULL_LOGGING) {
+            return;
+        }
+        final String id = String.valueOf(log.getTaggedData(FIELD_SELECTION_SESSION_ID));
+        final String categoryName = toCategoryName(log.getCategory());
+        final String eventName = toEventName(log.getType());
+        final String widgetType = String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_TYPE));
+        final String widgetVersion =
+                String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_VERSION));
+        final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
+        final String entityType = String.valueOf(log.getTaggedData(FIELD_SELECTION_ENTITY_TYPE));
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("writeEvent: ");
+        builder.append("id=").append(id);
+        builder.append(", category=").append(categoryName);
+        builder.append(", eventName=").append(eventName);
+        builder.append(", widgetType=").append(widgetType);
+        builder.append(", widgetVersion=").append(widgetVersion);
+        builder.append(", model=").append(model);
+        builder.append(", entityType=").append(entityType);
+
+        Log.v(TAG, builder.toString());
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index fcd06c3..9ab963e 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -115,9 +115,9 @@
     @GuardedBy("mLock") // Do not access outside this lock.
     private ActionsSuggestionsModel mActionsImpl;
 
-    private final Object mLoggerLock = new Object();
-    @GuardedBy("mLoggerLock") // Do not access outside this lock.
-    private SelectionSessionLogger mSessionLogger;
+    private final SelectionSessionLogger mSessionLogger = new SelectionSessionLogger();
+    private final TextClassifierEventTronLogger mTextClassifierEventTronLogger =
+            new TextClassifierEventTronLogger();
 
     private final TextClassificationConstants mSettings;
 
@@ -337,12 +337,7 @@
     @Override
     public void onSelectionEvent(SelectionEvent event) {
         Preconditions.checkNotNull(event);
-        synchronized (mLoggerLock) {
-            if (mSessionLogger == null) {
-                mSessionLogger = new SelectionSessionLogger();
-            }
-            mSessionLogger.writeEvent(event);
-        }
+        mSessionLogger.writeEvent(event);
     }
 
     @Override
@@ -350,6 +345,7 @@
         if (DEBUG) {
             Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
         }
+        mTextClassifierEventTronLogger.writeEvent(event);
     }
 
     /** @inheritDoc */
@@ -397,7 +393,7 @@
                     actionsImpl.suggestActions(nativeConversation, null);
 
             Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
-            List<ConversationActions.ConversationAction> conversationActions = new ArrayList<>();
+            List<ConversationAction> conversationActions = new ArrayList<>();
             int maxSuggestions = nativeSuggestions.length;
             if (request.getMaxSuggestions() > 0) {
                 maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
@@ -409,7 +405,7 @@
                     continue;
                 }
                 conversationActions.add(
-                        new ConversationActions.ConversationAction.Builder(actionType)
+                        new ConversationAction.Builder(actionType)
                                 .setTextReply(nativeSuggestion.getResponseText())
                                 .setConfidenceScore(nativeSuggestion.getScore())
                                 .build());
@@ -449,10 +445,10 @@
 
     private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
         List<String> defaultActionTypes =
-                request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
+                request.getHints().contains(ConversationActions.Request.HINT_FOR_NOTIFICATION)
                         ? mSettings.getNotificationConversationActionTypes()
                         : mSettings.getInAppConversationActionTypes();
-        return request.getTypeConfig().resolveTypes(defaultActionTypes);
+        return request.getTypeConfig().resolveEntityListModifications(defaultActionTypes);
     }
 
     private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 9733701..f553ca5 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -27,7 +27,6 @@
 
 import com.android.internal.textservice.ISpellCheckerSession;
 import com.android.internal.textservice.ISpellCheckerSessionListener;
-import com.android.internal.textservice.ITextServicesManager;
 import com.android.internal.textservice.ITextServicesSessionListener;
 
 import dalvik.system.CloseGuard;
@@ -96,7 +95,7 @@
     private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
 
     private final InternalListener mInternalListener;
-    private final ITextServicesManager mTextServicesManager;
+    private final TextServicesManager mTextServicesManager;
     private final SpellCheckerInfo mSpellCheckerInfo;
     @UnsupportedAppUsage
     private final SpellCheckerSessionListener mSpellCheckerSessionListener;
@@ -124,7 +123,7 @@
      * @hide
      */
     public SpellCheckerSession(
-            SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) {
+            SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener) {
         if (info == null || listener == null || tsm == null) {
             throw new NullPointerException();
         }
@@ -166,12 +165,8 @@
      */
     public void close() {
         mGuard.close();
-        try {
-            mSpellCheckerSessionListenerImpl.close();
-            mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl);
-        } catch (RemoteException e) {
-            // do nothing
-        }
+        mSpellCheckerSessionListenerImpl.close();
+        mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl);
     }
 
     /**
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 4c6862c..9ff64d9 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -16,16 +16,20 @@
 
 package android.view.textservice;
 
+import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 
+import com.android.internal.textservice.ISpellCheckerSessionListener;
 import com.android.internal.textservice.ITextServicesManager;
 
 import java.util.Locale;
@@ -68,22 +72,40 @@
     private static final boolean DBG = false;
 
     /**
-     * A compile time switch to control per-profile spell checker, which is not yet ready.
-     * @hide
+     * @deprecated Do not use. Just kept because of {@link UnsupportedAppUsage} in
+     * {@link #getInstance()}.
      */
-    public static final boolean DISABLE_PER_PROFILE_SPELL_CHECKER = true;
-
+    @Deprecated
     private static TextServicesManager sInstance;
 
     private final ITextServicesManager mService;
 
-    private TextServicesManager() throws ServiceNotFoundException {
+    @UserIdInt
+    private final int mUserId;
+
+    private TextServicesManager(@UserIdInt int userId) throws ServiceNotFoundException {
         mService = ITextServicesManager.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
+        mUserId = userId;
     }
 
     /**
-     * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
+     * The factory method of {@link TextServicesManager}.
+     *
+     * @param context {@link Context} from which {@link TextServicesManager} should be instantiated.
+     * @return {@link TextServicesManager} that is associated with {@link Context#getUserId()}.
+     * @throws ServiceNotFoundException When {@link TextServicesManager} is not available.
+     * @hide
+     */
+    @NonNull
+    public static TextServicesManager createInstance(@NonNull Context context)
+            throws ServiceNotFoundException {
+        return new TextServicesManager(context.getUserId());
+    }
+
+    /**
+     * @deprecated Do not use. Just kept because of {@link UnsupportedAppUsage} in
+     * {@link #getInstance()}.
      * @hide
      */
     @UnsupportedAppUsage
@@ -91,7 +113,7 @@
         synchronized (TextServicesManager.class) {
             if (sInstance == null) {
                 try {
-                    sInstance = new TextServicesManager();
+                    sInstance = new TextServicesManager(UserHandle.myUserId());
                 } catch (ServiceNotFoundException e) {
                     throw new IllegalStateException(e);
                 }
@@ -142,7 +164,7 @@
 
         final SpellCheckerInfo sci;
         try {
-            sci = mService.getCurrentSpellChecker(null);
+            sci = mService.getCurrentSpellChecker(mUserId, null);
         } catch (RemoteException e) {
             return null;
         }
@@ -180,9 +202,9 @@
         if (subtypeInUse == null) {
             return null;
         }
-        final SpellCheckerSession session = new SpellCheckerSession(sci, mService, listener);
+        final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener);
         try {
-            mService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
+            mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(),
                     session.getTextServicesSessionListener(),
                     session.getSpellCheckerSessionListener(), bundle);
         } catch (RemoteException e) {
@@ -197,7 +219,7 @@
     @UnsupportedAppUsage
     public SpellCheckerInfo[] getEnabledSpellCheckers() {
         try {
-            final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers();
+            final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers(mUserId);
             if (DBG) {
                 Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
             }
@@ -214,7 +236,7 @@
     public SpellCheckerInfo getCurrentSpellChecker() {
         try {
             // Passing null as a locale for ICS
-            return mService.getCurrentSpellChecker(null);
+            return mService.getCurrentSpellChecker(mUserId, null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -227,7 +249,7 @@
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
             boolean allowImplicitlySelectedSubtype) {
         try {
-            return mService.getCurrentSpellCheckerSubtype(allowImplicitlySelectedSubtype);
+            return mService.getCurrentSpellCheckerSubtype(mUserId, allowImplicitlySelectedSubtype);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -239,7 +261,15 @@
     @UnsupportedAppUsage
     public boolean isSpellCheckerEnabled() {
         try {
-            return mService.isSpellCheckerEnabled();
+            return mService.isSpellCheckerEnabled(mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    void finishSpellCheckerService(ISpellCheckerSessionListener listener) {
+        try {
+            mService.finishSpellCheckerService(mUserId, listener);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index bad2dbf..6039350 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -69,6 +70,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * A View that displays web pages.
@@ -1688,6 +1690,84 @@
         return mProvider.getWebViewClient();
     }
 
+
+    /**
+     * Gets the WebView renderer associated with this WebView.
+     *
+     * <p>In {@link android.os.Build.VERSION_CODES#O} and above, WebView may
+     * run in "multiprocess" mode. In multiprocess mode, rendering of web
+     * content is performed by a sandboxed renderer process separate to the
+     * application process.  This renderer process may be shared with other
+     * WebViews in the application, but is not shared with other application
+     * processes.
+     *
+     * <p>If WebView is running in multiprocess mode, this method returns a
+     * handle to the renderer process associated with the WebView, which can
+     * be used to control the renderer process.
+     *
+     * @return the {@link WebViewRenderer} renderer handle associated
+     *         with this {@link WebView}, or {@code null} if
+     *         WebView is not runing in multiprocess mode.
+     */
+    @Nullable
+    public WebViewRenderer getWebViewRenderer() {
+        checkThread();
+        return mProvider.getWebViewRenderer();
+    }
+
+    /**
+     * Sets the renderer client object associated with this WebView.
+     *
+     * <p>The renderer client encapsulates callbacks relevant to WebView renderer
+     * state. See {@link WebViewRendererClient} for details.
+     *
+     * <p>Although many WebView instances may share a single underlying
+     * renderer, and renderers may live either in the application
+     * process, or in a sandboxed process that is isolated from the
+     * application process, instances of {@link WebViewRendererClient}
+     * are set per-WebView.  Callbacks represent renderer events from
+     * the perspective of this WebView, and may or may not be correlated
+     * with renderer events affecting other WebViews.
+     *
+     * @param executor the Executor on which {@link WebViewRendererClient} callbacks will execute.
+     * @param webViewRendererClient the {@link WebViewRendererClient} object.
+     */
+    public void setWebViewRendererClient(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WebViewRendererClient webViewRendererClient) {
+        checkThread();
+        mProvider.setWebViewRendererClient(executor, webViewRendererClient);
+    }
+
+    /**
+     * Sets the renderer client object associated with this WebView.
+     *
+     * See {@link #setWebViewRendererClient(Executor,WebViewRendererClient)} for details.
+     *
+     * <p> {@link WebViewRendererClient} callbacks will run on the thread that this WebView was
+     * initialized on.
+     *
+     * @param webViewRendererClient the {@link WebViewRendererClient} object.
+     */
+    public void setWebViewRendererClient(
+            @Nullable WebViewRendererClient webViewRendererClient) {
+        checkThread();
+        mProvider.setWebViewRendererClient(null, webViewRendererClient);
+    }
+
+    /**
+     * Gets the renderer client object associated with this WebView.
+     *
+     * @return the {@link WebViewRendererClient} object associated with this WebView, if one has
+     * been set via {@link #setWebViewRendererClient(WebViewRendererClient)} or {@code null}
+     * otherwise.
+     */
+    @Nullable
+    public WebViewRendererClient getWebViewRendererClient() {
+        checkThread();
+        return mProvider.getWebViewRendererClient();
+    }
+
     /**
      * Registers the interface to be used when content can not be handled by
      * the rendering engine, and should be downloaded instead. This will replace
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 95e7a986..baf5826 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -54,6 +54,7 @@
 import java.io.BufferedWriter;
 import java.io.File;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * WebView backend provider interface: this interface is the abstract backend to a WebView
@@ -237,6 +238,14 @@
 
     public WebViewClient getWebViewClient();
 
+    public WebViewRenderer getWebViewRenderer();
+
+    public void setWebViewRendererClient(
+            @Nullable Executor executor,
+            @Nullable WebViewRendererClient client);
+
+    public WebViewRendererClient getWebViewRendererClient();
+
     public void setDownloadListener(DownloadListener listener);
 
     public void setWebChromeClient(WebChromeClient client);
diff --git a/core/java/android/webkit/WebViewRenderer.java b/core/java/android/webkit/WebViewRenderer.java
new file mode 100644
index 0000000..5328254
--- /dev/null
+++ b/core/java/android/webkit/WebViewRenderer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 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.webkit;
+
+/**
+ * WebViewRenderer provides an opaque handle to a {@link WebView} renderer.
+ */
+public abstract class WebViewRenderer {
+    /**
+     * Cause this renderer to terminate.
+     *
+     * <p>Calling this on a not yet started, or an already terminated renderer will have no effect.
+     *
+     * <p>Terminating a renderer process may have an effect on multiple {@link WebView} instances.
+     *
+     * <p>Renderer termination must be handled by properly overriding
+     * {@link WebViewClient#onRenderProcessGone} for every WebView that shares this
+     * renderer. If termination is not handled by all associated WebViews, then the application
+     * process will also be terminated.
+     *
+     * @return {@code true} if it was possible to terminate this renderer, {@code false} otherwise.
+     */
+    public abstract boolean terminate();
+
+    /**
+     * This class cannot be created by applications.
+     * @hide
+     */
+    public WebViewRenderer() {
+    }
+}
diff --git a/core/java/android/webkit/WebViewRendererClient.java b/core/java/android/webkit/WebViewRendererClient.java
new file mode 100644
index 0000000..2fadf54
--- /dev/null
+++ b/core/java/android/webkit/WebViewRendererClient.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 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.webkit;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Used to receive callbacks on {@link WebView} renderer events.
+ *
+ * WebViewRendererClient instances may be set or retrieved via {@link
+ * WebView#setWebViewRendererClient(WebViewRendererClient)} and {@link
+ * WebView#getWebViewRendererClient()}.
+ *
+ * Instances may be attached to multiple WebViews, and thus a single renderer event may cause
+ * a callback to be called multiple times with different WebView parameters.
+ */
+public abstract class WebViewRendererClient {
+    /**
+     * Called when the renderer currently associated with {@code view} becomes unresponsive as a
+     * result of a long running blocking task such as the execution of JavaScript.
+     *
+     * <p>If a WebView fails to process an input event, or successfully navigate to a new URL within
+     * a reasonable time frame, the renderer is considered to be unresponsive, and this callback
+     * will be called.
+     *
+     * <p>This callback will continue to be called at regular intervals as long as the renderer
+     * remains unresponsive. If the renderer becomes responsive again, {@link
+     * WebViewRendererClient#onRendererResponsive} will be called once, and this method will not
+     * subsequently be called unless another period of unresponsiveness is detected.
+     *
+     * <p>No action is taken by WebView as a result of this method call. Applications may
+     * choose to terminate the associated renderer via the object that is passed to this callback,
+     * if in multiprocess mode, however this must be accompanied by correctly handling
+     * {@link WebViewClient#onRenderProcessGone} for this WebView, and all other WebViews associated
+     * with the same renderer. Failure to do so will result in application termination.
+     *
+     * @param view The {@link WebView} for which unresponsiveness was detected.
+     * @param renderer The {@link WebViewRenderer} that has become unresponsive,
+     * or {@code null} if WebView is running in single process mode.
+     */
+    public abstract void onRendererUnresponsive(
+            @NonNull WebView view, @Nullable WebViewRenderer renderer);
+
+    /**
+     * Called once when an unresponsive renderer currently associated with {@code view} becomes
+     * responsive.
+     *
+     * <p>After a WebView renderer becomes unresponsive, which is notified to the application by
+     * {@link WebViewRendererClient#onRendererUnresponsive}, it is possible for the blocking
+     * renderer task to complete, returning the renderer to a responsive state. In that case,
+     * this method is called once to indicate responsiveness.
+     *
+     * <p>No action is taken by WebView as a result of this method call.
+     *
+     * @param view The {@link WebView} for which responsiveness was detected.
+     *
+     * @param renderer The {@link WebViewRenderer} that has become responsive, or {@code null} if
+     * WebView is running in single process mode.
+     */
+    public abstract void onRendererResponsive(
+            @NonNull WebView view, @Nullable WebViewRenderer renderer);
+}
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 49e11b8..29b3b3c 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -159,7 +159,10 @@
                     0,  // runtimeFlags
                     "webview_zygote",  // seInfo
                     sPackage.applicationInfo.primaryCpuAbi,  // abi
-                    null);  // instructionSet
+                    TextUtils.join(",", Build.SUPPORTED_ABIS),
+                    null, // instructionSet
+                    Process.FIRST_ISOLATED_UID,
+                    Process.LAST_ISOLATED_UID);
 
             // All the work below is usually done by LoadedApk, but the zygote can't talk to
             // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 7d02757..afe46701 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -104,8 +104,9 @@
     private final int mDefaultHorizontalSourceToMagnifierOffset;
     // The vertical offset between the source and window coords when #show(float, float) is used.
     private final int mDefaultVerticalSourceToMagnifierOffset;
-    // Whether the magnifier will be clamped inside the main surface and not overlap system insets.
-    private final boolean mForcePositionWithinWindowSystemInsetsBounds;
+    // Whether the area where the magnifier can be positioned will be clipped to the main window
+    // and within system insets.
+    private final boolean mClippingEnabled;
     // The behavior of the left bound of the rectangle where the content can be copied from.
     private @SourceBound int mLeftContentBound;
     // The behavior of the top bound of the rectangle where the content can be copied from.
@@ -165,7 +166,7 @@
         params.mOverlay = new ColorDrawable(a.getColor(
                 R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
         a.recycle();
-        params.mForcePositionWithinWindowSystemInsetsBounds = true;
+        params.mClippingEnabled = true;
         params.mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
         params.mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
         params.mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
@@ -203,8 +204,7 @@
                 params.mHorizontalDefaultSourceToMagnifierOffset;
         mDefaultVerticalSourceToMagnifierOffset =
                 params.mVerticalDefaultSourceToMagnifierOffset;
-        mForcePositionWithinWindowSystemInsetsBounds =
-                params.mForcePositionWithinWindowSystemInsetsBounds;
+        mClippingEnabled = params.mClippingEnabled;
         mLeftContentBound = params.mLeftContentBound;
         mTopContentBound = params.mTopContentBound;
         mRightContentBound = params.mRightContentBound;
@@ -271,7 +271,7 @@
             if (mWindow == null) {
                 synchronized (mLock) {
                     mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
-                            mParentSurface.mSurface, mWindowWidth, mWindowHeight,
+                            mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight,
                             mWindowElevation, mWindowCornerRadius,
                             mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
                             Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
@@ -447,7 +447,7 @@
     }
 
     /**
-     * Returns the overlay to be drawn on the top of the magnifier content, or
+     * Returns the overlay to be drawn on the top of the magnifier, or
      * {@code null} if no overlay should be drawn.
      * @return the overlay
      * @see Magnifier.Builder#setOverlay(Drawable)
@@ -459,13 +459,15 @@
 
     /**
      * Returns whether the magnifier position will be adjusted such that the magnifier will be
-     * fully within the bounds of the main application window, by also avoiding any overlap with
-     * system insets (such as the one corresponding to the status bar).
+     * fully within the bounds of the main application window, by also avoiding any overlap
+     * with system insets (such as the one corresponding to the status bar) i.e. whether the
+     * area where the magnifier can be positioned will be clipped to the main application window
+     * and the system insets.
      * @return whether the magnifier position will be adjusted
-     * @see Magnifier.Builder#setForcePositionWithinWindowSystemInsetsBounds(boolean)
+     * @see Magnifier.Builder#setClippingEnabled(boolean)
      */
-    public boolean isForcePositionWithinWindowSystemInsetsBounds() {
-        return mForcePositionWithinWindowSystemInsetsBounds;
+    public boolean isClippingEnabled() {
+        return mClippingEnabled;
     }
 
     /**
@@ -528,17 +530,20 @@
                 final int surfaceHeight =
                         viewRootImpl.getHeight() + surfaceInsets.top + surfaceInsets.bottom;
                 validMainWindowSurface =
-                        new SurfaceInfo(mainWindowSurface, surfaceWidth, surfaceHeight, true);
+                        new SurfaceInfo(viewRootImpl.getSurfaceControl(), mainWindowSurface,
+                                surfaceWidth, surfaceHeight, true);
             }
         }
         // Get the surface backing the magnified view, if it is a SurfaceView.
         SurfaceInfo validSurfaceViewSurface = SurfaceInfo.NULL;
         if (mView instanceof SurfaceView) {
+            final SurfaceControl sc = ((SurfaceView) mView).getSurfaceControl();
             final SurfaceHolder surfaceHolder = ((SurfaceView) mView).getHolder();
             final Surface surfaceViewSurface = surfaceHolder.getSurface();
-            if (surfaceViewSurface != null && surfaceViewSurface.isValid()) {
+
+            if (sc != null && sc.isValid()) {
                 final Rect surfaceFrame = surfaceHolder.getSurfaceFrame();
-                validSurfaceViewSurface = new SurfaceInfo(surfaceViewSurface,
+                validSurfaceViewSurface = new SurfaceInfo(sc, surfaceViewSurface,
                         surfaceFrame.right, surfaceFrame.bottom, false);
             }
         }
@@ -708,7 +713,7 @@
      * @return the current window coordinates, after they are clamped inside the parent surface
      */
     private Point getCurrentClampedWindowCoordinates() {
-        if (!mForcePositionWithinWindowSystemInsetsBounds) {
+        if (!mClippingEnabled) {
             // No position adjustment should be done, so return the raw coordinates.
             return new Point(mWindowCoords);
         }
@@ -733,15 +738,18 @@
      * Contains a surface and metadata corresponding to it.
      */
     private static class SurfaceInfo {
-        public static final SurfaceInfo NULL = new SurfaceInfo(null, 0, 0, false);
+        public static final SurfaceInfo NULL = new SurfaceInfo(null, null, 0, 0, false);
 
         private Surface mSurface;
+        private SurfaceControl mSurfaceControl;
         private int mWidth;
         private int mHeight;
         private boolean mIsMainWindowSurface;
 
-        SurfaceInfo(final Surface surface, final int width, final int height,
+        SurfaceInfo(final SurfaceControl surfaceControl, final Surface surface,
+                final int width, final int height,
                 final boolean isMainWindowSurface) {
+            mSurfaceControl = surfaceControl;
             mSurface = surface;
             mWidth = width;
             mHeight = height;
@@ -819,7 +827,7 @@
         private Bitmap mCurrentContent;
 
         InternalPopupWindow(final Context context, final Display display,
-                final Surface parentSurface, final int width, final int height,
+                final SurfaceControl parentSurfaceControl, final int width, final int height,
                 final float elevation, final float cornerRadius, final Drawable overlay,
                 final Handler handler, final Object lock, final Callback callback) {
             mDisplay = display;
@@ -829,17 +837,18 @@
 
             mContentWidth = width;
             mContentHeight = height;
-            mOffsetX = (int) (0.1f * width);
-            mOffsetY = (int) (0.1f * height);
+            mOffsetX = (int) (1.05f * elevation);
+            mOffsetY = (int) (1.05f * elevation);
             // Setup the surface we will use for drawing the content and shadow.
             mSurfaceWidth = mContentWidth + 2 * mOffsetX;
             mSurfaceHeight = mContentHeight + 2 * mOffsetY;
-            mSurfaceSession = new SurfaceSession(parentSurface);
+            mSurfaceSession = new SurfaceSession();
             mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                     .setName("magnifier surface")
                     .setFlags(SurfaceControl.HIDDEN)
+                    .setParent(parentSurfaceControl)
                     .build();
             mSurface = new Surface();
             mSurface.copyFrom(mSurfaceControl);
@@ -1129,7 +1138,7 @@
         private @Nullable Drawable mOverlay;
         private int mHorizontalDefaultSourceToMagnifierOffset;
         private int mVerticalDefaultSourceToMagnifierOffset;
-        private boolean mForcePositionWithinWindowSystemInsetsBounds;
+        private boolean mClippingEnabled;
         private @SourceBound int mLeftContentBound;
         private @SourceBound int mTopContentBound;
         private @SourceBound int mRightContentBound;
@@ -1157,7 +1166,7 @@
                     resources.getDimensionPixelSize(R.dimen.default_magnifier_vertical_offset);
             mOverlay = new ColorDrawable(resources.getColor(
                     R.color.default_magnifier_color_overlay, null));
-            mForcePositionWithinWindowSystemInsetsBounds = true;
+            mClippingEnabled = true;
             mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
             mTopContentBound = SOURCE_BOUND_MAX_VISIBLE;
             mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
@@ -1220,11 +1229,11 @@
         }
 
         /**
-         * Sets an overlay that will be drawn on the top of the magnifier content.
-         * In general, the overlay should not be opaque, in order to let the expected magnifier
-         * content be partially visible. The default overlay is {@code null} (no overlay).
-         * As an example, TextView applies a white {@link ColorDrawable} overlay with
-         * 5% alpha, aiming to make the magnifier distinguishable when shown in dark
+         * Sets an overlay that will be drawn on the top of the magnifier.
+         * In general, the overlay should not be opaque, in order to let the magnified
+         * content be partially visible in the magnifier. The default overlay is {@code null}
+         * (no overlay). As an example, TextView applies a white {@link ColorDrawable}
+         * overlay with 5% alpha, aiming to make the magnifier distinguishable when shown in dark
          * application regions. To disable the overlay, the parameter should be set
          * to {@code null}. If not null, the overlay will be automatically redrawn
          * when the drawable is invalidated. To achieve this, the magnifier will set a new
@@ -1258,22 +1267,24 @@
          * Defines the behavior of the magnifier when it is requested to position outside the
          * surface of the main application window. The default value is {@code true}, which means
          * that the position will be adjusted such that the magnifier will be fully within the
-         * bounds of the main application window, by also avoiding any overlap with system insets
-         * (such as the one corresponding to the status bar). If you require a custom behavior, this
-         * flag should be set to {@code false}, meaning that the magnifier will be able to cross the
-         * main application surface boundaries (and also overlap the system insets). This should be
-         * handled with care, when passing coordinates to {@link #show(float, float)}; note that:
+         * bounds of the main application window, while also avoiding any overlap with system insets
+         * (such as the one corresponding to the status bar). If this flag is set to {@code false},
+         * the area where the magnifier can be positioned will no longer be clipped, so the
+         * magnifier will be able to extend outside the main application window boundaries (and also
+         * overlap the system insets). This can be useful if you require a custom behavior, but it
+         * should be handled with care, when passing coordinates to {@link #show(float, float)};
+         * note that:
          * <ul>
          *   <li>in a multiwindow context, if the magnifier crosses the boundary between the two
          *   windows, it will not be able to show over the window of the other application</li>
          *   <li>if the magnifier overlaps the status bar, there is no guarantee about which one
          *   will be displayed on top. This should be handled with care.</li>
          * </ul>
-         * @param force whether the magnifier position will be adjusted
+         * @param clip whether the magnifier position will be adjusted
          */
         @NonNull
-        public Builder setForcePositionWithinWindowSystemInsetsBounds(boolean force) {
-            mForcePositionWithinWindowSystemInsetsBounds = force;
+        public Builder setClippingEnabled(boolean clip) {
+            mClippingEnabled = clip;
             return this;
         }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0ea8579..4a60954 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -48,7 +48,6 @@
 import android.content.res.ColorStateList;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -889,7 +888,7 @@
     // sanitize autofill requests.
     private boolean mTextSetFromXmlOrResourceId = false;
     // Resource id used to set the text.
-    private @StringRes int mTextId = ResourceId.ID_NULL;
+    private @StringRes int mTextId = Resources.ID_NULL;
     //
     // End of autofill-related attributes
 
@@ -1180,7 +1179,7 @@
 
                 case com.android.internal.R.styleable.TextView_text:
                     textIsSetFromXml = true;
-                    mTextId = a.getResourceId(attr, ResourceId.ID_NULL);
+                    mTextId = a.getResourceId(attr, Resources.ID_NULL);
                     text = a.getText(attr);
                     break;
 
@@ -3520,7 +3519,7 @@
      */
     @android.view.RemotableViewMethod
     public void setTextSelectHandle(@DrawableRes int textSelectHandle) {
-        Preconditions.checkArgumentPositive(textSelectHandle,
+        Preconditions.checkArgument(textSelectHandle != 0,
                 "The text select handle should be a valid drawable resource id.");
         setTextSelectHandle(mContext.getDrawable(textSelectHandle));
     }
@@ -3577,7 +3576,7 @@
      */
     @android.view.RemotableViewMethod
     public void setTextSelectHandleLeft(@DrawableRes int textSelectHandleLeft) {
-        Preconditions.checkArgumentPositive(textSelectHandleLeft,
+        Preconditions.checkArgument(textSelectHandleLeft != 0,
                 "The text select left handle should be a valid drawable resource id.");
         setTextSelectHandleLeft(mContext.getDrawable(textSelectHandleLeft));
     }
@@ -3634,7 +3633,7 @@
      */
     @android.view.RemotableViewMethod
     public void setTextSelectHandleRight(@DrawableRes int textSelectHandleRight) {
-        Preconditions.checkArgumentPositive(textSelectHandleRight,
+        Preconditions.checkArgument(textSelectHandleRight != 0,
                 "The text select right handle should be a valid drawable resource id.");
         setTextSelectHandleRight(mContext.getDrawable(textSelectHandleRight));
     }
@@ -3667,9 +3666,7 @@
      * @see #setTextCursorDrawable(int)
      * @attr ref android.R.styleable#TextView_textCursorDrawable
      */
-    public void setTextCursorDrawable(@NonNull Drawable textCursorDrawable) {
-        Preconditions.checkNotNull(textCursorDrawable,
-                "The cursor drawable should not be null.");
+    public void setTextCursorDrawable(@Nullable Drawable textCursorDrawable) {
         mCursorDrawable = textCursorDrawable;
         mCursorDrawableRes = 0;
         if (mEditor != null) {
@@ -3687,9 +3684,8 @@
      * @attr ref android.R.styleable#TextView_textCursorDrawable
      */
     public void setTextCursorDrawable(@DrawableRes int textCursorDrawable) {
-        Preconditions.checkArgumentPositive(textCursorDrawable,
-                "The cursor drawable should be a valid drawable resource id.");
-        setTextCursorDrawable(mContext.getDrawable(textCursorDrawable));
+        setTextCursorDrawable(
+                textCursorDrawable != 0 ? mContext.getDrawable(textCursorDrawable) : null);
     }
 
     /**
@@ -8061,6 +8057,26 @@
                     }
                 }
                 break;
+
+            case KeyEvent.KEYCODE_FORWARD_DEL:
+                if (event.hasModifiers(KeyEvent.META_SHIFT_ON) && canCut()) {
+                    if (onTextContextMenuItem(ID_CUT)) {
+                        return KEY_EVENT_HANDLED;
+                    }
+                }
+                break;
+
+            case KeyEvent.KEYCODE_INSERT:
+                if (event.hasModifiers(KeyEvent.META_CTRL_ON) && canCopy()) {
+                    if (onTextContextMenuItem(ID_COPY)) {
+                        return KEY_EVENT_HANDLED;
+                    }
+                } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON) && canPaste()) {
+                    if (onTextContextMenuItem(ID_PASTE)) {
+                        return KEY_EVENT_HANDLED;
+                    }
+                }
+                break;
         }
 
         if (mEditor != null && mEditor.mKeyListener != null) {
@@ -10831,25 +10847,6 @@
                         return onTextContextMenuItem(ID_PASTE);
                     }
                     break;
-                case KeyEvent.KEYCODE_INSERT:
-                    if (canCopy()) {
-                        return onTextContextMenuItem(ID_COPY);
-                    }
-                    break;
-            }
-        } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
-            // Handle Shift-only shortcuts.
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_FORWARD_DEL:
-                    if (canCut()) {
-                        return onTextContextMenuItem(ID_CUT);
-                    }
-                    break;
-                case KeyEvent.KEYCODE_INSERT:
-                    if (canPaste()) {
-                        return onTextContextMenuItem(ID_PASTE);
-                    }
-                    break;
             }
         } else if (event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)) {
             // Handle Ctrl-Shift shortcuts.
@@ -11033,7 +11030,7 @@
             if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
                 structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
             }
-            if (mTextId != ResourceId.ID_NULL) {
+            if (mTextId != Resources.ID_NULL) {
                 try {
                     structure.setTextIdEntry(getResources().getResourceEntryName(mTextId));
                 } catch (Resources.NotFoundException e) {
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 6a3a8f0..dc9a585 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -454,6 +454,7 @@
                 (RelativeLayout.LayoutParams) mAmPmLayout.getLayoutParams();
         if (params.getRule(RelativeLayout.RIGHT_OF) != 0
                 || params.getRule(RelativeLayout.LEFT_OF) != 0) {
+            final int margin = (int) (mContext.getResources().getDisplayMetrics().density * 8);
             // Horizontal mode, with AM/PM appearing to left/right of hours and minutes.
             final boolean isAmPmAtLeft;
             if (TextUtils.getLayoutDirectionFromLocale(mLocale) == View.LAYOUT_DIRECTION_LTR) {
@@ -461,10 +462,6 @@
             } else {
                 isAmPmAtLeft = !isAmPmAtStart;
             }
-            if (mIsAmPmAtLeft == isAmPmAtLeft) {
-                // AM/PM is already at the correct location. No change needed.
-                return;
-            }
 
             if (isAmPmAtLeft) {
                 params.removeRule(RelativeLayout.RIGHT_OF);
@@ -473,6 +470,14 @@
                 params.removeRule(RelativeLayout.LEFT_OF);
                 params.addRule(RelativeLayout.RIGHT_OF, mMinuteView.getId());
             }
+
+            if (isAmPmAtStart) {
+                params.setMarginStart(0);
+                params.setMarginEnd(margin);
+            } else {
+                params.setMarginStart(margin);
+                params.setMarginEnd(0);
+            }
             mIsAmPmAtLeft = isAmPmAtLeft;
         } else if (params.getRule(RelativeLayout.BELOW) != 0
                 || params.getRule(RelativeLayout.ABOVE) != 0) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 4523d3b..b4d8322 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -21,19 +21,24 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.LabeledIntent;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.database.DataSetObserver;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -75,6 +80,7 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -92,6 +98,15 @@
 
     private static final boolean DEBUG = false;
 
+    /**
+     * If set to true, use ShortcutManager to retrieve the matching direct share targets, instead of
+     * binding to every ChooserTargetService implementation.
+     */
+    // TODO(b/121287573): Replace with a system flag (setprop?)
+    private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = false;
+    // TODO(b/121287224): Re-evaluate this limit
+    private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
+
     private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
     private static final int WATCHDOG_TIMEOUT_MILLIS = 2000;
 
@@ -120,6 +135,7 @@
 
     private static final int CHOOSER_TARGET_SERVICE_RESULT = 1;
     private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2;
+    private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 3;
 
     private final Handler mChooserHandler = new Handler() {
         @Override
@@ -158,6 +174,18 @@
                     mChooserListAdapter.setShowServiceTargets(true);
                     break;
 
+                case SHORTCUT_MANAGER_SHARE_TARGET_RESULT:
+                    if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT");
+                    if (isDestroyed()) break;
+                    final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
+                    if (resultInfo.resultTargets != null) {
+                        mChooserListAdapter.addServiceResults(resultInfo.originalTarget,
+                                resultInfo.resultTargets);
+                    }
+                    sendVoiceChoicesIfNeeded();
+                    mChooserListAdapter.setShowServiceTargets(true);
+                    break;
+
                 default:
                     super.handleMessage(msg);
             }
@@ -552,6 +580,94 @@
         }
     }
 
+    private IntentFilter getTargetIntentFilter() {
+        try {
+            final Intent intent = getTargetIntent();
+            String dataString = intent.getDataString();
+            if (TextUtils.isEmpty(dataString)) {
+                dataString = intent.getType();
+            }
+            return new IntentFilter(intent.getAction(), dataString);
+        } catch (Exception e) {
+            Log.e(TAG, "failed to get target intent filter " + e);
+            return null;
+        }
+    }
+
+    private void queryDirectShareTargets(ChooserListAdapter adapter) {
+        final IntentFilter filter = getTargetIntentFilter();
+        if (filter == null) {
+            return;
+        }
+
+        // Need to keep the original DisplayResolveInfos to be able to reconstruct ServiceResultInfo
+        // and use the old code path. This Ugliness should go away when Sharesheet is refactored.
+        final List<DisplayResolveInfo> driList = new ArrayList<>();
+        int targetsToQuery = 0;
+        for (int i = 0, n = adapter.getDisplayResolveInfoCount(); i < n; i++) {
+            final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
+            if (adapter.getScore(dri) == 0) {
+                // A score of 0 means the app hasn't been used in some time;
+                // don't query it as it's not likely to be relevant.
+                continue;
+            }
+            driList.add(dri);
+            targetsToQuery++;
+            // TODO(b/121287224): Do we need this here? (similar to queryTargetServices)
+            if (targetsToQuery >= SHARE_TARGET_QUERY_PACKAGE_LIMIT) {
+                if (DEBUG) {
+                    Log.d(TAG, "queryTargets hit query target limit "
+                            + SHARE_TARGET_QUERY_PACKAGE_LIMIT);
+                }
+                break;
+            }
+        }
+
+        AsyncTask.execute(() -> {
+            ShortcutManager sm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);
+            List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter);
+
+            // Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path
+            // for direct share targets. After ShareSheet is refactored we should use the
+            // ShareShortcutInfos directly.
+            for (int i = 0; i < driList.size(); i++) {
+                List<ChooserTarget> chooserTargets = new ArrayList<>();
+                for (int j = 0; j < resultList.size(); j++) {
+                    if (driList.get(i).getResolvedComponentName().equals(
+                            resultList.get(j).getTargetComponent())) {
+                        chooserTargets.add(convertToChooserTarget(resultList.get(j)));
+                    }
+                }
+                if (chooserTargets.isEmpty()) {
+                    continue;
+                }
+
+                final Message msg = Message.obtain();
+                msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
+                msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null);
+                mChooserHandler.sendMessage(msg);
+            }
+        });
+    }
+
+    private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) {
+        ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+        Bundle extras = new Bundle();
+        extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
+        return new ChooserTarget(
+                // The name of this target.
+                shortcutInfo.getShortLabel(),
+                // Don't load the icon until it is selected to be shown
+                null,
+                // The ranking score for this target (0.0-1.0); the system will omit items with low
+                // scores when there are too many Direct Share items.
+                0.5f,
+                // The name of the component to be launched if this target is chosen.
+                shareShortcut.getTargetComponent().clone(),
+                // The extra values here will be merged into the Intent when this target is chosen.
+                extras);
+    }
+
     private String convertServiceName(String packageName, String serviceName) {
         if (TextUtils.isEmpty(serviceName)) {
             return null;
@@ -765,9 +881,8 @@
                     }
                 }
             }
-            final Icon icon = chooserTarget.getIcon();
-            // TODO do this in the background
-            mDisplayIcon = icon != null ? icon.loadDrawable(ChooserActivity.this) : null;
+            // TODO(b/121287224): do this in the background thread, and only for selected targets
+            mDisplayIcon = getChooserTargetIconDrawable(chooserTarget);
 
             if (sourceInfo != null) {
                 mBackupResolveInfo = null;
@@ -791,6 +906,39 @@
             mModifiedScore = other.mModifiedScore;
         }
 
+        /**
+         * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip
+         * the call to LauncherApps#getShortcuts(ShortcutQuery).
+         */
+        // TODO(121287224): Refactor code to apply the suggestion above
+        private Drawable getChooserTargetIconDrawable(ChooserTarget target) {
+            final Icon icon = target.getIcon();
+            if (icon != null) {
+                return icon.loadDrawable(ChooserActivity.this);
+            }
+            if (!USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) {
+                return null;
+            }
+
+            Bundle extras = target.getIntentExtras();
+            if (extras == null || !extras.containsKey(Intent.EXTRA_SHORTCUT_ID)) {
+                return null;
+            }
+            CharSequence shortcutId = extras.getCharSequence(Intent.EXTRA_SHORTCUT_ID);
+            LauncherApps launcherApps = (LauncherApps) getSystemService(
+                    Context.LAUNCHER_APPS_SERVICE);
+            final LauncherApps.ShortcutQuery q = new LauncherApps.ShortcutQuery();
+            q.setPackage(target.getComponentName().getPackageName());
+            q.setShortcutIds(Arrays.asList(shortcutId.toString()));
+            q.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC);
+            final List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(q, getUser());
+            if (shortcuts != null && shortcuts.size() > 0) {
+                return launcherApps.getShortcutIconDrawable(shortcuts.get(0), 0);
+            }
+
+            return null;
+        }
+
         public float getModifiedScore() {
             return mModifiedScore;
         }
@@ -1030,8 +1178,15 @@
                     mTargetsNeedPruning = true;
                 }
             }
-            if (DEBUG) Log.d(TAG, "List built querying services");
-            queryTargetServices(this);
+            if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) {
+                if (DEBUG) {
+                    Log.d(TAG, "querying direct share targets from ShortcutManager");
+                }
+                queryDirectShareTargets(this);
+            } else {
+                if (DEBUG) Log.d(TAG, "List built querying services");
+                queryTargetServices(this);
+            }
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index e59bee4..c4ab91f 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -17,8 +17,10 @@
 package com.android.internal.app;
 
 import android.app.AppOpsManager;
+import android.app.AppOpsManager;
 import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
+import android.os.RemoteCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
@@ -42,10 +44,15 @@
     int checkPackage(int uid, String packageName);
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
-    ParceledListSlice getAllHistoricalPackagesOps(in String[] ops,
-            long beginTimeMillis, long endTimeMillis);
-    AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int uid, String packageName,
-            in String[] ops, long beginTimeMillis, long endTimeMillis);
+    void getHistoricalOps(int uid, String packageName, in String[] ops, long beginTimeMillis,
+            long endTimeMillis, in RemoteCallback callback);
+    void getHistoricalOpsFromDiskRaw(int uid, String packageName, in String[] ops,
+            long beginTimeMillis, long endTimeMillis, in RemoteCallback callback);
+    void offsetHistory(long duration);
+    void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
+    void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
+    void resetHistoryParameters();
+    void clearHistory();
     List<AppOpsManager.PackageOps> getUidOps(int uid, in int[] ops);
     void setUidMode(int code, int uid, int mode);
     void setMode(int code, int uid, String packageName, int mode);
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 96d3baf..f61a03b 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -22,44 +22,36 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
-import android.content.SharedPreferences;
-import android.content.ServiceConnection;
 import android.metrics.LogMaker;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.storage.StorageManager;
 import android.os.UserHandle;
-import android.service.resolver.IResolverRankerService;
 import android.service.resolver.IResolverRankerResult;
+import android.service.resolver.IResolverRankerService;
 import android.service.resolver.ResolverRankerService;
 import android.service.resolver.ResolverTarget;
-import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
+
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import java.io.File;
-import java.lang.InterruptedException;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Ranks and compares packages based on usage stats.
@@ -90,6 +82,8 @@
 
     private final Collator mCollator;
     private final boolean mHttp;
+    // can be null if mHttp == false or current user has no default browser package
+    private final String mDefaultBrowserPackageName;
     private final PackageManager mPm;
     private final UsageStatsManager mUsm;
     private final Map<String, UsageStats> mStats;
@@ -184,6 +178,10 @@
         getContentAnnotations(intent);
         mAction = intent.getAction();
         mRankerServiceName = new ComponentName(mContext, this.getClass());
+
+        mDefaultBrowserPackageName = mHttp
+                ? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId())
+                : null;
     }
 
     // get annotations of content from intent.
@@ -312,7 +310,14 @@
         if (mHttp) {
             // Special case: we want filters that match URI paths/schemes to be
             // ordered before others.  This is for the case when opening URIs,
-            // to make native apps go above browsers.
+            // to make native apps go above browsers - except for 1 even more special case
+            // which is the default browser, as we want that to go above them all.
+            if (isDefaultBrowser(lhs)) {
+                return -1;
+            }
+            if (isDefaultBrowser(rhs)) {
+                return 1;
+            }
             final boolean lhsSpecific = ResolverActivity.isSpecificUriMatch(lhs.match);
             final boolean rhsSpecific = ResolverActivity.isSpecificUriMatch(rhs.match);
             if (lhsSpecific != rhsSpecific) {
@@ -419,6 +424,20 @@
         }
     }
 
+    private boolean isDefaultBrowser(ResolveInfo ri) {
+        // It makes sense to prefer the default browser
+        // only if the targeted user is the current user
+        if (ri.targetUserId != UserHandle.USER_CURRENT) {
+            return false;
+        }
+
+        if (ri.activityInfo.packageName != null
+                && ri.activityInfo.packageName.equals(mDefaultBrowserPackageName)) {
+            return true;
+        }
+        return false;
+    }
+
     // records metrics for evaluation.
     private void logMetrics(int selectedPos) {
         if (mRankerServiceName != null) {
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 498de53..70935d4 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.app;
 
-import static android.content.res.ResourceId.ID_NULL;
+import static android.content.res.Resources.ID_NULL;
 
 import android.Manifest;
 import android.app.AlertDialog;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 9b9b771..8e88c51 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -23,6 +23,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.service.procstats.ProcessStatsAvailablePagesProto;
 import android.service.procstats.ProcessStatsPackageProto;
 import android.service.procstats.ProcessStatsSectionProto;
 import android.text.format.DateFormat;
@@ -178,7 +179,7 @@
             {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 35;
+    private static final int PARCEL_VERSION = 36;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -237,10 +238,11 @@
     ArrayList<String> mIndexToCommonString;
 
     private static final Pattern sPageTypeRegex = Pattern.compile(
-            "^Node\\s+(\\d+),.*. type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
-    private final ArrayList<Integer> mPageTypeZones = new ArrayList<Integer>();
-    private final ArrayList<String> mPageTypeLabels = new ArrayList<String>();
-    private final ArrayList<int[]> mPageTypeSizes = new ArrayList<int[]>();
+            "^Node\\s+(\\d+),.* zone\\s+(\\w+),.* type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
+    private final ArrayList<Integer> mPageTypeNodes = new ArrayList<>();
+    private final ArrayList<String> mPageTypeZones = new ArrayList<>();
+    private final ArrayList<String> mPageTypeLabels = new ArrayList<>();
+    private final ArrayList<int[]> mPageTypeSizes = new ArrayList<>();
 
     public ProcessStats(boolean running) {
         mRunning = running;
@@ -621,6 +623,7 @@
         try {
             reader = new BufferedReader(new FileReader("/proc/pagetypeinfo"));
             final Matcher matcher = sPageTypeRegex.matcher("");
+            mPageTypeNodes.clear();
             mPageTypeZones.clear();
             mPageTypeLabels.clear();
             mPageTypeSizes.clear();
@@ -631,16 +634,18 @@
                 }
                 matcher.reset(line);
                 if (matcher.matches()) {
-                    final Integer zone = Integer.valueOf(matcher.group(1), 10);
-                    if (zone == null) {
+                    final Integer node = Integer.valueOf(matcher.group(1), 10);
+                    if (node == null) {
                         continue;
                     }
-                    mPageTypeZones.add(zone);
-                    mPageTypeLabels.add(matcher.group(2));
-                    mPageTypeSizes.add(splitAndParseNumbers(matcher.group(3)));
+                    mPageTypeNodes.add(node);
+                    mPageTypeZones.add(matcher.group(2));
+                    mPageTypeLabels.add(matcher.group(3));
+                    mPageTypeSizes.add(splitAndParseNumbers(matcher.group(4)));
                 }
             }
         } catch (IOException ex) {
+            mPageTypeNodes.clear();
             mPageTypeZones.clear();
             mPageTypeLabels.clear();
             mPageTypeSizes.clear();
@@ -935,7 +940,8 @@
         final int NPAGETYPES = mPageTypeLabels.size();
         out.writeInt(NPAGETYPES);
         for (int i=0; i<NPAGETYPES; i++) {
-            out.writeInt(mPageTypeZones.get(i));
+            out.writeInt(mPageTypeNodes.get(i));
+            out.writeString(mPageTypeZones.get(i));
             out.writeString(mPageTypeLabels.get(i));
             out.writeIntArray(mPageTypeSizes.get(i));
         }
@@ -1244,6 +1250,8 @@
 
         // Fragmentation info
         final int NPAGETYPES = in.readInt();
+        mPageTypeNodes.clear();
+        mPageTypeNodes.ensureCapacity(NPAGETYPES);
         mPageTypeZones.clear();
         mPageTypeZones.ensureCapacity(NPAGETYPES);
         mPageTypeLabels.clear();
@@ -1251,7 +1259,8 @@
         mPageTypeSizes.clear();
         mPageTypeSizes.ensureCapacity(NPAGETYPES);
         for (int i=0; i<NPAGETYPES; i++) {
-            mPageTypeZones.add(in.readInt());
+            mPageTypeNodes.add(in.readInt());
+            mPageTypeZones.add(in.readString());
             mPageTypeLabels.add(in.readString());
             mPageTypeSizes.add(in.createIntArray());
         }
@@ -1764,7 +1773,8 @@
         pw.println("Available pages by page size:");
         final int NPAGETYPES = mPageTypeLabels.size();
         for (int i=0; i<NPAGETYPES; i++) {
-            pw.format("Zone %3d  %14s ", mPageTypeZones.get(i), mPageTypeLabels.get(i));
+            pw.format("Node %3d Zone %7s  %14s ", mPageTypeNodes.get(i), mPageTypeZones.get(i),
+                    mPageTypeLabels.get(i));
             final int[] sizes = mPageTypeSizes.get(i);
             final int N = sizes == null ? 0 : sizes.length;
             for (int j=0; j<N; j++) {
@@ -2095,6 +2105,9 @@
             pw.print(",");
             pw.print(mPageTypeZones.get(i));
             pw.print(",");
+            // Wasn't included in original output.
+            //pw.print(mPageTypeNodes.get(i));
+            //pw.print(",");
             final int[] sizes = mPageTypeSizes.get(i);
             final int N = sizes == null ? 0 : sizes.length;
             for (int j=0; j<N; j++) {
@@ -2135,6 +2148,20 @@
             proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
         }
 
+        final int NPAGETYPES = mPageTypeLabels.size();
+        for (int i = 0; i < NPAGETYPES; i++) {
+            final long token = proto.start(ProcessStatsSectionProto.AVAILABLE_PAGES);
+            proto.write(ProcessStatsAvailablePagesProto.NODE, mPageTypeNodes.get(i));
+            proto.write(ProcessStatsAvailablePagesProto.ZONE, mPageTypeZones.get(i));
+            proto.write(ProcessStatsAvailablePagesProto.LABEL, mPageTypeLabels.get(i));
+            final int[] sizes = mPageTypeSizes.get(i);
+            final int N = sizes == null ? 0 : sizes.length;
+            for (int j = 0; j < N; j++) {
+                proto.write(ProcessStatsAvailablePagesProto.PAGES_PER_ORDER, sizes[j]);
+            }
+            proto.end(token);
+        }
+
         final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
         if ((section & REPORT_PROC_STATS) != 0) {
             for (int ip = 0; ip < procMap.size(); ip++) {
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 2995a8f..7a00a51 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -58,6 +58,15 @@
         return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup);
     }
 
+    public boolean tapGestureEnabled(int user) {
+        return boolSettingDefaultOn(Settings.Secure.DOZE_TAP_SCREEN_GESTURE, user)
+                && tapSensorAvailable();
+    }
+
+    public boolean tapSensorAvailable() {
+        return !TextUtils.isEmpty(tapSensorType());
+    }
+
     public boolean doubleTapGestureEnabled(int user) {
         return boolSettingDefaultOn(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, user)
                 && doubleTapSensorAvailable();
@@ -86,6 +95,10 @@
         return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
     }
 
+    public String tapSensorType() {
+        return mContext.getResources().getString(R.string.config_dozeTapSensorType);
+    }
+
     public String longPressSensorType() {
         return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
     }
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index c94c64a..72c67d7 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -39,6 +39,7 @@
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 
 /**
  * Base class representing a remote service.
@@ -93,6 +94,9 @@
     // Used just for debugging purposes (on dump)
     private long mNextUnbind;
 
+    /** Requests that have been scheduled, but that are not finished yet */
+    private final ArrayList<PendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
+
     /**
      * Callback called when the service dies.
      *
@@ -135,6 +139,14 @@
         return mDestroyed;
     }
 
+    /**
+     * Gets the name of the service.
+     */
+    @NonNull
+    public final ComponentName getComponentName() {
+        return mComponentName;
+    }
+
     private void handleOnConnectedStateChangedInternal(boolean connected) {
         if (connected) {
             handlePendingRequests();
@@ -209,6 +221,7 @@
         }
         mService = null;
         mServiceDied = true;
+        cancelScheduledUnbind();
         @SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
         final S castService = (S) this;
         mVultureCallback.onServiceDied(castService);
@@ -229,6 +242,8 @@
                 .append(mComponentName.flattenToString()).println();
         pw.append(prefix).append(tab).append("destroyed=")
                 .append(String.valueOf(mDestroyed)).println();
+        pw.append(prefix).append(tab).append("numUnfinishedRequests=")
+                .append(String.valueOf(mUnfinishedRequests.size()));
         final boolean bound = handleIsBound();
         pw.append(prefix).append(tab).append("bound=")
                 .append(String.valueOf(bound));
@@ -257,23 +272,37 @@
      * <p>This request must be responded by the service somehow (typically using a callback),
      * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
      * service doesn't respond.
-     *
-     * <p><b>NOTE: </b>this request is responsible for calling {@link #scheduleUnbind()}.
      */
     protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
-        cancelScheduledUnbind();
         mHandler.sendMessage(obtainMessage(
                 AbstractRemoteService::handlePendingRequest, this, pendingRequest));
     }
 
     /**
+     * Marks a pendingRequest as finished.
+     *
+     * @param finshedRequest The request that is finished
+     */
+    void finishRequest(@NonNull PendingRequest<S, I> finshedRequest) {
+        mHandler.sendMessage(
+                obtainMessage(AbstractRemoteService::handleFinishRequest, this, finshedRequest));
+    }
+
+    private void handleFinishRequest(@NonNull PendingRequest<S, I> finshedRequest) {
+        mUnfinishedRequests.remove(finshedRequest);
+
+        if (mUnfinishedRequests.isEmpty()) {
+            scheduleUnbind();
+        }
+    }
+
+    /**
      * Schedules an async request.
      *
      * <p>This request is not expecting a callback from the service, hence it's represented by
      * a simple {@link Runnable}.
      */
     protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) {
-        scheduleUnbind();
         // TODO(b/117779333): fix generics below
         @SuppressWarnings({"unchecked", "rawtypes"})
         final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
@@ -341,6 +370,10 @@
             handleEnsureBound();
         } else {
             if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
+
+            mUnfinishedRequests.add(pendingRequest);
+            cancelScheduledUnbind();
+
             pendingRequest.run();
             if (pendingRequest.isFinal()) {
                 mCompleted = true;
@@ -504,6 +537,12 @@
                 }
                 mCompleted = true;
             }
+
+            S service = mWeakService.get();
+            if (service != null) {
+                service.finishRequest(this);
+            }
+
             mServiceHandler.removeCallbacks(mTimeoutTrigger);
             return true;
         }
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 0a7cff6..9bacf9b 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
+
 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
 import android.annotation.Nullable;
@@ -33,10 +34,8 @@
 
 import libcore.io.IoUtils;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.net.ProtocolException;
 import java.util.Arrays;
@@ -127,7 +126,7 @@
     }
 
     public NetworkStatsFactory() {
-        this(new File("/proc/"), new File("/sys/fs/bpf/traffic_uid_stats_map").exists());
+        this(new File("/proc/"), new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists());
     }
 
     @VisibleForTesting
diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java
new file mode 100644
index 0000000..afe6dad
--- /dev/null
+++ b/core/java/com/android/internal/os/AppZygoteInit.java
@@ -0,0 +1,105 @@
+/*
+ * 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.os;
+
+import android.app.LoadedApk;
+import android.content.pm.ApplicationInfo;
+import android.net.LocalSocket;
+import android.util.Log;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Startup class for an Application zygote process.
+ *
+ * See {@link ZygoteInit} for generic zygote startup documentation.
+ *
+ * @hide
+ */
+class AppZygoteInit {
+    public static final String TAG = "AppZygoteInit";
+
+    private static ZygoteServer sServer;
+
+    private static class AppZygoteServer extends ZygoteServer {
+        @Override
+        protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+                throws IOException {
+            return new AppZygoteConnection(socket, abiList);
+        }
+    }
+
+    private static class AppZygoteConnection extends ZygoteConnection {
+        AppZygoteConnection(LocalSocket socket, String abiList) throws IOException {
+            super(socket, abiList);
+        }
+
+        @Override
+        protected void preload() {
+            // Nothing to preload by default.
+        }
+
+        @Override
+        protected boolean isPreloadComplete() {
+            // App zygotes don't preload any classes or resources or defaults, all of their
+            // preloading is package specific.
+            return true;
+        }
+
+        @Override
+        protected boolean canPreloadApp() {
+            return true;
+        }
+
+        @Override
+        protected void handlePreloadApp(ApplicationInfo appInfo) {
+            Log.i(TAG, "Beginning application preload for " + appInfo.packageName);
+            LoadedApk loadedApk = new LoadedApk(null, appInfo, null, null, false, true, false);
+            ClassLoader loader = loadedApk.getClassLoader();
+            Class<?> cl;
+            Method m;
+            try {
+                cl = Class.forName(appInfo.packageName + ".ZygotePreload", true, loader);
+                m = cl.getMethod("doPreload");
+                m.setAccessible(true);
+                m.invoke(null);
+            } catch (ClassNotFoundException e) {
+                // Don't treat this as an error since an app may not want to do any preloads
+                Log.w(TAG, "No ZygotePreload class found for " + appInfo.packageName);
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                Log.e(TAG, "AppZygote application preload failed for "
+                        + appInfo.packageName, e);
+            }
+            try {
+                DataOutputStream socketOut = getSocketOutputStream();
+                socketOut.writeInt(loader != null ? 1 : 0);
+            } catch (IOException e) {
+                throw new IllegalStateException("Error writing to command socket", e);
+            }
+
+            Log.i(TAG, "Application preload done");
+        }
+    }
+
+    public static void main(String[] argv) {
+        AppZygoteServer server = new AppZygoteServer();
+        ChildZygoteInit.runZygoteServer(server, argv);
+    }
+}
diff --git a/core/java/com/android/internal/os/AtomicDirectory.java b/core/java/com/android/internal/os/AtomicDirectory.java
new file mode 100644
index 0000000..f24d12e
--- /dev/null
+++ b/core/java/com/android/internal/os/AtomicDirectory.java
@@ -0,0 +1,294 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.FileUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * Helper class for performing atomic operations on a directory, by creating a
+ * backup directory until a write has successfully completed.
+ * <p>
+ * Atomic directory guarantees directory integrity by ensuring that a directory has
+ * been completely written and sync'd to disk before removing its backup.
+ * As long as the backup directory exists, the original directory is considered
+ * to be invalid (leftover from a previous attempt to write).
+ * <p>
+ * Atomic directory does not confer any file locking semantics. Do not use this
+ * class when the directory may be accessed or modified concurrently
+ * by multiple threads or processes. The caller is responsible for ensuring
+ * appropriate mutual exclusion invariants whenever it accesses the directory.
+ * <p>
+ * To ensure atomicity you must always use this class to interact with the
+ * backing directory when checking existence, making changes, and deleting.
+ */
+public final class AtomicDirectory {
+    private final @NonNull ArrayMap<File, FileOutputStream> mOpenFiles = new ArrayMap<>();
+    private final @NonNull File mBaseDirectory;
+    private final @NonNull File mBackupDirectory;
+
+    private int mBaseDirectoryFd = -1;
+    private int mBackupDirectoryFd = -1;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param baseDirectory The base directory to treat atomically.
+     */
+    public AtomicDirectory(@NonNull File baseDirectory) {
+        Preconditions.checkNotNull(baseDirectory, "baseDirectory cannot be null");
+        mBaseDirectory = baseDirectory;
+        mBackupDirectory = new File(baseDirectory.getPath() + "_bak");
+    }
+
+    /**
+     * Gets the backup directory if present. This could be useful if you are
+     * writing new state to the dir but need to access the last persisted state
+     * at the same time. This means that this call is useful in between
+     * {@link #startWrite()} and {@link #finishWrite()} or {@link #failWrite()}.
+     * You should not modify the content returned by this method.
+     *
+     * @see #startRead()
+     */
+    public @Nullable File getBackupDirectory() {
+        return mBackupDirectory;
+    }
+
+    /**
+     * Starts reading this directory. After calling this method you should
+     * not make any changes to its contents.
+     *
+     * @throws IOException If an error occurs.
+     *
+     * @see #finishRead()
+     * @see #startWrite()
+     */
+    public @NonNull File startRead() throws IOException {
+        restore();
+        return getOrCreateBaseDirectory();
+    }
+
+    /**
+     * Finishes reading this directory.
+     *
+     * @see #startRead()
+     * @see #startWrite()
+     */
+    public void finishRead() {
+        mBaseDirectoryFd = -1;
+        mBackupDirectoryFd = -1;
+    }
+
+    /**
+     * Starts editing this directory. After calling this method you should
+     * add content to the directory only via the APIs on this class. To open a
+     * file for writing in this directory you should use {@link #openWrite(File)}
+     * and to close the file {@link #closeWrite(FileOutputStream)}. Once all
+     * content has been written and all files closed you should commit via a
+     * call to {@link #finishWrite()} or discard via a call to {@link #failWrite()}.
+     *
+     * @throws IOException If an error occurs.
+     *
+     * @see #startRead()
+     * @see #openWrite(File)
+     * @see #finishWrite()
+     * @see #failWrite()
+     */
+    public @NonNull File startWrite() throws IOException {
+        backup();
+        return getOrCreateBaseDirectory();
+    }
+
+    /**
+     * Opens a file in this directory for writing.
+     *
+     * @param file The file to open. Must be a file in the base directory.
+     * @return An input stream for reading.
+     *
+     * @throws IOException If an I/O error occurs.
+     *
+     * @see #closeWrite(FileOutputStream)
+     */
+    public @NonNull FileOutputStream openWrite(@NonNull File file) throws IOException {
+        if (file.isDirectory() || !file.getParentFile().equals(getOrCreateBaseDirectory())) {
+            throw new IllegalArgumentException("Must be a file in " + getOrCreateBaseDirectory());
+        }
+        final FileOutputStream destination = new FileOutputStream(file);
+        if (mOpenFiles.put(file, destination) != null) {
+            throw new IllegalArgumentException("Already open file" + file.getCanonicalPath());
+        }
+        return destination;
+    }
+
+    /**
+     * Closes a previously opened file.
+     *
+     * @param destination The stream to the file returned by {@link #openWrite(File)}.
+     *
+     * @see #openWrite(File)
+     */
+    public void closeWrite(@NonNull FileOutputStream destination) {
+        final int indexOfValue = mOpenFiles.indexOfValue(destination);
+        if (mOpenFiles.removeAt(indexOfValue) == null) {
+            throw new IllegalArgumentException("Unknown file stream " + destination);
+        }
+        FileUtils.sync(destination);
+        try {
+            destination.close();
+        } catch (IOException ignored) {}
+    }
+
+    public void failWrite(@NonNull FileOutputStream destination) {
+        final int indexOfValue = mOpenFiles.indexOfValue(destination);
+        if (indexOfValue >= 0) {
+            mOpenFiles.removeAt(indexOfValue);
+        }
+    }
+
+    /**
+     * Finishes the edit and commits all changes.
+     *
+     * @see #startWrite()
+     *
+     * @throws IllegalStateException is some files are not closed.
+     */
+    public void finishWrite() {
+        throwIfSomeFilesOpen();
+        fsyncDirectoryFd(mBaseDirectoryFd);
+        deleteDirectory(mBackupDirectory);
+        fsyncDirectoryFd(mBackupDirectoryFd);
+        mBaseDirectoryFd = -1;
+        mBackupDirectoryFd = -1;
+    }
+
+    /**
+     * Finishes the edit and discards all changes.
+     *
+     * @see #startWrite()
+     */
+    public void failWrite() {
+        throwIfSomeFilesOpen();
+        try{
+            restore();
+        } catch (IOException ignored) {}
+        mBaseDirectoryFd = -1;
+        mBackupDirectoryFd = -1;
+    }
+
+    /**
+     * @return Whether this directory exists.
+     */
+    public boolean exists() {
+        return mBaseDirectory.exists() || mBackupDirectory.exists();
+    }
+
+    /**
+     * Deletes this directory.
+     */
+    public void delete() {
+        if (mBaseDirectory.exists()) {
+            deleteDirectory(mBaseDirectory);
+            fsyncDirectoryFd(mBaseDirectoryFd);
+        }
+        if (mBackupDirectory.exists()) {
+            deleteDirectory(mBackupDirectory);
+            fsyncDirectoryFd(mBackupDirectoryFd);
+        }
+    }
+
+    private @NonNull File getOrCreateBaseDirectory() throws IOException {
+        if (!mBaseDirectory.exists()) {
+            if (!mBaseDirectory.mkdirs()) {
+                throw new IOException("Couldn't create directory " + mBaseDirectory);
+            }
+            FileUtils.setPermissions(mBaseDirectory.getPath(),
+                    FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH,
+                    -1, -1);
+        }
+        if (mBaseDirectoryFd < 0) {
+            mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath());
+        }
+        return mBaseDirectory;
+    }
+
+    private void throwIfSomeFilesOpen() {
+        if (!mOpenFiles.isEmpty()) {
+            throw new IllegalStateException("Unclosed files: "
+                    + Arrays.toString(mOpenFiles.keySet().toArray()));
+        }
+    }
+
+    private void backup() throws IOException {
+        if (!mBaseDirectory.exists()) {
+            return;
+        }
+        if (mBaseDirectoryFd < 0) {
+            mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath());
+        }
+        if (mBackupDirectory.exists()) {
+            deleteDirectory(mBackupDirectory);
+        }
+        if (!mBaseDirectory.renameTo(mBackupDirectory)) {
+            throw new IOException("Couldn't backup " + mBaseDirectory
+                    + " to " + mBackupDirectory);
+        }
+        mBackupDirectoryFd = mBaseDirectoryFd;
+        mBaseDirectoryFd = -1;
+        fsyncDirectoryFd(mBackupDirectoryFd);
+    }
+
+    private void restore() throws IOException {
+        if (!mBackupDirectory.exists()) {
+            return;
+        }
+        if (mBackupDirectoryFd == -1) {
+            mBackupDirectoryFd = getDirectoryFd(mBackupDirectory.getCanonicalPath());
+        }
+        if (mBaseDirectory.exists()) {
+            deleteDirectory(mBaseDirectory);
+        }
+        if (!mBackupDirectory.renameTo(mBaseDirectory)) {
+            throw new IOException("Couldn't restore " + mBackupDirectory
+                    + " to " + mBaseDirectory);
+        }
+        mBaseDirectoryFd = mBackupDirectoryFd;
+        mBackupDirectoryFd = -1;
+        fsyncDirectoryFd(mBaseDirectoryFd);
+    }
+
+    private static void deleteDirectory(@NonNull File file) {
+        final File[] children = file.listFiles();
+        if (children != null) {
+            for (File child : children) {
+                deleteDirectory(child);
+            }
+        }
+        file.delete();
+    }
+
+    private static native int getDirectoryFd(String path);
+    private static native void fsyncDirectoryFd(int fd);
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index cc8da5c..17cc6af 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -30,6 +30,7 @@
 import android.database.ContentObserver;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
 import android.net.NetworkStats;
 import android.net.Uri;
 import android.net.wifi.WifiActivityEnergyInfo;
@@ -86,7 +87,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.location.gnssmetrics.GnssMetrics;
-import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -337,6 +337,25 @@
 
     private final PlatformIdleStateCallback mPlatformIdleStateCallback;
 
+    private final Runnable mDeferSetCharging = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (BatteryStatsImpl.this) {
+                if (mOnBattery) {
+                    // if the device gets unplugged in the time between this runnable being
+                    // executed and the lock being taken, we don't want to set charging state
+                    return;
+                }
+                boolean changed = setChargingLocked(true);
+                if (changed) {
+                    final long uptime = mClocks.uptimeMillis();
+                    final long elapsedRealtime = mClocks.elapsedRealtime();
+                    addHistoryRecordLocked(elapsedRealtime, uptime);
+                }
+            }
+        }
+    };
+
     /**
      * This handler is running on {@link BackgroundThread}.
      */
@@ -11012,7 +11031,6 @@
         }
     }
 
-    private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
     private final Pools.Pool<NetworkStats> mNetworkStatsPool = new Pools.SynchronizedPool<>(6);
 
     private final Object mWifiNetworkLock = new Object();
@@ -11034,11 +11052,16 @@
     private NetworkStats readNetworkStatsLocked(String[] ifaces) {
         try {
             if (!ArrayUtils.isEmpty(ifaces)) {
-                return mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
-                        NetworkStats.TAG_NONE, mNetworkStatsPool.acquire());
+                INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+                        ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+                if (statsService != null) {
+                    return statsService.getDetailedUidStats(ifaces);
+                } else {
+                    Slog.e(TAG, "Failed to get networkStatsService ");
+                }
             }
-        } catch (IOException e) {
-            Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces));
+        } catch (RemoteException e) {
+            Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e);
         }
         return null;
     }
@@ -12336,6 +12359,14 @@
     }
 
     boolean setChargingLocked(boolean charging) {
+        // if the device is no longer charging, remove the callback
+        // if the device is now charging, it means that this is either called
+        // 1. directly when level >= 90
+        // 2. or from within the runnable that we deferred
+        // For 1. if we have an existing callback, remove it, since we will immediatelly send a
+        // ACTION_CHARGING
+        // For 2. we remove existing callback so we don't send multiple ACTION_CHARGING
+        mHandler.removeCallbacks(mDeferSetCharging);
         if (mCharging != charging) {
             mCharging = charging;
             if (charging) {
@@ -12674,12 +12705,23 @@
                     // charging even if it happens to go down a level.
                     changed |= setChargingLocked(true);
                     mLastChargeStepLevel = level;
-                } if (!mCharging) {
+                }
+                if (!mCharging) {
                     if (mLastChargeStepLevel < level) {
-                        // We have not reporting that we are charging, but the level has now
-                        // gone up, so consider the state to be charging.
-                        changed |= setChargingLocked(true);
+                        // We have not reported that we are charging, but the level has gone up,
+                        // but we would like to not have tons of activity from charging-constraint
+                        // jobs, so instead of reporting ACTION_CHARGING immediately, we defer it.
                         mLastChargeStepLevel = level;
+                        if (!mHandler.hasCallbacks(mDeferSetCharging)) {
+                            mHandler.postDelayed(
+                                    mDeferSetCharging,
+                                    mConstants.BATTERY_CHARGED_DELAY_MS);
+                        }
+                    } else if (mLastChargeStepLevel > level) {
+                        // if we had deferred a runnable due to charge level increasing, but then
+                        // later the charge level drops (could be due to thermal issues), we don't
+                        // want to trigger the deferred runnable, so remove it here
+                        mHandler.removeCallbacks(mDeferSetCharging);
                     }
                 } else {
                     if (mLastChargeStepLevel > level) {
@@ -13296,6 +13338,8 @@
                 = "battery_level_collection_delay_ms";
         public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
         public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
+        public static final String KEY_BATTERY_CHARGED_DELAY_MS =
+                "battery_charged_delay_ms";
 
         private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = false;
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
@@ -13308,6 +13352,7 @@
         private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
         private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
         private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
+        private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
@@ -13320,6 +13365,7 @@
                 = DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
         public int MAX_HISTORY_FILES;
         public int MAX_HISTORY_BUFFER; /*Bytes*/
+        public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13388,6 +13434,9 @@
                                 DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
                                 : DEFAULT_MAX_HISTORY_BUFFER_KB)
                         * 1024;
+                BATTERY_CHARGED_DELAY_MS = mParser.getInt(
+                        KEY_BATTERY_CHARGED_DELAY_MS,
+                        DEFAULT_BATTERY_CHARGED_DELAY_MS);
             }
         }
 
@@ -13445,6 +13494,8 @@
             pw.println(MAX_HISTORY_FILES);
             pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
             pw.println(MAX_HISTORY_BUFFER/1024);
+            pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
+            pw.println(BATTERY_CHARGED_DELAY_MS);
         }
     }
 
diff --git a/core/java/com/android/internal/os/ChildZygoteInit.java b/core/java/com/android/internal/os/ChildZygoteInit.java
new file mode 100644
index 0000000..a052a3b
--- /dev/null
+++ b/core/java/com/android/internal/os/ChildZygoteInit.java
@@ -0,0 +1,137 @@
+/*
+ * 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.os;
+
+import android.os.Process;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+
+/**
+ * ChildZygoteInit is shared by both the Application and WebView zygote to initialize
+ * and run a (child) Zygote server.
+ *
+ * @hide
+ */
+public class ChildZygoteInit {
+    private static final String TAG = "ChildZygoteInit";
+
+    static String parseSocketNameFromArgs(String[] argv) {
+        for (String arg : argv) {
+            if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+                return arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
+            }
+        }
+
+        return null;
+    }
+
+    static String parseAbiListFromArgs(String[] argv) {
+        for (String arg : argv) {
+            if (arg.startsWith(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG)) {
+                return arg.substring(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG.length());
+            }
+        }
+
+        return null;
+    }
+
+    static int parseIntFromArg(String[] argv, String desiredArg) {
+        int value = -1;
+        for (String arg : argv) {
+            if (arg.startsWith(desiredArg)) {
+                String valueStr = arg.substring(arg.indexOf('=') + 1);
+                try {
+                    value = Integer.parseInt(valueStr);
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Invalid int argument: "
+                            + valueStr, e);
+                }
+            }
+        }
+        return value;
+    }
+
+    /**
+     * Starts a ZygoteServer and listens for requests
+     *
+     * @param server An instance of a ZygoteServer to listen on
+     * @param args Passed in arguments for this ZygoteServer
+     */
+    static void runZygoteServer(ZygoteServer server, String[] args) {
+        String socketName = parseSocketNameFromArgs(args);
+        if (socketName == null) {
+            throw new NullPointerException("No socketName specified");
+        }
+
+        String abiList = parseAbiListFromArgs(args);
+        if (abiList == null) {
+            throw new NullPointerException("No abiList specified");
+        }
+
+        try {
+            Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
+        }
+
+        int uidGidMin = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_START);
+        int uidGidMax = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_END);
+        if (uidGidMin == -1 || uidGidMax == -1) {
+            throw new RuntimeException("Couldn't parse UID range start/end");
+        }
+        if (uidGidMin > uidGidMax) {
+            throw new RuntimeException("Passed in UID range is invalid, min > max.");
+        }
+
+        // Verify the UIDs are in the isolated UID range, as that's the only thing that we should
+        // be forking right now
+        if (!Process.isIsolated(uidGidMin) || !Process.isIsolated(uidGidMax)) {
+            throw new RuntimeException("Passed in UID range does not map to isolated processes.");
+        }
+
+        /**
+         * Install a seccomp filter that ensure this Zygote can only setuid()/setgid()
+         * to the passed in range.
+         */
+        Zygote.nativeInstallSeccompUidGidFilter(uidGidMin, uidGidMax);
+
+        final Runnable caller;
+        try {
+            server.registerServerSocketAtAbstractName(socketName);
+
+            // Add the abstract socket to the FD whitelist so that the native zygote code
+            // can properly detach it after forking.
+            Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
+
+            // The select loop returns early in the child process after a fork and
+            // loops forever in the zygote.
+            caller = server.runSelectLoop(abiList);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Fatal exception:", e);
+            throw e;
+        } finally {
+            server.closeServerSocket();
+        }
+
+        // We're in the child process and have exited the select loop. Proceed to execute the
+        // command.
+        if (caller != null) {
+            caller.run();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index dc660a4..b529bbe 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -30,6 +30,23 @@
     public static final String CONTROL_PRIVAPP_PERMISSIONS =
             SystemProperties.get("ro.control_privapp_permissions");
 
+    // ------ ro.hdmi.* -------- //
+    /**
+     * Property to indicate if a CEC audio device should forward volume keys when system audio
+     * mode is off.
+     */
+    public static final boolean CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF =
+            SystemProperties.getBoolean(
+                    "ro.hdmi.cec_audio_device_forward_volume_keys_system_audio_mode_off", false);
+
+    /**
+     * Property to indicate if the current device is a cec switch device.
+     *
+     * <p> Default is false.
+     */
+    public static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
+            "ro.hdmi.property_is_device_hdmi_cec_switch";
+
     // ------ ro.config.* -------- //
     public static final boolean CONFIG_AVOID_GFX_ACCEL =
             SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 9f2434e..0b329d7 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -18,11 +18,7 @@
 
 import android.app.ApplicationLoaders;
 import android.net.LocalSocket;
-import android.net.LocalServerSocket;
 import android.os.Build;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.WebViewFactory;
@@ -44,8 +40,6 @@
 class WebViewZygoteInit {
     public static final String TAG = "WebViewZygoteInit";
 
-    private static ZygoteServer sServer;
-
     private static class WebViewZygoteServer extends ZygoteServer {
         @Override
         protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
@@ -127,48 +121,7 @@
 
     public static void main(String argv[]) {
         Log.i(TAG, "Starting WebViewZygoteInit");
-
-        String socketName = null;
-        for (String arg : argv) {
-            Log.i(TAG, arg);
-            if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
-                socketName = arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
-            }
-        }
-        if (socketName == null) {
-            throw new RuntimeException("No " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + " specified");
-        }
-
-        try {
-            Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
-        } catch (ErrnoException ex) {
-            throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
-        }
-
-        sServer = new WebViewZygoteServer();
-
-        final Runnable caller;
-        try {
-            sServer.registerServerSocketAtAbstractName(socketName);
-
-            // Add the abstract socket to the FD whitelist so that the native zygote code
-            // can properly detach it after forking.
-            Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
-
-            // The select loop returns early in the child process after a fork and
-            // loops forever in the zygote.
-            caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Fatal exception:", e);
-            throw e;
-        } finally {
-            sServer.closeServerSocket();
-        }
-
-        // We're in the child process and have exited the select loop. Proceed to execute the
-        // command.
-        if (caller != null) {
-            caller.run();
-        }
+        WebViewZygoteServer server = new WebViewZygoteServer();
+        ChildZygoteInit.runZygoteServer(server, argv);
     }
 }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 65b9fad..f5746ca 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -98,6 +98,26 @@
      */
     public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
 
+    /**
+     * An extraArg passed when a zygote process is forking a child-zygote, specifying the
+     * requested ABI for the child Zygote.
+     */
+    public static final String CHILD_ZYGOTE_ABI_LIST_ARG = "--abi-list=";
+
+    /**
+     * An extraArg passed when a zygote process is forking a child-zygote, specifying the
+     * start of the UID range the children of the Zygote may setuid()/setgid() to. This
+     * will be enforced with a seccomp filter.
+     */
+    public static final String CHILD_ZYGOTE_UID_RANGE_START = "--uid-range-start=";
+
+    /**
+     * An extraArg passed when a zygote process is forking a child-zygote, specifying the
+     * end of the UID range the children of the Zygote may setuid()/setgid() to. This
+     * will be enforced with a seccomp filter.
+     */
+    public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
+
     private Zygote() {}
 
     /** Called for some security initialization before any fork. */
@@ -216,6 +236,13 @@
     native protected static void nativeAllowFileAcrossFork(String path);
 
     /**
+     * Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
+     * @param uidGidMin The smallest allowed uid/gid
+     * @param uidGidMax The largest allowed uid/gid
+     */
+    native protected static void nativeInstallSeccompUidGidFilter(int uidGidMin, int uidGidMax);
+
+    /**
      * Zygote unmount storage space on initializing.
      * This method is called once.
      */
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index f182c4d..5990d72 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -27,9 +27,11 @@
 import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
 import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
 
+import android.content.pm.ApplicationInfo;
 import android.net.Credentials;
 import android.net.LocalSocket;
 import android.os.FactoryTest;
+import android.os.Parcel;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
@@ -52,6 +54,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 
 /**
  * A connection that can make spawn requests.
@@ -168,6 +171,21 @@
             return null;
         }
 
+        if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
+            byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
+            Parcel appInfoParcel = Parcel.obtain();
+            appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
+            appInfoParcel.setDataPosition(0);
+            ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
+            appInfoParcel.recycle();
+            if (appInfo != null) {
+                handlePreloadApp(appInfo);
+            } else {
+                throw new IllegalArgumentException("Failed to deserialize --preload-app");
+            }
+            return null;
+        }
+
         if (parsedArgs.apiBlacklistExemptions != null) {
             handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
             return null;
@@ -341,7 +359,15 @@
 
     protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
             String cacheKey) {
-        throw new RuntimeException("Zyogte does not support package preloading");
+        throw new RuntimeException("Zygote does not support package preloading");
+    }
+
+    protected boolean canPreloadApp() {
+        return false;
+    }
+
+    protected void handlePreloadApp(ApplicationInfo aInfo) {
+        throw new RuntimeException("Zygote does not support app preloading");
     }
 
     /**
@@ -467,6 +493,12 @@
         String preloadPackage;
 
         /**
+         * A Base64 string representing a serialize ApplicationInfo Parcel,
+           when using --preload-app.
+          */
+        String mPreloadApp;
+
+        /**
          * The native library path of the package to preload, when using --preload-package.
          */
         String preloadPackageLibs;
@@ -666,6 +698,8 @@
                     instructionSet = arg.substring(arg.indexOf('=') + 1);
                 } else if (arg.startsWith("--app-data-dir=")) {
                     appDataDir = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--preload-app")) {
+                    mPreloadApp = args[++curArg];
                 } else if (arg.equals("--preload-package")) {
                     preloadPackage = args[++curArg];
                     preloadPackageLibs = args[++curArg];
@@ -714,6 +748,11 @@
                     throw new IllegalArgumentException(
                             "Unexpected arguments after --preload-package.");
                 }
+            } else if (mPreloadApp != null) {
+                if (args.length - curArg > 0) {
+                    throw new IllegalArgumentException(
+                            "Unexpected arguments after --preload-app.");
+                }
             } else if (expectRuntimeArgs) {
                 if (!seenRuntimeArgs) {
                     throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index e2c23de..cdaf33f 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -232,7 +232,7 @@
     // This is the caption view for the window, containing the caption and window control
     // buttons. The visibility of this decor depends on the workspace and the window type.
     // If the window type does not require such a view, this member might be null.
-    DecorCaptionView mDecorCaptionView;
+    private DecorCaptionView mDecorCaptionView;
 
     private boolean mWindowResizeCallbacksAdded = false;
     private Drawable.Callback mLastBackgroundDrawableCb = null;
@@ -977,6 +977,7 @@
     @Override
     public void onWindowSystemUiVisibilityChanged(int visible) {
         updateColorViews(null /* insets */, true /* animate */);
+        updateDecorCaptionStatus(getResources().getConfiguration());
     }
 
     @Override
@@ -1855,8 +1856,27 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-        final boolean displayWindowDecor =
-                newConfig.windowConfiguration.hasWindowDecorCaption();
+        updateDecorCaptionStatus(newConfig);
+
+        updateAvailableWidth();
+        initializeElevation();
+    }
+
+    /**
+     * Determines if the workspace is entirely covered by the window.
+     * @return {@code true} when the window is filling the entire screen/workspace.
+     **/
+    private boolean isFillingScreen(Configuration config) {
+        final boolean isFullscreen = config.windowConfiguration.getWindowingMode()
+                == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+        return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility())
+                & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
+    }
+
+    private void updateDecorCaptionStatus(Configuration config) {
+        final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
+                && !isFillingScreen(config);
         if (mDecorCaptionView == null && displayWindowDecor) {
             // Configuration now requires a caption.
             final LayoutInflater inflater = mWindow.getLayoutInflater();
@@ -1875,9 +1895,6 @@
             mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
             enableCaption(displayWindowDecor);
         }
-
-        updateAvailableWidth();
-        initializeElevation();
     }
 
     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
index 100c6ee..adf7692 100644
--- a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -16,10 +16,10 @@
 
 package com.android.internal.policy;
 
-import com.android.internal.R;
-
 import android.content.res.Resources;
 
+import com.android.internal.R;
+
 /**
  * Utility functions for screen decorations used by both window manager and System UI.
  */
@@ -31,15 +31,19 @@
      * scaling, this means that we don't have to reload them on config changes.
      */
     public static float getWindowCornerRadius(Resources resources) {
+        if (!supportsRoundedCornersOnWindows(resources)) {
+            return 0f;
+        }
+
         // Radius that should be used in case top or bottom aren't defined.
         float defaultRadius = resources.getDimension(R.dimen.rounded_corner_radius);
 
         float topRadius = resources.getDimension(R.dimen.rounded_corner_radius_top);
-        if (topRadius == 0) {
+        if (topRadius == 0f) {
             topRadius = defaultRadius;
         }
         float bottomRadius = resources.getDimension(R.dimen.rounded_corner_radius_bottom);
-        if (bottomRadius == 0) {
+        if (bottomRadius == 0f) {
             bottomRadius = defaultRadius;
         }
 
@@ -47,4 +51,11 @@
         // completely cover the display.
         return Math.min(topRadius, bottomRadius);
     }
+
+    /**
+     * If live rounded corners are supported on windows.
+     */
+    public static boolean supportsRoundedCornersOnWindows(Resources resources) {
+        return resources.getBoolean(R.bool.config_supportsRoundedCornersOnWindows);
+    }
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 53b56f2..6a28059 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -153,13 +153,11 @@
     void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
             boolean requireConfirmation, int userId);
     // Used to hide the dialog when a biometric is authenticated
-    void onBiometricAuthenticated();
+    void onBiometricAuthenticated(boolean authenticated);
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
     void onBiometricHelp(String message);
     // Used to set a message - the dialog will dismiss after a certain amount of time
     void onBiometricError(String error);
     // Used to hide the biometric dialog when the AuthenticationClient is stopped
     void hideBiometricDialog();
-    // Used to request the "try again" button for authentications which requireConfirmation=true
-    void showBiometricTryAgain();
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9087dd2..197e873 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -97,13 +97,11 @@
     void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
             boolean requireConfirmation, int userId);
     // Used to hide the dialog when a biometric is authenticated
-    void onBiometricAuthenticated();
+    void onBiometricAuthenticated(boolean authenticated);
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
     void onBiometricHelp(String message);
     // Used to set a message - the dialog will dismiss after a certain amount of time
     void onBiometricError(String error);
     // Used to hide the biometric dialog when the AuthenticationClient is stopped
     void hideBiometricDialog();
-    // Used to request the "try again" button for authentications which requireConfirmation=true
-    void showBiometricTryAgain();
 }
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index 20f2aa0..8022949 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -29,12 +29,13 @@
  * @hide
  */
 interface ITextServicesManager {
-    SpellCheckerInfo getCurrentSpellChecker(String locale);
-    SpellCheckerSubtype getCurrentSpellCheckerSubtype(boolean allowImplicitlySelectedSubtype);
-    oneway void getSpellCheckerService(String sciId, in String locale,
+    SpellCheckerInfo getCurrentSpellChecker(int userId, String locale);
+    SpellCheckerSubtype getCurrentSpellCheckerSubtype(int userId,
+            boolean allowImplicitlySelectedSubtype);
+    oneway void getSpellCheckerService(int userId, String sciId, in String locale,
             in ITextServicesSessionListener tsListener,
             in ISpellCheckerSessionListener scListener, in Bundle bundle);
-    oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
-    boolean isSpellCheckerEnabled();
-    SpellCheckerInfo[] getEnabledSpellCheckers();
+    oneway void finishSpellCheckerService(int userId, in ISpellCheckerSessionListener listener);
+    boolean isSpellCheckerEnabled(int userId);
+    SpellCheckerInfo[] getEnabledSpellCheckers(int userId);
 }
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 470533f2..605df04 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -330,4 +330,18 @@
     public static @NonNull <T> List<T> defeatNullable(@Nullable List<T> val) {
         return (val != null) ? val : Collections.emptyList();
     }
+
+    /**
+     * @return the first element if not empty/null, null otherwise
+     */
+    public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) {
+        return isEmpty(cur) ? null : cur.get(0);
+    }
+
+    /**
+     * @return list of single given element if it's not null, empty list otherwise
+     */
+    public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) {
+        return item == null ? Collections.emptyList() : Collections.singletonList(item);
+    }
 }
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index a403c06..e0ba317f 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -586,7 +586,7 @@
      *
      * @param color the base color to use
      * @param amount the amount from 1 to 100 how much to modify the color
-     * @return the now color that was modified
+     * @return the new color that was modified
      */
     public static int getShiftedColor(int color, int amount) {
         final double[] result = ColorUtilsFromCompat.getTempDouble3Array();
@@ -599,6 +599,19 @@
         return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]);
     }
 
+    /**
+     * Blends the provided color with white to create a muted version.
+     *
+     * @param color the color to mute
+     * @param alpha the amount from 0 to 1 to set the alpha component of the white scrim
+     * @return the new color that was modified
+     */
+    public static int getMutedColor(int color, float alpha) {
+        int whiteScrim = ColorUtilsFromCompat.setAlphaComponent(
+                Color.WHITE, (int) (255 * alpha));
+        return compositeColors(whiteScrim, color);
+    }
+
     private static boolean shouldUseDark(int backgroundColor, boolean defaultBackgroundIsDark) {
         if (backgroundColor == Notification.COLOR_DEFAULT) {
             return !defaultBackgroundIsDark;
@@ -675,6 +688,18 @@
         }
 
         /**
+         * Set the alpha component of {@code color} to be {@code alpha}.
+         */
+        @ColorInt
+        public static int setAlphaComponent(@ColorInt int color,
+                @IntRange(from = 0x0, to = 0xFF) int alpha) {
+            if (alpha < 0 || alpha > 255) {
+                throw new IllegalArgumentException("alpha must be between 0 and 255.");
+            }
+            return (color & 0x00ffffff) | (alpha << 24);
+        }
+
+        /**
          * Returns the luminance of a color as a float between {@code 0.0} and {@code 1.0}.
          * <p>Defined as the Y component in the XYZ representation of {@code color}.</p>
          */
diff --git a/core/java/com/android/internal/util/SyncResultReceiver.java b/core/java/com/android/internal/util/SyncResultReceiver.java
new file mode 100644
index 0000000..9a346ac
--- /dev/null
+++ b/core/java/com/android/internal/util/SyncResultReceiver.java
@@ -0,0 +1,145 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@code IResultReceiver} implementation that can be used to make "sync" Binder calls by blocking
+ * until it receives a result
+ *
+ * @hide
+ */
+public final class SyncResultReceiver extends IResultReceiver.Stub {
+
+    private static final String EXTRA = "EXTRA";
+
+    private final CountDownLatch mLatch  = new CountDownLatch(1);
+    private final int mTimeoutMs;
+    private int mResult;
+    private Bundle mBundle;
+
+    /**
+     * Default constructor.
+     *
+     * @param timeoutMs how long to block waiting for {@link IResultReceiver} callbacks.
+     */
+    public SyncResultReceiver(int timeoutMs) {
+        mTimeoutMs = timeoutMs;
+    }
+
+    private void waitResult() throws TimeoutException {
+        try {
+            if (!mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS)) {
+                throw new TimeoutException("Not called in " + mTimeoutMs + "ms");
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new TimeoutException("Interrupted");
+        }
+    }
+
+    /**
+     * Gets the result from an operation that returns an {@code int}.
+     */
+    public int getIntResult() throws TimeoutException {
+        waitResult();
+        return mResult;
+    }
+
+    /**
+     * Gets the result from an operation that returns an {@code String}.
+     */
+    @Nullable
+    public String getStringResult() throws TimeoutException {
+        waitResult();
+        return mBundle == null ? null : mBundle.getString(EXTRA);
+    }
+
+    /**
+     * Gets the result from an operation that returns a {@code String[]}.
+     */
+    @Nullable
+    public String[] getStringArrayResult() throws TimeoutException {
+        waitResult();
+        return mBundle == null ? null : mBundle.getStringArray(EXTRA);
+    }
+
+    /**
+     * Gets the result from an operation that returns a {@code Parcelable}.
+     */
+    @Nullable
+    public <P extends Parcelable> P getParcelableResult() throws TimeoutException {
+        waitResult();
+        return mBundle == null ? null : mBundle.getParcelable(EXTRA);
+    }
+
+    @Override
+    public void send(int resultCode, Bundle resultData) {
+        mResult = resultCode;
+        mBundle = resultData;
+        mLatch.countDown();
+    }
+
+    /**
+     * Creates a bundle for a {@code String} value so it can be retrieved by
+     * {@link #getStringResult()}.
+     */
+    @NonNull
+    public static Bundle bundleFor(@Nullable String value) {
+        final Bundle bundle = new Bundle();
+        bundle.putString(EXTRA, value);
+        return bundle;
+    }
+
+    /**
+     * Creates a bundle for a {@code String[]} value so it can be retrieved by
+     * {@link #getStringArrayResult()}.
+     */
+    @NonNull
+    public static Bundle bundleFor(@Nullable String[] value) {
+        final Bundle bundle = new Bundle();
+        bundle.putStringArray(EXTRA, value);
+        return bundle;
+    }
+
+    /**
+     * Creates a bundle for a {@code Parcelable} value so it can be retrieved by
+     * {@link #getParcelableResult()}.
+     */
+    @NonNull
+    public static Bundle bundleFor(@Nullable Parcelable value) {
+        final Bundle bundle = new Bundle();
+        bundle.putParcelable(EXTRA, value);
+        return bundle;
+    }
+
+    /** @hide */
+    public static final class TimeoutException extends RemoteException {
+        private TimeoutException(String msg) {
+            super(msg);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 901cfe3..9fe49b4 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -127,8 +127,10 @@
          */
         int ERROR_IME_NOT_CONNECTED = 8;
         /**
-         * Indicates that the caller is not the foreground user (or does not have
-         * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission).
+         * Indicates that the caller is not the foreground user, does not have
+         * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission, or the user
+         * specified in {@link android.view.inputmethod.EditorInfo#targetInputMethodUser} is not
+         * running.
          */
         int ERROR_INVALID_USER = 9;
         /**
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index b419113..21558d3 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -319,23 +319,12 @@
         mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
                 mClose.getRight(), mClose.getBottom());
     }
-    /**
-     * Determine if the workspace is entirely covered by the window.
-     * @return Returns true when the window is filling the entire screen/workspace.
-     **/
-    private boolean isFillingScreen() {
-        return (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) &
-                (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
-                        View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
-    }
 
     /**
      * Updates the visibility of the caption.
      **/
     private void updateCaptionVisibility() {
-        // Don't show the caption if the window has e.g. entered full screen.
-        boolean invisible = isFillingScreen() || !mShow;
-        mCaption.setVisibility(invisible ? GONE : VISIBLE);
+        mCaption.setVisibility(mShow ? VISIBLE : GONE);
         mCaption.setOnTouchListener(this);
     }
 
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 591f15f..9a77802 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -62,7 +62,9 @@
             in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile);
     KeyChainSnapshot getKeyChainSnapshot();
     String generateKey(String alias);
+    String generateKeyWithMetadata(String alias, in byte[] metadata);
     String importKey(String alias, in byte[] keyBytes);
+    String importKeyWithMetadata(String alias, in byte[] keyBytes, in byte[] metadata);
     String getKey(String alias);
     void removeKey(String alias);
     void setSnapshotCreatedPendingIntent(in PendingIntent intent);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index dc6a73a..088e13f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -41,7 +41,7 @@
         "com_google_android_gles_jni_EGLImpl.cpp",
         "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
         "android_app_Activity.cpp",
-	"android_app_ActivityThread.cpp",
+        "android_app_ActivityThread.cpp",
         "android_app_NativeActivity.cpp",
         "android_app_admin_SecurityLog.cpp",
         "android_opengl_EGL14.cpp",
@@ -202,6 +202,7 @@
         "android_animation_PropertyValuesHolder.cpp",
         "android_security_Scrypt.cpp",
         "com_android_internal_net_NetworkStatsFactory.cpp",
+        "com_android_internal_os_AtomicDirectory.cpp",
         "com_android_internal_os_ClassLoaderFactory.cpp",
         "com_android_internal_os_FuseAppLoop.cpp",
         "com_android_internal_os_Zygote.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 687b105..1092222 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -218,6 +218,7 @@
 extern int register_android_security_Scrypt(JNIEnv *env);
 extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
 extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
+extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
@@ -1495,6 +1496,7 @@
     REG_JNI(register_android_security_Scrypt),
     REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
     REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
+    REG_JNI(register_com_android_internal_os_AtomicDirectory),
     REG_JNI(register_com_android_internal_os_FuseAppLoop),
 };
 
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index eb7338a..876bd4f 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -8,7 +8,6 @@
 #include "SkImageInfo.h"
 #include "SkColor.h"
 #include "SkColorSpace.h"
-#include "SkMatrix44.h"
 #include "GraphicsJNI.h"
 #include "SkStream.h"
 
@@ -356,8 +355,8 @@
     if (xyzD50 == nullptr || transferParameters == nullptr) {
         colorSpace = SkColorSpace::MakeSRGB();
     } else {
-        SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
-        SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
+        skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
+        skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
         colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
     }
 
@@ -549,8 +548,7 @@
     if (skbitmap.colorType() == kRGBA_F16_SkColorType) {
         // Convert to P3 before encoding. This matches SkAndroidCodec::computeOutputColorSpace
         // for wide gamuts.
-        auto cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                        SkColorSpace::kDCIP3_D65_Gamut);
+        auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
         auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType)
                                    .makeColorSpace(std::move(cs));
         SkBitmap p3;
@@ -568,11 +566,34 @@
     return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE;
 }
 
+static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color,
+        const sk_sp<SkColorSpace>& colorSpace) {
+    SkPaint p;
+    p.setColor4f(color, colorSpace.get());
+    p.setBlendMode(SkBlendMode::kSrc);
+    SkCanvas canvas(bitmap);
+    canvas.drawPaint(p);
+}
+
 static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
     LocalScopedBitmap bitmap(bitmapHandle);
     SkBitmap skBitmap;
     bitmap->getSkBitmap(&skBitmap);
-    skBitmap.eraseColor(color);
+    bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB());
+}
+
+static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jobject jColorSpace,
+        jfloat r, jfloat g, jfloat b, jfloat a) {
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
+    if (GraphicsJNI::hasException(env)) {
+        return;
+    }
+
+    LocalScopedBitmap bitmap(bitmapHandle);
+    SkBitmap skBitmap;
+    bitmap->getSkBitmap(&skBitmap);
+    SkColor4f color = SkColor4f{r, g, b, a};
+    bitmapErase(skBitmap, color, cs);
 }
 
 static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -717,7 +738,7 @@
 #endif
         // Dup the file descriptor so we can keep a reference to it after the Parcel
         // is disposed.
-        int dupFd = dup(blob.fd());
+        int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0);
         if (dupFd < 0) {
             ALOGE("Error allocating dup fd. Error:%d", errno);
             blob.release();
@@ -910,32 +931,32 @@
     SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
     if (colorSpace == nullptr) return JNI_FALSE;
 
-    SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
+    skcms_Matrix3x3 xyzMatrix;
     if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE;
 
     jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL);
-    xyz[0] = xyzMatrix.getFloat(0, 0);
-    xyz[1] = xyzMatrix.getFloat(1, 0);
-    xyz[2] = xyzMatrix.getFloat(2, 0);
-    xyz[3] = xyzMatrix.getFloat(0, 1);
-    xyz[4] = xyzMatrix.getFloat(1, 1);
-    xyz[5] = xyzMatrix.getFloat(2, 1);
-    xyz[6] = xyzMatrix.getFloat(0, 2);
-    xyz[7] = xyzMatrix.getFloat(1, 2);
-    xyz[8] = xyzMatrix.getFloat(2, 2);
+    xyz[0] = xyzMatrix.vals[0][0];
+    xyz[1] = xyzMatrix.vals[1][0];
+    xyz[2] = xyzMatrix.vals[2][0];
+    xyz[3] = xyzMatrix.vals[0][1];
+    xyz[4] = xyzMatrix.vals[1][1];
+    xyz[5] = xyzMatrix.vals[2][1];
+    xyz[6] = xyzMatrix.vals[0][2];
+    xyz[7] = xyzMatrix.vals[1][2];
+    xyz[8] = xyzMatrix.vals[2][2];
     env->ReleaseFloatArrayElements(xyzArray, xyz, 0);
 
-    SkColorSpaceTransferFn transferParams;
+    skcms_TransferFunction transferParams;
     if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE;
 
     jfloat* params = env->GetFloatArrayElements(paramsArray, NULL);
-    params[0] = transferParams.fA;
-    params[1] = transferParams.fB;
-    params[2] = transferParams.fC;
-    params[3] = transferParams.fD;
-    params[4] = transferParams.fE;
-    params[5] = transferParams.fF;
-    params[6] = transferParams.fG;
+    params[0] = transferParams.a;
+    params[1] = transferParams.b;
+    params[2] = transferParams.c;
+    params[3] = transferParams.d;
+    params[4] = transferParams.e;
+    params[5] = transferParams.f;
+    params[6] = transferParams.g;
     env->ReleaseFloatArrayElements(paramsArray, params, 0);
 
     return JNI_TRUE;
@@ -1121,8 +1142,8 @@
 
 static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
                                                jfloatArray xyzD50, jobject transferParameters) {
-    SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
-    SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
+    skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
+    skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
     sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
     AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
         hardwareBuffer);
@@ -1183,6 +1204,7 @@
     {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
         (void*)Bitmap_compress },
     {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
+    {   "nativeErase",              "(JLandroid/graphics/ColorSpace;FFFF)V", (void*)Bitmap_eraseLong },
     {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
     {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
     {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 9ae05f4..4b0ab5b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -231,7 +231,7 @@
     }
 
     if (isMutable && isHardware) {
-        doThrowIAE(env, "Bitmaps with Config.HARWARE are always immutable");
+        doThrowIAE(env, "Bitmaps with Config.HARDWARE are always immutable");
         return nullObjectReturn("Cannot create mutable hardware bitmap");
     }
 
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 67d0c8a..9e74b88 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -424,30 +424,30 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
-    SkColorSpaceTransferFn p;
-    p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
-    p.fB = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID);
-    p.fC = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID);
-    p.fD = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID);
-    p.fE = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID);
-    p.fF = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID);
-    p.fG = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID);
+skcms_TransferFunction GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
+    skcms_TransferFunction p;
+    p.a = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
+    p.b = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID);
+    p.c = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID);
+    p.d = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID);
+    p.e = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID);
+    p.f = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID);
+    p.g = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID);
     return p;
 }
 
-SkMatrix44 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
-    SkMatrix44 xyzMatrix(SkMatrix44::kIdentity_Constructor);
+skcms_Matrix3x3 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
+    skcms_Matrix3x3 xyzMatrix;
     jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
-    xyzMatrix.setFloat(0, 0, array[0]);
-    xyzMatrix.setFloat(1, 0, array[1]);
-    xyzMatrix.setFloat(2, 0, array[2]);
-    xyzMatrix.setFloat(0, 1, array[3]);
-    xyzMatrix.setFloat(1, 1, array[4]);
-    xyzMatrix.setFloat(2, 1, array[5]);
-    xyzMatrix.setFloat(0, 2, array[6]);
-    xyzMatrix.setFloat(1, 2, array[7]);
-    xyzMatrix.setFloat(2, 2, array[8]);
+    xyzMatrix.vals[0][0] = array[0];
+    xyzMatrix.vals[1][0] = array[1];
+    xyzMatrix.vals[2][0] = array[2];
+    xyzMatrix.vals[0][1] = array[3];
+    xyzMatrix.vals[1][1] = array[4];
+    xyzMatrix.vals[2][1] = array[5];
+    xyzMatrix.vals[0][2] = array[6];
+    xyzMatrix.vals[1][2] = array[7];
+    xyzMatrix.vals[2][2] = array[8];
     env->ReleaseFloatArrayElements(xyzD50, array, 0);
     return xyzMatrix;
 }
@@ -456,12 +456,14 @@
     if (colorSpace == nullptr) return nullptr;
     if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) {
         doThrowIAE(env, "The color space must be an RGB color space");
+        return nullptr;
     }
 
     jobject transferParams = env->CallObjectMethod(colorSpace,
             gColorSpaceRGB_getTransferParametersMethodID);
     if (transferParams == nullptr) {
         doThrowIAE(env, "The color space must use an ICC parametric transfer function");
+        return nullptr;
     }
 
     jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class,
@@ -472,8 +474,8 @@
     jfloatArray xyzD50 = (jfloatArray) env->CallObjectMethod(colorSpaceD50,
             gColorSpaceRGB_getTransformMethodID);
 
-    SkMatrix44 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
-    SkColorSpaceTransferFn transferFunction = getNativeTransferParameters(env, transferParams);
+    skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
+    skcms_TransferFunction transferFunction = getNativeTransferParameters(env, transferParams);
 
     return SkColorSpace::MakeRGB(transferFunction, xyzMatrix);
 }
@@ -499,30 +501,30 @@
         } else if (decodeColorSpace.get() != nullptr) {
             // Try to match against known RGB color spaces using the CIE XYZ D50
             // conversion matrix and numerical transfer function parameters
-            SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
+            skcms_Matrix3x3 xyzMatrix;
             LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
 
-            SkColorSpaceTransferFn transferParams;
+            skcms_TransferFunction transferParams;
             // We can only handle numerical transfer functions at the moment
             LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
 
             jobject params = env->NewObject(gTransferParameters_class,
                     gTransferParameters_constructorMethodID,
-                    transferParams.fA, transferParams.fB, transferParams.fC,
-                    transferParams.fD, transferParams.fE, transferParams.fF,
-                    transferParams.fG);
+                    transferParams.a, transferParams.b, transferParams.c,
+                    transferParams.d, transferParams.e, transferParams.f,
+                    transferParams.g);
 
             jfloatArray xyzArray = env->NewFloatArray(9);
             jfloat xyz[9] = {
-                    xyzMatrix.getFloat(0, 0),
-                    xyzMatrix.getFloat(1, 0),
-                    xyzMatrix.getFloat(2, 0),
-                    xyzMatrix.getFloat(0, 1),
-                    xyzMatrix.getFloat(1, 1),
-                    xyzMatrix.getFloat(2, 1),
-                    xyzMatrix.getFloat(0, 2),
-                    xyzMatrix.getFloat(1, 2),
-                    xyzMatrix.getFloat(2, 2)
+                    xyzMatrix.vals[0][0],
+                    xyzMatrix.vals[1][0],
+                    xyzMatrix.vals[2][0],
+                    xyzMatrix.vals[0][1],
+                    xyzMatrix.vals[1][1],
+                    xyzMatrix.vals[2][1],
+                    xyzMatrix.vals[0][2],
+                    xyzMatrix.vals[1][2],
+                    xyzMatrix.vals[2][2]
             };
             env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index b0bd683..699d153 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -10,7 +10,6 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkColorSpace.h"
-#include "SkMatrix44.h"
 #include <jni.h>
 #include <hwui/Canvas.h>
 #include <hwui/Bitmap.h>
@@ -101,8 +100,8 @@
             int srcStride, int x, int y, int width, int height,
             SkBitmap* dstBitmap);
 
-    static SkColorSpaceTransferFn getNativeTransferParameters(JNIEnv* env, jobject transferParams);
-    static SkMatrix44 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
+    static skcms_TransferFunction getNativeTransferParameters(JNIEnv* env, jobject transferParams);
+    static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
     static sk_sp<SkColorSpace> getNativeColorSpace(JNIEnv* env, jobject colorSpace);
 
     static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e97c9bc..e2e3042 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -557,6 +557,41 @@
         return result;
     }
 
+    // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow
+    // passing the SkColorSpace directly from JNI.
+    static void setColor(JNIEnv* env, jobject clazz, jlong paintHandle, jobject jColorSpace,
+            jfloat r, jfloat g, jfloat b, jfloat a) {
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
+        if (GraphicsJNI::hasException(env)) {
+            return;
+        }
+
+        SkColor4f color = SkColor4f{r, g, b, a};
+        reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
+    }
+
+    // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow
+    // passing the SkColorSpace directly from JNI.
+    static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
+                               jfloat dx, jfloat dy, jobject jColorSpace,
+                               jfloat r, jfloat g, jfloat b, jfloat a) {
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
+        if (GraphicsJNI::hasException(env)) {
+            return;
+        }
+
+        SkColor4f color = SkColor4f{r, g, b, a};
+
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (radius <= 0) {
+            paint->setLooper(nullptr);
+        }
+        else {
+            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+            paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
+        }
+    }
+
     // ------------------ @FastNative ---------------------------
 
     static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
@@ -752,22 +787,6 @@
         obj->setStyle(style);
     }
 
-    static jint getColor(jlong paintHandle) {
-        int color;
-        color = reinterpret_cast<Paint*>(paintHandle)->getColor();
-        return static_cast<jint>(color);
-    }
-
-    static jint getAlpha(jlong paintHandle) {
-        int alpha;
-        alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
-        return static_cast<jint>(alpha);
-    }
-
-    static void setColor(jlong paintHandle, jint color) {
-        reinterpret_cast<Paint*>(paintHandle)->setColor(color);
-    }
-
     static void setAlpha(jlong paintHandle, jint a) {
         reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
     }
@@ -1015,18 +1034,6 @@
         return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize);
     }
 
-    static void setShadowLayer(jlong paintHandle, jfloat radius,
-                               jfloat dx, jfloat dy, jint color) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        if (radius <= 0) {
-            paint->setLooper(nullptr);
-        }
-        else {
-            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
-            paint->setLooper(SkBlurDrawLooper::Make((SkColor)color, sigma, dx, dy));
-        }
-    }
-
     static jboolean hasShadowLayer(jlong paintHandle) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
@@ -1075,6 +1082,9 @@
     {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
     {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
+    {"nSetColor","(JLandroid/graphics/ColorSpace;FFFF)V", (void*) PaintGlue::setColor},
+    {"nSetShadowLayer", "(JFFFLandroid/graphics/ColorSpace;FFFF)V",
+            (void*)PaintGlue::setShadowLayer},
 
     // --------------- @FastNative ----------------------
 
@@ -1104,9 +1114,6 @@
     {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
     {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
     {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
-    {"nGetColor","(J)I", (void*) PaintGlue::getColor},
-    {"nSetColor","(JI)V", (void*) PaintGlue::setColor},
-    {"nGetAlpha","(J)I", (void*) PaintGlue::getAlpha},
     {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
     {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
     {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
@@ -1147,7 +1154,6 @@
     {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
     {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
     {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
-    {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
     {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
     {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
 };
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 49a24a3..b2d3651 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -281,15 +281,18 @@
     std::unique_ptr<NativeCode> code;
     bool needs_native_bridge = false;
 
+    char* nativeloader_error_msg = nullptr;
     void* handle = OpenNativeLibrary(env,
                                      sdkVersion,
                                      pathStr.c_str(),
                                      classLoader,
                                      libraryPath,
                                      &needs_native_bridge,
-                                     &g_error_msg);
+                                     &nativeloader_error_msg);
 
     if (handle == nullptr) {
+        g_error_msg = nativeloader_error_msg;
+        NativeLoaderFreeErrorMessage(nativeloader_error_msg);
         ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s",
               pathStr.c_str(),
               g_error_msg.c_str());
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 12a8343b4..67c3064 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -841,6 +841,43 @@
     return jStatus;
 }
 
+static int android_media_AudioRecord_set_microphone_direction(JNIEnv *env, jobject thiz,
+                                                              jint direction) {
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+    if (lpRecorder == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve AudioRecord pointer for setMicrophoneDirection()");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    jint jStatus = AUDIO_JAVA_SUCCESS;
+    status_t status =
+        lpRecorder->setMicrophoneDirection(static_cast<audio_microphone_direction_t>(direction));
+    if (status != NO_ERROR) {
+        jStatus = nativeToJavaStatus(status);
+    }
+
+    return jStatus;
+}
+
+static int android_media_AudioRecord_set_microphone_field_dimension(JNIEnv *env, jobject thiz,
+                                                                    jfloat zoom) {
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+    if (lpRecorder == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve AudioRecord pointer for setMicrophoneFieldDimension()");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    jint jStatus = AUDIO_JAVA_SUCCESS;
+    status_t status = lpRecorder->setMicrophoneFieldDimension(zoom);
+    if (status != NO_ERROR) {
+        jStatus = nativeToJavaStatus(status);
+    }
+
+    return jStatus;
+}
+
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_get_port_id(JNIEnv *env,  jobject thiz) {
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
@@ -896,6 +933,10 @@
     {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
                                         (void *)android_media_AudioRecord_get_active_microphones},
     {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
+    {"native_set_microphone_direction", "(I)I",
+                                (void *)android_media_AudioRecord_set_microphone_direction},
+    {"native_set_microphone_field_dimension", "(F)I",
+                                (void *)android_media_AudioRecord_set_microphone_field_dimension},
 };
 
 // field names found in android/media/AudioRecord.java
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 888dab1..7d63ec9 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -29,7 +29,6 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <atomic>
 #include <iomanip>
 #include <string>
 #include <vector>
@@ -42,6 +41,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include "jni.h"
+#include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <memtrack/memtrack.h>
 #include <memunreachable/memunreachable.h>
@@ -150,14 +150,6 @@
     int swappedOutPss;
 };
 
-enum pss_rollup_support {
-  PSS_ROLLUP_UNTRIED,
-  PSS_ROLLUP_SUPPORTED,
-  PSS_ROLLUP_UNSUPPORTED
-};
-
-static std::atomic<pss_rollup_support> g_pss_rollup_support;
-
 #define BINDER_STATS "/proc/binder/stats"
 
 static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
@@ -555,37 +547,9 @@
     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
 }
 
-UniqueFile OpenSmapsOrRollup(int pid)
-{
-    enum pss_rollup_support rollup_support =
-            g_pss_rollup_support.load(std::memory_order_relaxed);
-    if (rollup_support != PSS_ROLLUP_UNSUPPORTED) {
-        std::string smaps_rollup_path =
-                base::StringPrintf("/proc/%d/smaps_rollup", pid);
-        UniqueFile fp_rollup = MakeUniqueFile(smaps_rollup_path.c_str(), "re");
-        if (fp_rollup == nullptr && errno != ENOENT) {
-            return fp_rollup;  // Actual error, not just old kernel.
-        }
-        if (fp_rollup != nullptr) {
-            if (rollup_support == PSS_ROLLUP_UNTRIED) {
-                ALOGI("using rollup pss collection");
-                g_pss_rollup_support.store(PSS_ROLLUP_SUPPORTED,
-                                           std::memory_order_relaxed);
-            }
-            return fp_rollup;
-        }
-        g_pss_rollup_support.store(PSS_ROLLUP_UNSUPPORTED,
-                                   std::memory_order_relaxed);
-    }
-
-    std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
-    return MakeUniqueFile(smaps_path.c_str(), "re");
-}
-
 static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
         jlongArray outUssSwapPssRss, jlongArray outMemtrack)
 {
-    char lineBuffer[1024];
     jlong pss = 0;
     jlong rss = 0;
     jlong swapPss = 0;
@@ -597,59 +561,14 @@
         pss = uss = rss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
     }
 
-    {
-        UniqueFile fp = OpenSmapsOrRollup(pid);
-
-        if (fp != nullptr) {
-            char* line;
-
-            while (true) {
-                if (fgets(lineBuffer, sizeof (lineBuffer), fp.get()) == NULL) {
-                    break;
-                }
-                line = lineBuffer;
-
-                switch (line[0]) {
-                    case 'P':
-                        if (strncmp(line, "Pss:", 4) == 0) {
-                            char* c = line + 4;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            pss += atoi(c);
-                        } else if (strncmp(line, "Private_Clean:", 14) == 0
-                                    || strncmp(line, "Private_Dirty:", 14) == 0) {
-                            char* c = line + 14;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            uss += atoi(c);
-                        }
-                        break;
-                    case 'R':
-                        if (strncmp(line, "Rss:", 4) == 0) {
-                            char* c = line + 4;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            rss += atoi(c);
-                        }
-                        break;
-                    case 'S':
-                        if (strncmp(line, "SwapPss:", 8) == 0) {
-                            char* c = line + 8;
-                            jlong lSwapPss;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            lSwapPss = atoi(c);
-                            swapPss += lSwapPss;
-                            pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP
-                        }
-                        break;
-                }
-            }
-        }
+    ::android::meminfo::ProcMemInfo proc_mem(pid);
+    ::android::meminfo::MemUsage stats;
+    if (proc_mem.SmapsOrRollup(&stats)) {
+        pss += stats.pss;
+        uss += stats.uss;
+        rss += stats.rss;
+        swapPss = stats.swap_pss;
+        pss += swapPss; // Also in swap, those pages would be accounted as Pss without SWAP
     }
 
     if (outUssSwapPssRss != NULL) {
@@ -686,34 +605,6 @@
     return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
 }
 
-static long get_allocated_vmalloc_memory() {
-    char line[1024];
-
-    long vmalloc_allocated_size = 0;
-
-    UniqueFile fp = MakeUniqueFile("/proc/vmallocinfo", "re");
-    if (fp == nullptr) {
-        return 0;
-    }
-
-    while (true) {
-        if (fgets(line, 1024, fp.get()) == NULL) {
-            break;
-        }
-
-        // check to see if there are pages mapped in vmalloc area
-        if (!strstr(line, "pages=")) {
-            continue;
-        }
-
-        long nr_pages;
-        if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
-            vmalloc_allocated_size += (nr_pages * getpagesize());
-        }
-    }
-    return vmalloc_allocated_size;
-}
-
 // The 1:1 mapping of MEMINFO_* enums here must match with the constants from
 // Debug.java.
 enum {
@@ -763,9 +654,8 @@
     if (outArray != NULL) {
         outLen = MEMINFO_COUNT;
         for (int i = 0; i < outLen; i++) {
-            // TODO: move get_allocated_vmalloc_memory() to libmeminfo
             if (i == MEMINFO_VMALLOC_USED) {
-                outArray[i] = get_allocated_vmalloc_memory() / 1024;
+                outArray[i] = smi.ReadVmallocInfo() / 1024;
                 continue;
             }
             outArray[i] = mem[i];
@@ -775,7 +665,6 @@
     env->ReleaseLongArrayElements(out, outArray, 0);
 }
 
-
 static jint read_binder_stat(const char* stat)
 {
     UniqueFile fp = MakeUniqueFile(BINDER_STATS, "re");
diff --git a/core/jni/android_os_Debug.h b/core/jni/android_os_Debug.h
index c7b731b..747776a 100644
--- a/core/jni/android_os_Debug.h
+++ b/core/jni/android_os_Debug.h
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <stdio.h>
+#include <meminfo/meminfo.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 
@@ -34,8 +35,6 @@
     return UniqueFile(fopen(path, mode), safeFclose);
 }
 
-UniqueFile OpenSmapsOrRollup(int pid);
-
 }  // namespace android
 
 #endif  // ANDROID_OS_HW_BLOB_H
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 163b86b..42e3942 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -329,7 +329,7 @@
         return NULL;
     }
 
-    LOG(INFO) << "HwBinder: Starting thread pool for " << serviceName << "::" << ifaceName;
+    LOG(INFO) << "HwBinder: Starting thread pool for getting: " << ifaceName << "/" << serviceName;
     ::android::hardware::ProcessState::self()->startThreadPool();
 
     return JHwRemoteBinder::NewObject(env, service);
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 7ef06dc..3b59321 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -473,7 +473,7 @@
     if (parcel != NULL) {
         int fd = parcel->readFileDescriptor();
         if (fd < 0) return NULL;
-        fd = dup(fd);
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
         if (fd < 0) return NULL;
         return jniCreateFileDescriptor(env, fd);
     }
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7b564ae..4101c04 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -44,6 +44,8 @@
 #include "androidfw/MutexGuard.h"
 #include "androidfw/PosixUtils.h"
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/ResourceUtils.h"
+
 #include "core_jni_helpers.h"
 #include "jni.h"
 #include "nativehelper/JNIHelp.h"
@@ -975,34 +977,7 @@
     return nullptr;
   }
 
-  std::string result;
-  if (name.package != nullptr) {
-    result.append(name.package, name.package_len);
-  }
-
-  if (name.type != nullptr || name.type16 != nullptr) {
-    if (!result.empty()) {
-      result += ":";
-    }
-
-    if (name.type != nullptr) {
-      result.append(name.type, name.type_len);
-    } else {
-      result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
-    }
-  }
-
-  if (name.entry != nullptr || name.entry16 != nullptr) {
-    if (!result.empty()) {
-      result += "/";
-    }
-
-    if (name.entry != nullptr) {
-      result.append(name.entry, name.entry_len);
-    } else {
-      result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
-    }
-  }
+  std::string result = ToFormattedResourceString(&name);
   return env->NewStringUTF(result.c_str());
 }
 
@@ -1049,6 +1024,26 @@
   return nullptr;
 }
 
+static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/,
+                                                      jclass /*clazz*/,
+                                                      jlong ptr,
+                                                      jboolean enabled) {
+  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+  assetmanager->SetResourceResolutionLoggingEnabled(enabled);
+}
+
+static jstring NativeGetLastResourceResolution(JNIEnv* env,
+                                               jclass /*clazz*/,
+                                               jlong ptr) {
+  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+  std::string resolution = assetmanager->GetLastResourceResolution();
+  if (resolution.empty()) {
+    return nullptr;
+  } else {
+    return env->NewStringUTF(resolution.c_str());
+  }
+}
+
 static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
                                      jboolean exclude_system) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
@@ -1452,6 +1447,10 @@
     {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
     {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
     {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
+    {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
+     (void*) NativeSetResourceResolutionLoggingEnabled},
+    {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
+     (void*) NativeGetLastResourceResolution},
     {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
     {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
      (void*)NativeGetSizeConfigurations},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 3329e20..f201ceb 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -875,6 +875,11 @@
     return IPCThreadState::self()->getCallingUid();
 }
 
+static jboolean android_os_Binder_isHandlingTransaction()
+{
+    return IPCThreadState::self()->isServingCall();
+}
+
 static jlong android_os_Binder_clearCallingIdentity()
 {
     return IPCThreadState::self()->clearCallingIdentity();
@@ -960,6 +965,8 @@
     // @CriticalNative
     { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
     // @CriticalNative
+    { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction },
+    // @CriticalNative
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
     // @CriticalNative
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 0c1a8aa..26a474c 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -22,11 +22,12 @@
 #include <utils/Log.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-#include <cutils/sched_policy.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
+#include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <processgroup/processgroup.h>
+#include <processgroup/sched_policy.h>
 
 #include <string>
 #include <vector>
@@ -1083,21 +1084,12 @@
 
 static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid)
 {
-    UniqueFile file = OpenSmapsOrRollup(pid);
-    if (file == nullptr) {
+    ::android::meminfo::ProcMemInfo proc_mem(pid);
+    uint64_t pss;
+    if (!proc_mem.SmapsOrRollupPss(&pss)) {
         return (jlong) -1;
     }
 
-    // Tally up all of the Pss from the various maps
-    char line[256];
-    jlong pss = 0;
-    while (fgets(line, sizeof(line), file.get())) {
-        jlong v;
-        if (sscanf(line, "Pss: %" SCNd64 " kB", &v) == 1) {
-            pss += v;
-        }
-    }
-
     // Return the Pss value in bytes, not kilobytes
     return pss * 1024;
 }
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 10da892..aa10a2f 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -138,6 +138,7 @@
                 event->getDeviceId(), event->getSource(), event->getDisplayId(),
                 event->getAction(), event->getActionButton(), event->getFlags(),
                 event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
+                event->getClassification(),
                 event->getXOffset(), event->getYOffset(),
                 event->getXPrecision(), event->getYPrecision(),
                 event->getDownTime(), event->getHistoricalEventTime(i),
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index ecf8119..50cff5c 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -334,7 +334,7 @@
 static jlong android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
         jlong nativePtr,
         jint deviceId, jint source, jint displayId, jint action, jint flags, jint edgeFlags,
-        jint metaState, jint buttonState,
+        jint metaState, jint buttonState, jint classification,
         jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
         jlong downTimeNanos, jlong eventTimeNanos,
         jint pointerCount, jobjectArray pointerPropertiesObjArray,
@@ -373,7 +373,8 @@
     }
 
     event->initialize(deviceId, source, displayId, action, 0, flags, edgeFlags, metaState,
-            buttonState, xOffset, yOffset, xPrecision, yPrecision,
+            buttonState, static_cast<MotionClassification>(classification),
+            xOffset, yOffset, xPrecision, yPrecision,
             downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
 
     return reinterpret_cast<jlong>(event);
@@ -668,6 +669,11 @@
     event->setButtonState(buttonState);
 }
 
+static jint android_view_MotionEvent_nativeGetClassification(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return static_cast<jint>(event->getClassification());
+}
+
 static void android_view_MotionEvent_nativeOffsetLocation(jlong nativePtr, jfloat deltaX,
         jfloat deltaY) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -747,7 +753,7 @@
 static const JNINativeMethod gMotionEventMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInitialize",
-            "(JIIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
+            "(JIIIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
                     "[Landroid/view/MotionEvent$PointerCoords;)J",
             (void*)android_view_MotionEvent_nativeInitialize },
     { "nativeDispose",
@@ -846,6 +852,9 @@
     { "nativeSetButtonState",
             "(JI)V",
             (void*)android_view_MotionEvent_nativeSetButtonState },
+    { "nativeGetClassification",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetClassification },
     { "nativeOffsetLocation",
             "(JFF)V",
             (void*)android_view_MotionEvent_nativeOffsetLocation },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 6576587..897427f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -438,8 +438,9 @@
 static jboolean nativeSetDisplayedContentSamplingEnabled(JNIEnv* env, jclass clazz,
         jobject tokenObj, jboolean enable, jint componentMask, jint maxFrames) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
-    return SurfaceComposerClient::setDisplayContentSamplingEnabled(
+    status_t rc = SurfaceComposerClient::setDisplayContentSamplingEnabled(
             token, enable, componentMask, maxFrames);
+    return rc == OK;
 }
 
 static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobject tokenObj,
@@ -641,6 +642,27 @@
     return static_cast<jint>(SurfaceComposerClient::getActiveColorMode(token));
 }
 
+static jintArray nativeGetCompositionDataspaces(JNIEnv* env, jclass) {
+    ui::Dataspace defaultDataspace, wcgDataspace;
+    ui::PixelFormat defaultPixelFormat, wcgPixelFormat;
+    if (SurfaceComposerClient::getCompositionPreference(&defaultDataspace,
+                                                        &defaultPixelFormat,
+                                                        &wcgDataspace,
+                                                        &wcgPixelFormat) != NO_ERROR) {
+        return nullptr;
+    }
+    jintArray array = env->NewIntArray(2);
+    if (array == nullptr) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+        return nullptr;
+    }
+    jint* arrayValues = env->GetIntArrayElements(array, 0);
+    arrayValues[0] = static_cast<jint>(defaultDataspace);
+    arrayValues[1] = static_cast<jint>(wcgDataspace);
+    env->ReleaseIntArrayElements(array, arrayValues, 0);
+    return array;
+}
+
 static jboolean nativeSetActiveColorMode(JNIEnv* env, jclass,
         jobject tokenObj, jint colorMode) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
@@ -659,6 +681,10 @@
     if (t.duration() > 100ms) ALOGD("Excessive delay in setPowerMode()");
 }
 
+static jboolean nativeGetProtectedContentSupport(JNIEnv* env, jclass) {
+    return static_cast<jboolean>(SurfaceComposerClient::getProtectedContentSupport());
+}
+
 static jboolean nativeClearContentFrameStats(JNIEnv* env, jclass clazz, jlong nativeObject) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->clearLayerFrameStats();
@@ -869,14 +895,6 @@
     transaction->setOverrideScalingMode(ctrl, scalingMode);
 }
 
-static void nativeDestroyInTransaction(JNIEnv* env, jclass clazz,
-                                       jlong transactionObj,
-                                       jlong nativeObject) {
-    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-    auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
-    transaction->destroySurface(ctrl);
-}
-
 static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     return javaObjectForIBinder(env, ctrl->getHandle());
@@ -916,6 +934,17 @@
     return reinterpret_cast<jlong>(surface.get());
 }
 
+static jlong nativeCopyFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
+    sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
+    if (surface == nullptr) {
+        return 0;
+    }
+
+    sp<SurfaceControl> newSurface = new SurfaceControl(surface);
+    newSurface->incStrong((void *)nativeCreate);
+    return reinterpret_cast<jlong>(newSurface.get());
+}
+
 static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
         jlong nativeObject, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
@@ -924,7 +953,9 @@
         return;
     }
     SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject);
-    self->writeToParcel(parcel);
+    if (self != nullptr) {
+        self->writeToParcel(parcel);
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -934,6 +965,8 @@
             (void*)nativeCreate },
     {"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
             (void*)nativeReadFromParcel },
+    {"nativeCopyFromSurfaceControl", "(J)J" ,
+            (void*)nativeCopyFromSurfaceControl },
     {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
             (void*)nativeWriteToParcel },
     {"nativeRelease", "(J)V",
@@ -1008,6 +1041,8 @@
             (void*)nativeGetActiveColorMode},
     {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
             (void*)nativeSetActiveColorMode},
+    {"nativeGetCompositionDataspaces", "()[I",
+            (void*)nativeGetCompositionDataspaces},
     {"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;",
             (void*)nativeGetHdrCapabilities },
     {"nativeClearContentFrameStats", "(J)Z",
@@ -1020,6 +1055,8 @@
             (void*)nativeGetAnimationFrameStats },
     {"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V",
             (void*)nativeSetDisplayPowerMode },
+    {"nativeGetProtectedContentSupport", "()Z",
+            (void*)nativeGetProtectedContentSupport },
     {"nativeDeferTransactionUntil", "(JJLandroid/os/IBinder;J)V",
             (void*)nativeDeferTransactionUntil },
     {"nativeDeferTransactionUntilSurface", "(JJJJ)V",
@@ -1032,8 +1069,6 @@
             (void*)nativeSeverChildren } ,
     {"nativeSetOverrideScalingMode", "(JJI)V",
             (void*)nativeSetOverrideScalingMode },
-    {"nativeDestroy", "(JJ)V",
-            (void*)nativeDestroyInTransaction },
     {"nativeGetHandle", "(J)Landroid/os/IBinder;",
             (void*)nativeGetHandle },
     {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 30c0030..191f748 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -46,13 +46,6 @@
     return reinterpret_cast<jlong>(client);
 }
 
-static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
-    Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
-    SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
-    client->incStrong((void*)nativeCreate);
-    return reinterpret_cast<jlong>(client);
-}
-
 static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
     SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
     client->decStrong((void*)nativeCreate);
@@ -67,8 +60,6 @@
     /* name, signature, funcPtr */
     { "nativeCreate", "()J",
             (void*)nativeCreate },
-    { "nativeCreateScoped", "(J)J",
-            (void*)nativeCreateScoped },
     { "nativeDestroy", "(J)V",
             (void*)nativeDestroy },
     { "nativeKill", "(J)V",
diff --git a/core/jni/com_android_internal_os_AtomicDirectory.cpp b/core/jni/com_android_internal_os_AtomicDirectory.cpp
new file mode 100644
index 0000000..50b2288
--- /dev/null
+++ b/core/jni/com_android_internal_os_AtomicDirectory.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 <nativehelper/ScopedUtfChars.h>
+#include "jni.h"
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+static jint com_android_internal_os_AtomicDirectory_getDirectoryFd(JNIEnv* env,
+        jobject /*clazz*/, jstring path) {
+    ScopedUtfChars path8(env, path);
+    if (path8.c_str() == NULL) {
+        ALOGE("Invalid path: %s", path8.c_str());
+        return -1;
+    }
+    int fd;
+    if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY))) == -1) {
+        ALOGE("Cannot open directory %s, error: %s\n", path8.c_str(), strerror(errno));
+        return -1;
+    }
+    return fd;
+}
+
+static void com_android_internal_os_AtomicDirectory_fsyncDirectoryFd(JNIEnv* env,
+        jobject /*clazz*/, jint fd) {
+    if (TEMP_FAILURE_RETRY(fsync(fd)) == -1) {
+        ALOGE("Cannot fsync directory %d, error: %s\n", fd, strerror(errno));
+    }
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gRegisterMethods[] = {
+    /* name, signature, funcPtr */
+    { "fsyncDirectoryFd",
+      "(I)V",
+       (void*) com_android_internal_os_AtomicDirectory_fsyncDirectoryFd
+    },
+    { "getDirectoryFd",
+      "(Ljava/lang/String;)I",
+       (void*) com_android_internal_os_AtomicDirectory_getDirectoryFd
+    },
+};
+
+int register_com_android_internal_os_AtomicDirectory(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "com/android/internal/os/AtomicDirectory",
+            gRegisterMethods, NELEM(gRegisterMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index a8e1427..7aee833 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+/*
+ * Disable optimization of this file if we are compiling with the address
+ * sanitizer.  This is a mitigation for b/122921367 and can be removed once the
+ * bug is fixed.
+ */
+#if __has_feature(address_sanitizer)
+#pragma clang optimize off
+#endif
+
 #define LOG_TAG "Zygote"
 
 // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
@@ -53,13 +62,13 @@
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
 #include <cutils/multiuser.h>
-#include <cutils/sched_policy.h>
 #include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 #include <selinux/android.h>
 #include <seccomp_policy.h>
 #include <stats_event_list.h>
 #include <processgroup/processgroup.h>
+#include <processgroup/sched_policy.h>
 
 #include "core_jni_helpers.h"
 #include <nativehelper/JNIHelp.h>
@@ -86,6 +95,7 @@
 static pid_t gSystemServerPid = 0;
 
 static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
+static const char kIsolatedStorageSnapshot[] = "sys.isolated_storage_snapshot";
 static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
 static jclass gZygoteClass;
 static jmethodID gCallPostForkSystemServerHooks;
@@ -303,7 +313,7 @@
   mallopt(M_DECAY_TIME, 1);
 }
 
-static void SetUpSeccompFilter(uid_t uid) {
+static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
   if (!g_is_security_enforced) {
     ALOGI("seccomp disabled by setenforce 0");
     return;
@@ -311,7 +321,14 @@
 
   // Apply system or app filter based on uid.
   if (uid >= AID_APP_START) {
-    set_app_seccomp_filter();
+    if (is_child_zygote) {
+      // set_app_zygote_seccomp_filter();
+      // TODO(b/111434506) install the filter; for now, install the app filter
+      // which is more restrictive.
+      set_app_seccomp_filter();
+    } else {
+      set_app_seccomp_filter();
+    }
   } else {
     set_system_seccomp_filter();
   }
@@ -530,7 +547,7 @@
         return true;
     }
 
-    if (GetBoolProperty(kIsolatedStorage, false)) {
+    if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false))) {
         if (mount_mode == MOUNT_EXTERNAL_FULL) {
             storageSource = "/mnt/runtime/write";
             if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
@@ -996,7 +1013,7 @@
   // alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
   // breaks SELinux domain transition (see b/71859146).  As the result,
   // privileged syscalls used below still need to be accessible in app process.
-  SetUpSeccompFilter(uid);
+  SetUpSeccompFilter(uid, is_child_zygote);
 
   if (setresuid(uid, uid, uid) == -1) {
     fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
@@ -1295,6 +1312,23 @@
     UnmountTree("/storage");
 }
 
+static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
+        JNIEnv* env, jclass, jint uidGidMin, jint uidGidMax) {
+  if (!g_is_security_enforced) {
+    ALOGI("seccomp disabled by setenforce 0");
+    return;
+  }
+
+  // TODO(b/111434506) install the filter
+
+  /*
+  bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax);
+  if (!installed) {
+      RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter.");
+  }
+  */
+}
+
 static const JNINativeMethod gMethods[] = {
     { "nativeSecurityInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeSecurityInit },
@@ -1308,7 +1342,9 @@
     { "nativeUnmountStorageOnInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit },
     { "nativePreApplicationInit", "()V",
-      (void *) com_android_internal_os_Zygote_nativePreApplicationInit }
+      (void *) com_android_internal_os_Zygote_nativePreApplicationInit },
+    { "nativeInstallSeccompUidGidFilter", "(II)V",
+      (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter }
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index bd87dcc..41e00b9 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -33,6 +33,7 @@
 
 // Static whitelist of open paths that the zygote is allowed to keep open.
 static const char* kPathWhitelist[] = {
+  "/apex/com.android.conscrypt/javalib/conscrypt.jar",
   "/dev/null",
   "/dev/socket/zygote",
   "/dev/socket/zygote_secondary",
diff --git a/core/proto/Android.bp b/core/proto/Android.bp
new file mode 100644
index 0000000..80cc2d4
--- /dev/null
+++ b/core/proto/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+// C++ library for Bluetooth platform wide protobuf definitions
+cc_library_static {
+    name: "libbt-platform-protos-lite",
+    host_supported: true,
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: [
+        "android/bluetooth/enums.proto",
+        "android/bluetooth/hci/enums.proto",
+    ],
+}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3e1c5a3..f68c760 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -27,8 +27,574 @@
     PAGE_VISIBLE = 1;
     PAGE_HIDE = 2;
 
+    // ACTION: Settings > Wi-Fi > [Long press network] > Connect to network
+    //   SUBTYPE: true if connecting to a saved network, false if not
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_WIFI_CONNECT = 135;
+
+    // ACTION: Settings > Wi-Fi > [Long press network] > Forget network
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_WIFI_FORGET = 137;
+
+    // ACTION: Settings > Wi-Fi > Toggle off
+    //   SUBTYPE: true if connected to network before toggle, false if not
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_WIFI_OFF = 138;
+
+    // ACTION: Settings > Wi-Fi > Toggle on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_WIFI_ON = 139;
+
+    // ACTION: Settings > Bluetooth > Overflow > Rename this device
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_BLUETOOTH_RENAME = 161;
+
+    // ACTION: Settings > Bluetooth > Overflow > Show received files
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_BLUETOOTH_FILES = 162;
+
+    // ACTION: DND Settings > Priority only allows > Reminder toggle
+    //   SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ZEN_ALLOW_REMINDERS = 167;
+
+    // ACTION: DND Settings > Priority only allows > Event toggle
+    //   SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ZEN_ALLOW_EVENTS = 168;
+
+    // ACTION: DND Settings > Priority only allows > Messages
+    //   SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ZEN_ALLOW_MESSAGES = 169;
+
+    // ACTION: DND Settings > Priority only allows > Calls
+    //   SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ZEN_ALLOW_CALLS = 170;
+
+    // ACTION: DND Settings > Priority only allows > Repeat callers toggle
+    //   SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ZEN_ALLOW_REPEAT_CALLS = 171;
+
+    // ACTION: DND Settings > Automatic rules > [Rule] > Delete rule > Delete
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ZEN_DELETE_RULE_OK = 175;
+
+    // ACTION: Settings > More > Airplane mode toggle
+    //   SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_AIRPLANE_TOGGLE = 177;
+
+    // ACTION: Settings > Data usage > Cellular data toggle
+    //   SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_CELL_DATA_TOGGLE = 178;
+
+    // ACTION: Settings > Display > When device is rotated
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ROTATION_LOCK = 203;
+
+    // OPEN: Settings > Search > Perform search
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_SEARCH_RESULTS = 226;
+
+    // ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Delete
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_FINGERPRINT_DELETE = 253;
+
+    // ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Rename
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_FINGERPRINT_RENAME = 254;
+
+    // ACTION: Settings -> Developer Options -> Take bug report -> Interactive report
+    // CATEGORY: SETTINGS
+    // OS: N
+    // Interactive bug report initiated from Settings.
+    ACTION_BUGREPORT_FROM_SETTINGS_INTERACTIVE = 294;
+
+    // ACTION: Settings -> Developer Options -> Take bug report -> Full report
+    // CATEGORY: SETTINGS
+    // OS: N
+    // Interactive bug report initiated from Settings.
+    ACTION_BUGREPORT_FROM_SETTINGS_FULL = 295;
+
+    // click on collapsed conditional or clicks expand button
+    ACTION_SETTINGS_CONDITION_EXPAND = 373;
+
+    // click main area of expanded conditional
+    ACTION_SETTINGS_CONDITION_CLICK = 375;
+
+    // click a direct button on expanded conditional
+    ACTION_SETTINGS_CONDITION_BUTTON = 376;
+
+    // Action: user enable / disabled data saver using Settings
+    // OPEN: Settings -> Data Usage -> Data saver -> On/off toggle
+    // VALUE: 1 for enabled, 0 for disabled
+    // CATEGORY: SETTINGS
+    // OS: N
+    ACTION_DATA_SAVER_MODE = 394;
+
+    // User whitelisted an app for Data Saver mode; action pass package name of app
+    // Action: user enable / disabled data saver using Settings
+    // OPEN: Settings -> Data Usage -> Data saver -> Unrestricted data access > APP toggle turned on
+    //       or
+    //       Settings -> Apps -> APP -> Data usage -> Unrestricted data usage toggle turned on
+    // VALUE: package name of APP
+    // CATEGORY: SETTINGS
+    // OS: N
+    ACTION_DATA_SAVER_WHITELIST = 395;
+
+    // User blacklisted an app for Data Saver mode; action pass package name of app
+    // OPEN: Settings -> Apps -> APP -> Data usage -> Background data toggle turned off
+    // VALUE: package name of APP
+    // CATEGORY: SETTINGS
+    // OS: N
+    ACTION_DATA_SAVER_BLACKLIST = 396;
+
+    // ACTION: Settings -> Storage -> Manage storage -> Click Storage Manager
+    //   SUBTYPE: false is off, true is on
+    ACTION_TOGGLE_STORAGE_MANAGER = 489;
+
+    // OPEN: Settings > Display -> Ambient Display
+    // CATEGORY: SETTINGS
+    ACTION_AMBIENT_DISPLAY = 495;
+
+    // ACTION: Allow Battery optimization for an app
+    APP_SPECIAL_PERMISSION_BATTERY_ALLOW = 764;
+
+    // ACTION: Deny Battery optimization for an app
+    APP_SPECIAL_PERMISSION_BATTERY_DENY = 765;
+
+    // ACTION: Enable Device Admin app
+    APP_SPECIAL_PERMISSION_ADMIN_ALLOW = 766;
+
+    // ACTION: Disable Device Admin app
+    APP_SPECIAL_PERMISSION_ADMIN_DENY = 767;
+
+    // ACTION: Allow "Do Not Disturb access" for an app
+    APP_SPECIAL_PERMISSION_DND_ALLOW = 768;
+
+    // ACTION: Deny "Do Not Disturb access" for an app
+    APP_SPECIAL_PERMISSION_DND_DENY = 769;
+
+    // ACTION: Allow "Draw over other apps" for an app
+    APP_SPECIAL_PERMISSION_APPDRAW_ALLOW = 770;
+
+    // ACTION: Deny "Display over other apps" for an app
+    APP_SPECIAL_PERMISSION_APPDRAW_DENY = 771;
+
+    // ACTION: Allow "VR helper services" for an app
+    APP_SPECIAL_PERMISSION_VRHELPER_ALLOW = 772;
+
+    // ACTION: Deny "VR helper services" for an app
+    APP_SPECIAL_PERMISSION_VRHELPER_DENY = 773;
+
+    // ACTION: Allow "Modify system settings" for an app
+    APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW = 774;
+
+    // ACTION: Deny "Modify system settings" for an app
+    APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY = 775;
+
+    // ACTION: Allow "Notification access" for an app
+    APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW = 776;
+
+    // ACTION: Deny "Notification access" for an app
+    APP_SPECIAL_PERMISSION_NOTIVIEW_DENY = 777;
+
+    // ACTION: "Premium SMS access" for an app - "ask user" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK = 778;
+
+    // ACTION: "Premium SMS access" for an app - "never allow" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY = 779;
+
+    // ACTION: "Premium SMS access" for an app - "always allow" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW = 780;
+
+    // ACTION: Allow "Unrestricted data access" for an app
+    APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW = 781;
+
+    // ACTION: Deny "Unrestricted data access" for an app
+    APP_SPECIAL_PERMISSION_UNL_DATA_DENY = 782;
+
+    // ACTION: Allow "Usage access" for an app
+    APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW = 783;
+
+    // ACTION: Deny "Usage access" for an app
+    APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY = 784;
+
+    // ACTION: "Force stop" action on an app
+    ACTION_APP_FORCE_STOP = 807;
+
+    // ACTION: Allow "Enable picture-in-picture" for an app
+    APP_PICTURE_IN_PICTURE_ALLOW = 813;
+
+    // ACTION: Create a Settings shortcut item.
+    ACTION_SETTINGS_CREATE_SHORTCUT = 829;
+
+    // ACTION: Settings advanced button is expanded
+    ACTION_SETTINGS_ADVANCED_BUTTON_EXPAND = 834;
+
+    // ACTION: Deny "Enable picture-in-picture" for an app
+    APP_PICTURE_IN_PICTURE_DENY = 814;
+
+    // ACTION: Settings -> Display -> Theme
+    ACTION_THEME = 816;
+
+    // ACTION: Settings > About device > Build number
+    ACTION_SETTINGS_BUILD_NUMBER_PREF = 847;
+
+    // ACTION: Settings > Battery > Menu > Optimization
+    ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION = 851;
+
+    // ACTION: Settings > Battery > Menu > Apps Toggle
+    ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE = 852;
+
     // ACTION: Settings > Any preference is changed
     ACTION_SETTINGS_PREFERENCE_CHANGE = 853;
+
+    // ACTION: Settings > Connected devices > Bluetooth -> Available devices
+    ACTION_SETTINGS_BLUETOOTH_PAIR = 866;
+
+    // ACTION: Settings > Connected devices > Bluetooth -> Paired devices
+    ACTION_SETTINGS_BLUETOOTH_CONNECT = 867;
+
+    // ACTION: Settings > Connected devices > Bluetooth -> Connected device
+    ACTION_SETTINGS_BLUETOOTH_DISCONNECT = 868;
+
+    // ACTION: Settings > Connected devices > Bluetooth -> Error dialog
+    ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR = 869;
+
+    // ACTION: Settings > Connected devices > Bluetooth master switch Toggle
+    ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870;
+
+    // ACTION: Settings > App detail > Uninstall
+    ACTION_SETTINGS_UNINSTALL_APP = 872;
+
+    // ACTION: Settings > App detail > Uninstall Device admin app
+    ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN = 873;
+
+    // ACTION: Settings > App detail > Disable app
+    ACTION_SETTINGS_DISABLE_APP = 874;
+
+    // ACTION: Settings > App detail > Enable app
+    ACTION_SETTINGS_ENABLE_APP = 875;
+
+    // ACTION: Settings > App detail > Clear data
+    ACTION_SETTINGS_CLEAR_APP_DATA = 876;
+
+    // ACTION: Settings > App detail > Clear cache
+    ACTION_SETTINGS_CLEAR_APP_CACHE = 877;
+
+    // ACTION: Logs pressing the "Clear app" button in the app info settings page for an instant
+    // app.
+    // VALUE: The package name of the app
+    ACTION_SETTINGS_CLEAR_INSTANT_APP = 923;
+
+    // OPEN: Assist Gesture training intro in Settings
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    SETTINGS_ASSIST_GESTURE_TRAINING_INTRO = 991;
+
+    // OPEN: Assist Gesture training enrolling in Settings
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    SETTINGS_ASSIST_GESTURE_TRAINING_ENROLLING = 992;
+
+    // OPEN: Assist Gesture training finished in Settings
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    SETTINGS_ASSIST_GESTURE_TRAINING_FINISHED = 993;
+
+    // ACTION: Update default app from Settings
+    ACTION_SETTINGS_UPDATE_DEFAULT_APP = 1000;
+
+    // ACTION: Settings > Wi-Fi > [Long press network] > Sign in to network
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    ACTION_WIFI_SIGNIN = 1008;
+
+    // ACTION: Settings > Notification Settings > Open application notification
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    ACTION_OPEN_APP_NOTIFICATION_SETTING = 1016;
+
+    // ACTION: Settings > App Info > Open app settings
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    ACTION_OPEN_APP_SETTING = 1017;
+
+    // ACTION: Collect PSD Signals
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    ACTION_PSD_LOADER = 1019;
+
+    // OPEN: Settings > Trampoline Intent > Settings page
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    TRAMPOLINE_SETTINGS_EVENT = 1033;
+
+    // ACTION: Logged when user tries to pair a Bluetooth device without name from Settings app
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES = 1096;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Network
+    // CATEGORY: SETTINGS
+    ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1210;
+
+    // ACTION: DND Settings > Priority only allows > Alarms toggle
+    // SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ALLOW_ALARMS = 1226;
+
+    // ACTION: DND Settings > Priority only allows > Media toggle
+    // SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ALLOW_MEDIA = 1227;
+
+    // ACTION: A private dns mode been selected by user
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_PRIVATE_DNS_MODE = 1249;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name > OK
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK = 1267;
+
+    // OPEN: Settings > Sound > Do Not Disturb > TURN ON NOW/TURN OFF NOW
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_TOGGLE_DND_BUTTON = 1268;
+
+    // ACTION: DND Settings > What to block > full screen intents
+    //   SUBTYPE: false is allowed, true is blocked
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACTION_ZEN_BLOCK_FULL_SCREEN_INTENTS = 1332;
+
+    // ACTION: DND Settings > What to block
+    //   SUBTYPE: false is allowed, true is blocked
+    // OS: P
+    ACTION_ZEN_BLOCK_LIGHT = 1333;
+
+    // ACTION: DND Settings > What to block
+    //   SUBTYPE: false is allowed, true is blocked
+    // OS: P
+    ACTION_ZEN_BLOCK_PEEK = 1334;
+
+    // ACTION: DND Settings > What to block
+    //   SUBTYPE: false is allowed, true is blocked
+    // OS: P
+    ACTION_ZEN_BLOCK_STATUS = 1335;
+
+    // ACTION: DND Settings > What to block
+    //   SUBTYPE: false is allowed, true is blocked
+    // OS: P
+    ACTION_ZEN_BLOCK_BADGE = 1336;
+
+    // ACTION: DND Settings > What to block
+    //   SUBTYPE: false is allowed, true is blocked
+    // OS: P
+    ACTION_ZEN_BLOCK_AMBIENT = 1337;
+
+    // ACTION: DND Settings > What to block
+    //   SUBTYPE: false is allowed, true is blocked
+    // OS: P
+    ACTION_ZEN_BLOCK_NOTIFICATION_LIST = 1338;
+
+    // ACTION: DND Settings > Priority only allows > System toggle
+    // SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ALLOW_SYSTEM = 1340;
+
+    // ACTION: Settings > Battery settings > Battery tip > App restriction tip
+    // OS: P
+    ACTION_APP_RESTRICTION_TIP = 1347;
+
+    // ACTION: Settings > Battery settings > Battery tip > High usage tip
+    // OS: P
+    ACTION_HIGH_USAGE_TIP = 1348;
+
+    // ACTION: Settings > Battery settings > Battery tip > Summary tip
+    // OS: P
+    ACTION_SUMMARY_TIP = 1349;
+
+    // ACTION: Settings > Battery settings > Battery tip > Smart battery tip
+    // OS: P
+    ACTION_SMART_BATTERY_TIP = 1350;
+
+    // ACTION: Settings > Battery settings > Battery tip > Early warning tip
+    // OS: P
+    ACTION_EARLY_WARNING_TIP = 1351;
+
+    // ACTION: Settings > Battery settings > Battery tip > Low battery tip
+    // OS: P
+    ACTION_LOW_BATTERY_TIP = 1352;
+
+    // ACTION: Settings > Battery settings > Battery tip > App restriction list shown
+    // OS: P
+    ACTION_APP_RESTRICTION_TIP_LIST = 1353;
+
+    // ACTION: Settings > Battery settings > Battery tip > High usage list shown
+    // OS: P
+    ACTION_HIGH_USAGE_TIP_LIST = 1354;
+
+    // ACTION: Settings > Battery settings > Battery tip > Open app restriction page
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_OPEN_APP_RESTRICTION_PAGE = 1361;
+
+    // ACTION: Settings > Battery settings > Battery tip > Restrict app
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_RESTRICT_APP = 1362;
+
+    // ACTION: Settings > Battery settings > Battery tip > Unrestrict app
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_UNRESTRICT_APP = 1363;
+
+    // ACTION: Settings > Battery settings > Battery tip > Open smart battery page
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_OPEN_SMART_BATTERY = 1364;
+
+    // ACTION: Settings > Battery settings > Battery tip > Turn on battery saver
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_TURN_ON_BATTERY_SAVER = 1365;
+
+    // ACTION: Settings > Anomaly receiver > Anomaly received
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ANOMALY_TRIGGERED = 1367;
+
+    // ACTION: A Settings Slice is requested
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_SETTINGS_SLICE_REQUESTED = 1371;
+
+    // ACTION: A Settings Slice is updated with new value
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_SETTINGS_SLICE_CHANGED = 1372;
+
+    // OPEN: DND onboarding activity > Ok button
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ONBOARDING_OK = 1378;
+
+    // OPEN: DND onboarding activity > Settings link
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ONBOARDING_SETTINGS = 1379;
+
+    // ACTION: Settings > Anomaly receiver > Anomaly ignored, don't show up in battery settings
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ANOMALY_IGNORED = 1387;
+
+    // ACTION: Settings > Battery settings > Battery tip > Open battery saver page
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_OPEN_BATTERY_SAVER_PAGE = 1388;
+
+    // ACTION: DND Settings > What to block
+    // OS: P
+    ACTION_ZEN_SOUND_ONLY = 1396;
+
+    // ACTION: DND Settings > Notifications
+    // OS: P
+    ACTION_ZEN_SOUND_AND_VIS_EFFECTS = 1397;
+
+    // ACTION: DND Settings > Notifications
+    // OS: P
+    ACTION_ZEN_SHOW_CUSTOM = 1398;
+
+    // ACTION: DND Settings > Notifications
+    // OS: P
+    ACTION_ZEN_CUSTOM = 1399;
+
+    // OPEN: DND onboarding activity > don't update button
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ONBOARDING_KEEP_CURRENT_SETTINGS = 1406;
+
+    // ACTION: Storage initialization wizard initialization choice of external/portable
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_STORAGE_INIT_EXTERNAL = 1407;
+
+    // ACTION: Storage initialization wizard initialization choice of internal/adoptable
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_STORAGE_INIT_INTERNAL = 1408;
+
+    // ACTION: Storage initialization wizard benchmark fast choice of continue
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_STORAGE_BENCHMARK_FAST_CONTINUE = 1409;
+
+    // ACTION: Storage initialization wizard benchmark slow choice of continue
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_STORAGE_BENCHMARK_SLOW_CONTINUE = 1410;
+
+    // ACTION: Storage initialization wizard benchmark slow choice of abort
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_STORAGE_BENCHMARK_SLOW_ABORT = 1411;
+
+    // ACTION: Storage initialization wizard migration choice of now
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_STORAGE_MIGRATE_NOW = 1412;
+
+    // ACTION: Storage initialization wizard migration choice of later
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_STORAGE_MIGRATE_LATER = 1413;
+
+    // OPEN: Settings > Sound > Switch a2dp devices dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_SWITCH_A2DP_DEVICES = 1415;
+
+
+    // OPEN: Settings > Sound > Switch hfp devices dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_SWITCH_HFP_DEVICES = 1416;
+
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Forground
+    ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622;
+
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Default
+    ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
 }
 
 /**
@@ -38,15 +604,1454 @@
     // Unknown page. Should not be used in production code.
     PAGE_UNKNOWN = 0;
 
+    // OPEN: Settings > Accessibility
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCESSIBILITY = 2;
+
+    // OPEN: Settings > Accessibility > Captions
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCESSIBILITY_CAPTION_PROPERTIES = 3;
+
+    // OPEN: Settings > Accessibility > [Service]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCESSIBILITY_SERVICE = 4;
+
+    // OPEN: Settings > Accessibility > Color correction
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCESSIBILITY_TOGGLE_DALTONIZER = 5;
+
+    // OPEN: Settings > Accessibility > Accessibility shortcut
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6;
+
+    // OPEN: Settings > Accessibility > Magnification gestures (Renamed in O)
+    // OPEN: Settings > Accessibility > Magnification > Magnify with triple-tap
+    // OPEN: Settings > Accessibility > Magnification > Magnify with button
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7;
+
+    // OPEN: Settings > Accounts
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCOUNT = 8;
+
+    // OPEN: Settings > Accounts > [Single Account Sync Settings]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCOUNTS_ACCOUNT_SYNC = 9;
+
+    // OPEN: Settings > Accounts > Add an account
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10;
+
+    // OPEN: Settings > Cellular network settings > APNs
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APN = 12;
+
+    // OPEN: Settings > More > Cellular network settings > APNs > [Edit APN]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APN_EDITOR = 13;
+
+    // OPEN: Settings > Apps > Configure apps > App links > [App]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_APP_LAUNCH = 17;
+
+    // OPEN: Settings > Internal storage > Apps storage > [App]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_APP_STORAGE = 19;
+
+    // OPEN: Settings > Apps > [App info]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_INSTALLED_APP_DETAILS = 20;
+
+    // OPEN: Settings > Memory > App usage > [App Memory usage]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_PROCESS_STATS_DETAIL = 21;
+
+    // OPEN: Settings > Memory > App usage
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_PROCESS_STATS_UI = 23;
+
+    // OPEN: Choose Bluetooth device (ex: when sharing)
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    BLUETOOTH_DEVICE_PICKER = 25;
+
+    // OPEN: Settings > Security > Choose screen lock
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    CHOOSE_LOCK_GENERIC = 27;
+
+    // OPEN: Settings > Security > Choose screen lock > Choose your password
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    CHOOSE_LOCK_PASSWORD = 28;
+
+    // OPEN: Settings > Security > Choose screen lock > Choose your pattern
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    CHOOSE_LOCK_PATTERN = 29;
+
+    // OPEN: Settings > Security > Choose screen lock > Confirm your password
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    CONFIRM_LOCK_PASSWORD = 30;
+
+    // OPEN: Settings > Security > Choose screen lock > Confirm your pattern
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    CONFIRM_LOCK_PATTERN = 31;
+
+    // OPEN: Settings > Security > Encrypt phone
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    CRYPT_KEEPER = 32;
+
+    // OPEN: Settings > Security > Encrypt phone > Confirm
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    CRYPT_KEEPER_CONFIRM = 33;
+
+    // OPEN: Settings (Root page)
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DASHBOARD_SUMMARY = 35;
+
+    // OPEN: Settings > Data usage
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DATA_USAGE_SUMMARY = 37;
+
+    // OPEN: Settings > Date & time
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DATE_TIME = 38;
+
+    // OPEN: Settings > Developer options
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DEVELOPMENT = 39;
+
+    // OPEN: Settings > About phone
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DEVICEINFO = 40;
+
+    // OPEN: Settings > Internal storage
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DEVICEINFO_STORAGE = 42;
+
+    // OPEN: Settings > Display
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DISPLAY = 46;
+
+    // OPEN: Settings > Display > Daydream
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    DREAM = 47;
+
+    // OPEN: Settings > Security > Screen lock > Secure start-up
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ENCRYPTION = 48;
+
+    // OPEN: Settings > Security > Nexus Imprint
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT = 49;
+
+    // OPEN: Settings > Battery > History details
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FUELGAUGE_BATTERY_HISTORY_DETAIL = 51;
+
+    // OPEN: Settings > Battery > Battery saver
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FUELGAUGE_BATTERY_SAVER = 52;
+
+    // OPEN: Settings > Battery > [App Use details]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FUELGAUGE_POWER_USAGE_DETAIL = 53;
+
+    // OPEN: Settings > Security > SIM card lock settings
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ICC_LOCK = 56;
+
+    // OPEN: Settings > Language & input > Physical keyboard
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    INPUTMETHOD_KEYBOARD = 58;
+
+    // OPEN: Settings > Language & input > Spell checker
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    INPUTMETHOD_SPELL_CHECKERS = 59;
+
+    // OBSOLETE
+    INPUTMETHOD_SUBTYPE_ENABLER = 60;
+
+    // OPEN: Settings > Language & input > Personal dictionary
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    INPUTMETHOD_USER_DICTIONARY = 61;
+
+    // OPEN: Settings > Language & input > Add word
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62;
+
+    // OPEN: Settings > Location
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    LOCATION = 63;
+
+    // OPEN: Settings > Apps
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    MANAGE_APPLICATIONS = 65;
+
+    // OPEN: Settings > Backup & reset > Factory data reset
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    MASTER_CLEAR = 66;
+
+    // OPEN: Settings > Backup & reset > Factory data reset > Confirm
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    MASTER_CLEAR_CONFIRM = 67;
+
+    // OPEN: Settings > More > Android Beam
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NFC_BEAM = 69;
+
+    // OPEN: Settings > Tap & pay
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NFC_PAYMENT = 70;
+
+    // OPEN: Settings > Sound & notification > App notifications > [App]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_APP_NOTIFICATION = 72;
+
+    // OBSOLETE
+    NOTIFICATION_REDACTION = 74;
+
+    // OPEN: Settings Widget > Notification log
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_STATION = 75;
+
+    // OPEN: Settings > Sound & notification > Do not disturb
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_ZEN_MODE = 76;
+
+
+    // OPEN: Print job notification > Print job settings
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    PRINT_JOB_SETTINGS = 78;
+
+    // OPEN: Settings > Printing > [Print Service]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    PRINT_SERVICE_SETTINGS = 79;
+
+    // OPEN: Settings > Printing
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    PRINT_SETTINGS = 80;
+
+    // OPEN: Settings > Backup & reset
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    PRIVACY = 81;
+
+    //OBSOLETE
+    PROXY_SELECTOR = 82;
+
+    // OPEN: Settings > Backup & reset > Network settings reset
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    RESET_NETWORK = 83;
+
+    // OPEN: Settings > Backup & reset > Network settings reset > Confirm
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    RESET_NETWORK_CONFIRM = 84;
+
+    // OPEN: Settings > Developer Options > Running Services
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    RUNNING_SERVICE_DETAILS = 85;
+
+    // OPEN: Settings > Security > Screen pinning
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    SCREEN_PINNING = 86;
+
+    // OPEN: Settings > Security
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    SECURITY = 87;
+
+    // OPEN: Settings > SIM cards
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    SIM = 88;
+
+    // OBSOLETE
+    TESTING = 89;
+
+    // OPEN: Settings > More > Tethering & portable hotspot
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    TETHER = 90;
+
+    // OPEN: Settings > Security > Trust agents
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    TRUST_AGENT = 91;
+
+    // OPEN: Settings > Security > Trusted credentials
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    TRUSTED_CREDENTIALS = 92;
+
+    // OPEN: Settings > Language & input > TTS output > [Engine] > Settings
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    TTS_ENGINE_SETTINGS = 93;
+
+    // OPEN: Settings > Language & input > Text-to-speech output
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    TTS_TEXT_TO_SPEECH = 94;
+
+    // OPEN: Settings > Security > Apps with usage access
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    USAGE_ACCESS = 95;
+
+    // OPEN: Settings > Users
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    USER = 96;
+
+    // OPEN: Settings > Users > [Restricted profile app & content access]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    USERS_APP_RESTRICTIONS = 97;
+
+    // OPEN: Settings > Users > [User settings]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    USER_DETAILS = 98;
+
+    // OPEN: Settings > More > VPN
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    VPN = 100;
+
+    // OPEN: Settings > Display > Choose wallpaper from
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    WALLPAPER_TYPE = 101;
+
+    // OPEN: Settings > Display > Cast
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    WFD_WIFI_DISPLAY = 102;
+
+    // OPEN: Settings > Wi-Fi
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    WIFI = 103;
+
+    // OPEN: Settings > More > Wi-Fi Calling
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    WIFI_CALLING = 105;
+
+    // OPEN: Settings > Wi-Fi > Saved networks
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    WIFI_SAVED_ACCESS_POINTS = 106;
+
+    // OPEN: Settings > Wi-Fi > Advanced Wi-Fi > Wi-Fi Direct
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    WIFI_P2P = 109;
+
+    // OPEN: Settings > Apps > Configure apps > App permissions
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_ADVANCED = 130;
+
+    // OPEN: Settings > Location > Scanning
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    LOCATION_SCANNING = 131;
+
+    // OPEN: Settings > Sound & notification > App notifications
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    MANAGE_APPLICATIONS_NOTIFICATIONS = 133;
+
+    // OPEN: Settings > Sound & notification > DND > Priority only allows
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_ZEN_MODE_PRIORITY = 141;
+
+    // OPEN: Settings > Sound & notification > DND > Automatic rules
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_ZEN_MODE_AUTOMATION = 142;
+
+    // OPEN: Settings > Sound & notification > DND > [Time based rule]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144;
+
+    // OPEN: Settings > Apps > Configure apps > App links
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    MANAGE_DOMAIN_URLS = 143;
+
+    // OPEN: Settings > Sound & notification > DND > [Event rule]
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_ZEN_MODE_EVENT_RULE = 146;
+
+    // OPEN: Settings > Sound & notification > Notification access
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_ACCESS = 179;
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb access
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    NOTIFICATION_ZEN_MODE_ACCESS = 180;
+
+    // OPEN: Settings > Internal storage > Apps storage
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_STORAGE_APPS = 182;
+
+    // OPEN: Settings > Security > Usage access
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_USAGE_ACCESS_DETAIL = 183;
+
+    // OPEN: Settings > Battery > Battery optimization
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_HIGH_POWER_APPS = 184;
+
+    // OPEN: Settings > Apps > Configure > Default apps > Assist & voice input
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    APPLICATIONS_MANAGE_ASSIST = 201;
+
+    // OPEN: Settings > Memory
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    PROCESS_STATS_SUMMARY = 202;
+
+    // OPEN: Settings > Apps > Configure Apps > Display over other apps
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    SYSTEM_ALERT_WINDOW_APPS = 221;
+
+    // OPEN: Settings > About phone > Legal information
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    ABOUT_LEGAL_SETTINGS = 225;
+
+
+    // OPEN: Settings > Developer options > Inactive apps
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FUELGAUGE_INACTIVE_APPS = 238;
+
+    // OPEN: Settings > Security > Nexus Imprint > Add Fingerprint
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_ENROLLING = 240;
+    // OPEN: Fingerprint Enroll > Find Sensor
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_FIND_SENSOR = 241;
+
+    // OPEN: Fingerprint Enroll > Fingerprint Enrolled!
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_ENROLL_FINISH = 242;
+
+    // OPEN: Fingerprint Enroll introduction
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_ENROLL_INTRO = 243;
+
+    // OPEN: Fingerprint Enroll > Let's Start!
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_ENROLL_SIDECAR = 245;
+
+    // OPEN: Fingerprint Enroll SUW > Let's Start!
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_ENROLLING_SETUP = 246;
+
+    // OPEN: Fingerprint Enroll SUW > Find Sensor
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_FIND_SENSOR_SETUP = 247;
+
+    // OPEN: Fingerprint Enroll SUW > Fingerprint Enrolled!
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_ENROLL_FINISH_SETUP = 248;
+
+    // OPEN: Fingerprint Enroll SUW introduction
+    // CATEGORY: SETTINGS
+    // OS: 6.0
+    FINGERPRINT_ENROLL_INTRO_SETUP = 249;
+
+    // OPEN: Settings > Developer Options > Background Check
+    // CATEGORY: SETTINGS
+    // OS: N
+    BACKGROUND_CHECK_SUMMARY = 258;
+
+    // OPEN: Settings > Notifications > [App] > Channel Notifications
+    // CATEGORY: SETTINGS
+    // OS: N
+    NOTIFICATION_TOPIC_NOTIFICATION = 265;
+
+    // OPEN: Settings > Security > User credentials
+    // CATEGORY: Settings
+    // OS: N
+    USER_CREDENTIALS = 285;
+
+    // Logs that the user has edited the enabled VR listeners.
+    // CATEGORY: SETTINGS
+    // OS: N
+    VR_MANAGE_LISTENERS = 334;
+
+    // Settings -> Accessibility -> Click after pointer stops moving
+    // CATEGORY: SETTINGS
+    // OS: N
+    ACCESSIBILITY_TOGGLE_AUTOCLICK = 335;
+
+    // Settings -> Sound
+    // CATEGORY: SETTINGS
+    // OS: N
+    SOUND = 336;
+
+    // Settings -> Notifications -> Gear
+    // CATEGORY: SETTINGS
+    // OS: N
+    CONFIGURE_NOTIFICATION = 337;
+
+    // Settings -> Wi-Fi -> Gear
+    // CATEGORY: SETTINGS
+    // OS: N
+    CONFIGURE_WIFI = 338;
+
+    // Settings -> Display -> Display size
+    // OS: N
+    DISPLAY_SCREEN_ZOOM = 339;
+
+    // Settings -> Display -> Font size
+    // CATEGORY: SETTINGS
+    // OS: N
+    ACCESSIBILITY_FONT_SIZE = 340;
+
+    // Settings -> Data usage -> Cellular/Wi-Fi data usage
+    // CATEGORY: SETTINGS
+    // OS: N
+    DATA_USAGE_LIST = 341;
+
+    // Settings -> Data usage -> Billing cycle or DATA_USAGE_LIST -> Gear
+    // CATEGORY: SETTINGS
+    // OS: N
+    BILLING_CYCLE = 342;
+
+    // DATA_USAGE_LIST -> Any item or App info -> Data usage
+    // CATEGORY: SETTINGS
+    // OS: N
+    APP_DATA_USAGE = 343;
+
+    // Settings -> Language & input -> Language
+    // CATEGORY: SETTINGS
+    // OS: N
+    USER_LOCALE_LIST = 344;
+
+    // Settings -> Language & input -> Virtual keyboard
+    // CATEGORY: SETTINGS
+    // OS: N
+    VIRTUAL_KEYBOARDS = 345;
+
+    // Settings -> Language & input -> Physical keyboard
+    // CATEGORY: SETTINGS
+    // OS: N
+    PHYSICAL_KEYBOARDS = 346;
+
+    // Settings -> Language & input -> Virtual keyboard -> Add a virtual keyboard
+    // CATEGORY: SETTINGS
+    // OS: N
+    ENABLE_VIRTUAL_KEYBOARDS = 347;
+
+    // Settings -> Data usage -> Data Saver
+    // CATEGORY: SETTINGS
+    // OS: N
+    DATA_SAVER_SUMMARY = 348;
+
+    // Settings -> Data usage -> Data Saver -> Unrestricted data access
+    // CATEGORY: SETTINGS
+    // OS: N
+    DATA_USAGE_UNRESTRICTED_ACCESS = 349;
+
+    // Settings -> Apps -> Gear -> Special access
+    SPECIAL_ACCESS = 351;
+
+    // OPEN: SUW Welcome Screen -> Vision Settings
+    // CATEGORY: SETTINGS
+    // OS: N
+    SUW_ACCESSIBILITY = 367;
+
+    // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification gestures (Renamed in O)
+    // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification -> Magnify with triple-tap
+    // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification -> Magnify with button
+    // ACTION: New magnification gesture configuration is chosen
+    //  SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: N
+    SUW_ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 368;
+
+    // OPEN: SUW Welcome Screen -> Vision Settings -> Font size
+    // ACTION: New font size is chosen
+    //  SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is largest
+    // CATEGORY: SETTINGS
+    // OS: N
+    SUW_ACCESSIBILITY_FONT_SIZE = 369;
+
+    // OPEN: SUW Welcome Screen -> Vision Settings -> Display size
+    // ACTION: New display size is chosen
+    //  SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is larger, 4 is largest
+    // CATEGORY: SETTINGS
+    // OS: N
+    SUW_ACCESSIBILITY_DISPLAY_SIZE = 370;
+
+    // OPEN: SUW Welcome Screen -> Vision Settings -> TalkBack
+    // ACTION: New screen reader configuration is chosen
+    //  SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: N
+    SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER = 371;
+
+    // Airplane mode on
+    SETTINGS_CONDITION_AIRPLANE_MODE = 377;
+    // AKA Data saver on
+    SETTINGS_CONDITION_BACKGROUND_DATA = 378;
+    // Battery saver on
+    SETTINGS_CONDITION_BATTERY_SAVER = 379;
+    // Cellular data off
+    SETTINGS_CONDITION_CELLULAR_DATA = 380;
+    // Do not disturb on
+    SETTINGS_CONDITION_DND = 381;
+    // Hotspot on
+    SETTINGS_CONDITION_HOTSPOT = 382;
+    // Work profile off
+    SETTINGS_CONDITION_WORK_MODE = 383;
+
+    // Settings > Apps > Gear > Special Access > Premium SMS access
+    PREMIUM_SMS_ACCESS = 388;
+
+    // OPEN: Settings > Accounts > Work profile settings
+    // CATEGORY: SETTINGS
+    ACCOUNTS_WORK_PROFILE_SETTINGS = 401;
+
+    // Settings -> Dev options -> Convert to file encryption
+    CONVERT_FBE = 402;
+
+    // Settings -> Dev options -> Convert to file encryption -> WIPE AND CONVERT...
+    CONVERT_FBE_CONFIRM = 403;
+
+    // Settings -> Dev options -> Running services
+    RUNNING_SERVICES = 404;
+
+    // The dialog shown by 3P intent to change current webview implementation.
+    WEBVIEW_IMPLEMENTATION = 405;
+
+    // OPEN: Settings > Internal storage > Storage manager
+    // CATEGORY: SETTINGS
+    STORAGE_MANAGER_SETTINGS = 458;
+
+    // OPEN: Settings -> Gestures
+    // CATEGORY: SETTINGS
+    SETTINGS_GESTURES = 459;
+
+    // OPEN: Settings > Display > Night Light
+    // CATEGORY: SETTINGS
+    NIGHT_DISPLAY_SETTINGS = 488;
+
+    // Night Light on
+    SETTINGS_CONDITION_NIGHT_DISPLAY = 492;
+
+    // OPEN: Settings > Language & input > Personal dictionary (single locale)
+    USER_DICTIONARY_SETTINGS = 514;
+
+    // OPEN: Settings > Date & time > Select time zone
+    ZONE_PICKER = 515;
+
+    // OPEN: Settings > Security > Device administrators
+    DEVICE_ADMIN_SETTINGS = 516;
+
+    // OPEN: Settings > Security > Factory Reset Protection dialog
+    DIALOG_FRP = 528;
+
+    // OPEN: Settings > Custom list preference with confirmation message
+    DIALOG_CUSTOM_LIST_CONFIRMATION = 529;
+
+    // OPEN: Settings > APN Editor > Error dialog
+    DIALOG_APN_EDITOR_ERROR = 530;
+
+    // OPEN: Settings > Users > Edit owner info dialog
+    DIALOG_OWNER_INFO_SETTINGS = 531;
+
+    // OPEN: Settings > Security > Use one lock dialog
+    DIALOG_UNIFICATION_CONFIRMATION = 532;
+
+    // OPEN: Settings > Security > User Credential
+    DIALOG_USER_CREDENTIAL = 533;
+
+    // OPEN: Settings > Accounts > Remove account
+    DIALOG_REMOVE_USER = 534;
+
+    // OPEN: Settings > Accounts > Confirm auto sync dialog
+    DIALOG_CONFIRM_AUTO_SYNC_CHANGE = 535;
+
+    // OPEN: Settings > Apps > Dialog for running service details
+    DIALOG_RUNNIGN_SERVICE = 536;
+
+    // OPEN: Settings > Bluetooth > Rename this device
+    DIALOG_BLUETOOTH_RENAME = 538;
+
+    // OPEN: Settings > Battery optimization > details for app
+    DIALOG_HIGH_POWER_DETAILS = 540;
+
+    // OPEN: Settings > Keyboard > Show keyboard layout dialog
+    DIALOG_KEYBOARD_LAYOUT = 541;
+
+    // OPEN: Settings > WIFI Scan permission dialog
+    DIALOG_WIFI_SCAN_MODE = 543;
+
+    // OPEN: Settings > Wireless > VPN > Config dialog
+    DIALOG_LEGACY_VPN_CONFIG = 545;
+
+    // OPEN: Settings > Wireless > VPN > Config dialog for app
+    DIALOG_VPN_APP_CONFIG = 546;
+
+    // OPEN: Settings > Wireless > VPN > Cannot connect dialog
+    DIALOG_VPN_CANNOT_CONNECT = 547;
+
+    // OPEN: Settings > Wireless > VPN > Replace existing VPN dialog
+    DIALOG_VPN_REPLACE_EXISTING = 548;
+
+    // OPEN: Settings > Billing cycle > Edit billing cycle dates dialog
+    DIALOG_BILLING_CYCLE = 549;
+
+    // OPEN: Settings > Billing cycle > Edit data limit/warning dialog
+    DIALOG_BILLING_BYTE_LIMIT = 550;
+
+    // OPEN: Settings > Billing cycle > turn on data limit dialog
+    DIALOG_BILLING_CONFIRM_LIMIT = 551;
+
+    // OPEN: Settings > Service > Turn off notification access dialog
+    DIALOG_DISABLE_NOTIFICATION_ACCESS = 552;
+
+    // OPEN: Settings > Sound > Use personal sound for work profile dialog
+    DIALOG_UNIFY_SOUND_SETTINGS = 553;
+
+    // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being granted.
+    DIALOG_ZEN_ACCESS_GRANT = 554;
+
+    // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being revoked.
+    DIALOG_ZEN_ACCESS_REVOKE = 555;
+
+    // OPEN: Settings > Zen mode > Dialog that picks time for zen mode.
+    DIALOG_ZEN_TIMEPICKER = 556;
+
+    // OPEN: Settings > Apps > Dialog that informs user to allow service access for app.
+    DIALOG_SERVICE_ACCESS_WARNING = 557;
+
+    // OPEN: Settings > Apps > Dialog for app actions (such as force stop/clear data)
+    DIALOG_APP_INFO_ACTION = 558;
+
+    // OPEN: Settings > Storage > Dialog for forgetting a storage device
+    DIALOG_VOLUME_FORGET = 559;
+
+    // OPEN: Settings > Storage > Dialog for initializing a volume
+    DIALOG_VOLUME_INIT = 561;
+
+    // OPEN: Settings > Storage > Dialog for unmounting a volume
+    DIALOG_VOLUME_UNMOUNT = 562;
+
+    // OPEN: Settings > Storage > Dialog for renaming a volume
+    DIALOG_VOLUME_RENAME = 563;
+
+    // OPEN: Settings > Storage > Dialog for clear cache
+    DIALOG_STORAGE_CLEAR_CACHE = 564;
+
+    // OPEN: Settings > Storage > Dialog for system info
+    DIALOG_STORAGE_SYSTEM_INFO = 565;
+
+    // OPEN: Settings > Storage > Dialog for other info
+    DIALOG_STORAGE_OTHER_INFO = 566;
+
+    // OPEN: Settings > Storage > Dialog for user info
+    DIALOG_STORAGE_USER_INFO = 567;
+    // OPEN: Settings > Add fingerprint > Dialog when user touches fingerprint icon.
+    DIALOG_FINGERPRINT_ICON_TOUCH = 568;
+
+    // OPEN: Settings > Add fingerprint > Error dialog
+    DIALOG_FINGERPINT_ERROR = 569;
+
+    // OPEN: Settings > Fingerprint > Rename or delete dialog
+    DIALOG_FINGERPINT_EDIT = 570;
+
+    // OPEN: Settings > Fingerprint > Dialog for deleting last fingerprint
+    DIALOG_FINGERPINT_DELETE_LAST = 571;
+
+    // OPEN: SUW > Fingerprint > Dialog to confirm skip fingerprint setup entirely.
+    DIALOG_FINGERPRINT_SKIP_SETUP = 573;
+
+    // OPEN: Settings > Proxy Selector error dialog
+    DIALOG_PROXY_SELECTOR_ERROR = 574;
+
+    // OPEN: Settings > Wifi > P2P Settings > Disconnect dialog
+    DIALOG_WIFI_P2P_DISCONNECT = 575;
+
+    // OPEN: Settings > Wifi > P2P Settings > Cancel connection dialog
+    DIALOG_WIFI_P2P_CANCEL_CONNECT = 576;
+
+    // OPEN: Settings > Wifi > P2P Settings > Rename dialog
+    DIALOG_WIFI_P2P_RENAME = 577;
+
+    // OPEN: Settings > Wifi > P2P Settings > Forget group dialog
+    DIALOG_WIFI_P2P_DELETE_GROUP = 578;
+
+    // OPEN: Settings > APN > Restore default dialog
+    DIALOG_APN_RESTORE_DEFAULT = 579;
+
+    // OPEN: Settings > Encryption interstitial accessibility warning dialog
+    DIALOG_ENCRYPTION_INTERSTITIAL_ACCESSIBILITY = 581;
+
+    // OPEN: Settings > Acessibility > Enable accessiblity service dialog
+    DIALOG_ACCESSIBILITY_SERVICE_ENABLE = 583;
+
+    // OPEN: Settings > Acessibility > Disable accessiblity service dialog
+    DIALOG_ACCESSIBILITY_SERVICE_DISABLE = 584;
+
+    // OPEN: Settings > Account > Remove account dialog
+    DIALOG_ACCOUNT_SYNC_REMOVE = 585;
+
+    // OPEN: Settings > Account > Remove account failed dialog
+    DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL = 586;
+
+    // OPEN: Settings > Account > Cannot do onetime sync dialog
+    DIALOG_ACCOUNT_SYNC_CANNOT_ONETIME_SYNC = 587;
+
+    // OPEN: Settings > Display > Night light > Set start time dialog
+    DIALOG_NIGHT_DISPLAY_SET_START_TIME = 588;
+
+    // OPEN: Settings > Display > Night light > Set end time dialog
+    DIALOG_NIGHT_DISPLAY_SET_END_TIME = 589;
+
+
+
+        // OPEN: Settings > User > Edit info dialog
+        DIALOG_USER_EDIT = 590;
+
+        // OPEN: Settings > User > Confirm remove dialog
+        DIALOG_USER_REMOVE = 591;
+
+        // OPEN: Settings > User > Enable calling dialog
+        DIALOG_USER_ENABLE_CALLING = 592;
+
+        // OPEN: Settings > User > Enable calling and sms dialog
+        DIALOG_USER_ENABLE_CALLING_AND_SMS = 593;
+
+        // OPEN: Settings > User > Cannot manage device message dialog
+        DIALOG_USER_CANNOT_MANAGE = 594;
+
+        // OPEN: Settings > User > Add user dialog
+        DIALOG_USER_ADD = 595;
+
+        // OPEN: Settings > User > Setup user dialog
+        DIALOG_USER_SETUP = 596;
+
+        // OPEN: Settings > User > Setup profile dialog
+        DIALOG_USER_SETUP_PROFILE = 597;
+
+        // OPEN: Settings > User > Choose user type dialog
+        DIALOG_USER_CHOOSE_TYPE = 598;
+
+        // OPEN: Settings > User > Need lockscreen dialog
+        DIALOG_USER_NEED_LOCKSCREEN = 599;
+
+        // OPEN: Settings > User > Confirm exit guest mode dialog
+        DIALOG_USER_CONFIRM_EXIT_GUEST = 600;
+
+        // OPEN: Settings > User > Edit user profile dialog
+        DIALOG_USER_EDIT_PROFILE = 601;
+
+
+    // OPEN: Settings > Wifi > Saved AP > Edit dialog
+    DIALOG_WIFI_SAVED_AP_EDIT = 602;
+
+    // OPEN: Settings > Wifi > Edit AP dialog
+    DIALOG_WIFI_AP_EDIT = 603;
+
+    // OPEN: Settings > Wifi > Write config to NFC dialog
+    DIALOG_WIFI_WRITE_NFC = 606;
+
+    // OPEN: Settings > Date > Date picker dialog
+    DIALOG_DATE_PICKER = 607;
+
+    // OPEN: Settings > Date > Time picker dialog
+    DIALOG_TIME_PICKER = 608;
+
+    // OPEN: Settings > Wireless > Manage wireless plan dialog
+    DIALOG_MANAGE_MOBILE_PLAN = 609;
+
+    // OPEN Settings > Bluetooth > Attempt to connect to device that shows dialog
+    BLUETOOTH_DIALOG_FRAGMENT = 613;
+
+    // OPEN: Settings > Security
+    // CATEGORY: SETTINGS
+    // OS: O
+    ENTERPRISE_PRIVACY_SETTINGS = 628;
+
+    // OPEN: Settings > System
+    SETTINGS_SYSTEM_CATEGORY = 744;
+
+    // OPEN: Settings > Storage
+    SETTINGS_STORAGE_CATEGORY = 745;
+
+    // OPEN: Settings > Network & Internet
+    SETTINGS_NETWORK_CATEGORY = 746;
+
+    // OPEN: Settings > Connected Device
+    SETTINGS_CONNECTED_DEVICE_CATEGORY = 747;
+
+    // OPEN: Settings > App & Notification
+    SETTINGS_APP_NOTIF_CATEGORY = 748;
+
+    // OPEN: Settings > System > Language & Region
+    SETTINGS_LANGUAGE_CATEGORY = 750;
+
+    // OPEN: Settings > System > Input & Gesture > Swipe to notification gesture
+    SETTINGS_GESTURE_SWIPE_TO_NOTIFICATION = 751;
+
+    // OPEN: Settings > System > Input & Gesture > Double tap power button gesture
+    SETTINGS_GESTURE_DOUBLE_TAP_POWER = 752;
+
+    // OPEN: Settings > System > Input & Gesture > Pick up gesture
+    SETTINGS_GESTURE_PICKUP = 753;
+
+    // OPEN: Settings > System > Input & Gesture > Double tap screen gesture
+    SETTINGS_GESTURE_DOUBLE_TAP_SCREEN = 754;
+
+    // OPEN: Settings > System > Input & Gesture > Double twist gesture
+    SETTINGS_GESTURE_DOUBLE_TWIST = 755;
+
+    // OPEN: Settings > Apps > Default Apps > Default browser
+    DEFAULT_BROWSER_PICKER = 785;
+    // OPEN: Settings > Apps > Default Apps > Default emergency app
+    DEFAULT_EMERGENCY_APP_PICKER = 786;
+
+    // OPEN: Settings > Apps > Default Apps > Default home
+    DEFAULT_HOME_PICKER = 787;
+
+    // OPEN: Settings > Apps > Default Apps > Default phone
+    DEFAULT_PHONE_PICKER = 788;
+
+    // OPEN: Settings > Apps > Default Apps > Default sms
+    DEFAULT_SMS_PICKER = 789;
+
+    // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
+    DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791;
+
+    // OPEN: Settings > Apps > Default Apps > Default autofill app
+    DEFAULT_AUTOFILL_PICKER = 792;
+
+    // OPEN: Settings > Apps > Gear > Special Access > Install other apps
+    // CATEGORY: SETTINGS
+    // OS: 8.0
+    MANAGE_EXTERNAL_SOURCES = 808;
+
+    // Logs that the user has edited the picture-in-picture settings.
+    // CATEGORY: SETTINGS
+    SETTINGS_MANAGE_PICTURE_IN_PICTURE = 812;
+
+    // OPEN: SUW Welcome Screen -> Vision Settings -> Select to Speak
+    // ACTION: Select to Speak configuration is chosen
+    //  SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: N
+    SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK = 817;
+
+    // OPEN: Settings > System > Backup
+    // CATEGORY: SETTINGS
+    // OS: O
+    BACKUP_SETTINGS = 818;
+
+    // OPEN: Settings > Storage > Games
+    // CATEGORY: SETTINGS
+    // OS: O
+    APPLICATIONS_STORAGE_GAMES = 838;
+
+    // OPEN: Settings > Storage > Audio and Music
+    // CATEGORY: SETTINGS
+    // OS: O
+    APPLICATIONS_STORAGE_MUSIC = 839;
+
+    // ACTION: Settings > Storage > Free Up Space to launch Deletion Helper
+    // CATEGORY: SETTINGS
+    // OS: O
+    STORAGE_FREE_UP_SPACE_NOW = 840;
+
+    // ACTION: Settings > Storage > Files to open the File Manager
+    // CATEGORY: SETTINGS
+    // OS: O
+    STORAGE_FILES = 841;
+
+    // OPEN: Settings > Apps > Default Apps > Assist >  Default assist
+    DEFAULT_ASSIST_PICKER = 843;
+
+    // OPEN: Settings > Apps > Default Apps > Assist >  Default voice input
+    DEFAULT_VOICE_INPUT_PICKER = 844;
+
+    // OPEN: Settings > Storage > [Profile]
+    SETTINGS_STORAGE_PROFILE = 845;
+
+    // OPEN: Settings > Security & screen lock -> Encryption & crendentials
+    // CATEGORY: SETTINGS
+    // OS: O
+    ENCRYPTION_AND_CREDENTIAL = 846;
+
+    // OPEN: Settings > Wi-Fi > Network Details (click on Access Point)
+    // CATEGORY: SETTINGS
+    // OS: O
+    WIFI_NETWORK_DETAILS = 849;
+
+    // OPEN: Settings > Wi-Fi > Wifi Preferences -> Advanced -> Network Scorer
+    // CATEGORY: SETTINGS
+    // OS: O
+    SETTINGS_NETWORK_SCORER = 861;
+
+    // OPEN: Settings > About device > Model > Hardware info dialog
+    DIALOG_SETTINGS_HARDWARE_INFO = 862;
+
+    // OPEN: Settings > Security & screen lock -> Lock screen preferences
+    // CATEGORY: SETTINGS
+    SETTINGS_LOCK_SCREEN_PREFERENCES = 882;
+
+
+    // OPEN: Settings -> Display -> When in VR Mode
+    VR_DISPLAY_PREFERENCE = 921;
+
+    // OPEN: Settings > Accessibility > Magnification
+    // CATEGORY: SETTINGS
+    // OS: O
+    ACCESSIBILITY_SCREEN_MAGNIFICATION_SETTINGS = 922;
+
+    // OPEN: Settings -> System -> Reset options
+    RESET_DASHBOARD = 924;
+
+     // OPEN: Settings > Security > Nexus Imprint > [Fingerprint] > Delete
+    // CATEGORY: SETTINGS
+    // OS: O
+    FINGERPRINT_REMOVE_SIDECAR = 934;
+
+    // OPEN: Settings > Storage > Movies & TV
+    // CATEGORY: SETTINGS
+    // OS: O
+    APPLICATIONS_STORAGE_MOVIES = 935;
+
+    // OPEN: Settings > Security > Managed Device Info > Apps installed
+    // CATEGORY: SETTINGS
+    // OS: O
+    ENTERPRISE_PRIVACY_INSTALLED_APPS = 938;
+
+    // OPEN: Settings > Security > Managed Device Info > nnn permissions
+    // CATEGORY: SETTINGS
+    // OS: O
+    ENTERPRISE_PRIVACY_PERMISSIONS = 939;
+
+
+    // OPEN: Settings > Security > Managed Device Info > Default apps
+    // CATEGORY: SETTINGS
+    // OS: O
+    ENTERPRISE_PRIVACY_DEFAULT_APPS = 940;
+
+    // OPEN: Choose screen lock dialog in Settings
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    SETTINGS_CHOOSE_LOCK_DIALOG = 990;
+
+    // OPEN: Settings > System > Languages & input > Assist gesture
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    SETTINGS_ASSIST_GESTURE = 996;
+
     // OPEN: Settings > Connected Devices > Bluetooth > (click on details link for a paired device)
     BLUETOOTH_DEVICE_DETAILS = 1009;
 
+    // OPEN: Settings > credential pages - prompt for key guard configuration confirmation
+    CONFIGURE_KEYGUARD_DIALOG = 1010;
+
+    // OPEN: Settings > Network > Tether > Wi-Fi hotspot
+    WIFI_TETHER_SETTINGS = 1014;
+
+    // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device)
+    // -> Edit name button.
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    DIALOG_BLUETOOTH_PAIRED_DEVICE_RENAME = 1015;
+
     // OPEN: Settings > Connected devices > Bluetooth > Pair new device
+    // CATEGORY: SETTINGS
+    // OS: O DR
     BLUETOOTH_PAIRING = 1018;
 
+    // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device)
+    // -> Forget button.
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    DIALOG_BLUETOOTH_PAIRED_DEVICE_FORGET = 1031;
+
+    // OPEN: Settings > Storage > Photos & Videos
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    APPLICATIONS_STORAGE_PHOTOS = 1092;
+
+    // OPEN: Settings > Display > Colors
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    COLOR_MODE_SETTINGS = 1143;
+
+    // OPEN: Settings > Developer Options > Experiment dashboard
+    // CATEGORY: SETTINGS
+    SETTINGS_FEATURE_FLAGS_DASHBOARD = 1217;
+
+    // OPEN: Settings > Notifications > [App] > Topic Notifications
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_CHANNEL_GROUP = 1218;
+
+    // OPEN: Settings > Developer options > Enable > Info dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_ENABLE_DEVELOPMENT_OPTIONS = 1219;
+
+    // OPEN: Settings > Developer options > OEM unlocking > Info dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_ENABLE_OEM_UNLOCKING = 1220;
+
+    // OPEN: Settings > Developer options > USB debugging > Info dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_ENABLE_ADB = 1222;
+
+    // OPEN: Settings > Security > Nexus Imprint > [Fingerprint]
+    // CATEGORY: SETTINGS
+    // OS: P
+    FINGERPRINT_AUTHENTICATE_SIDECAR = 1221;
+
+    // OPEN: Settings > Developer options > Revoke USB debugging authorizations > Info dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_CLEAR_ADB_KEYS = 1223;
+
+    // Open: Settings > Developer options > Quick setting tile config
+    // CATEGORY: SETTINGS
+    // OS: P
+    DEVELOPMENT_QS_TILE_CONFIG = 1224;
+
+    // OPEN: Settings > Developer options > Store logger data persistently on device > Info dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_LOG_PERSIST = 1225;
+
+    // OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
+    // CATEGORY: SETTINGS
+    // OS: P
+    WIFI_CALLING_FOR_SUB = 1230;
+
+    // Open: Settings > Dev options > Oem unlock > lock it > warning dialog.
+    // OS: P
+    DIALOG_OEM_LOCK_INFO = 1238;
+
+    // Open: Settings > System > About phone > IMEI
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_IMEI_INFO = 1240;
+
+    // OPEN: Settings > System > About Phone > Sim status
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_SIM_STATUS = 1246;
+
+    // OPEN: Settings > System > About Phone > Android Version
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_FIRMWARE_VERSION = 1247;
+
+    // OPEN: Settings > Battery(version 2)
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_POWER_USAGE_SUMMARY_V2 = 1263;
+
+    // OPEN: Settings > Connected devices > Connection preferences
+    // CATEGORY: SETTINGS
+    // OS: P
+    CONNECTION_DEVICE_ADVANCED = 1264;
+
+    // OPEN: Settings > Security > Screen lock gear icon
+    // CATEGORY: SETTINGS
+    // OS: P
+    SCREEN_LOCK_SETTINGS = 1265;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Delete rule (trash can icon)
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_DELETE_RULE_DIALOG = 1266;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule > Event/Time
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_RULE_NAME_DIALOG = 1269;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG = 1270;
+
+    // OPEN: Settings > Battery > Smart Battery
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_SMART_BATTERY = 1281;
+
+    // OPEN: Settings > Battery > Smart Battery > Restricted apps
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_RESTRICTED_APP_DETAILS = 1285;
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb > Turn on now
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_ENABLE_DIALOG = 1286;
+
+    // OPEN: Settings->Connected Devices->USB->(click on details link)
+    // CATEGORY: SETTINGS
+    // OS: P
+    USB_DEVICE_DETAILS = 1291;
+
+    // OPEN: Settings > Accessibility > Vibration
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACCESSIBILITY_VIBRATION = 1292;
+
+    // OPEN: Settings > Accessibility > Vibration > Notification vibration
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACCESSIBILITY_VIBRATION_NOTIFICATION = 1293;
+
+    // OPEN: Settings > Accessibility > Vibration > Touch vibration
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACCESSIBILITY_VIBRATION_TOUCH = 1294;
+
+    // OPEN: Settings->Developer Options->Default USB
+    // CATEGORY: SETTINGS
+    // OS: P
+    USB_DEFAULT = 1312;
+
+    // OPEN: Settings > Battery > Battery tip > Battery tip Dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_BATTERY_TIP_DIALOG = 1323;
+
+    // OPEN: DND Settings > What to block
+    // OS: P
+    ZEN_WHAT_TO_BLOCK = 1339;
+
+    // OPEN: Settings > Sounds > Do Not Disturb > Duration
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_DURATION_DIALOG = 1341;
+
+    // OPEN: Settings > Date & time > Select time zone -> Region
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_ZONE_PICKER_REGION = 1355;
+
+    // OPEN: Settings > Date & time > Select time zone -> Time Zone
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_ZONE_PICKER_TIME_ZONE = 1356;
+    // OPEN: Settings > Date & time > Select time zone -> Select UTC Offset
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_ZONE_PICKER_FIXED_OFFSET = 1357;
+
+    // OPEN: Settings > Gestures > Prevent Ringing
+    // OS: P
+    SETTINGS_PREVENT_RINGING = 1360;
+
+    // Settings > Condition > Device muted
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_CONDITION_DEVICE_MUTED = 1368;
+
+    // Settings > Condition > Device vibrate
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_CONDITION_DEVICE_VIBRATE = 1369;
+
+    // OPEN: Settings > Connected devices > previously connected devices
+    // CATEGORY: SETTINGS
+    // OS: P
+    PREVIOUSLY_CONNECTED_DEVICES = 1370;
+
+    // OPEN: Settings > Network & Internet > Wi-Fi > Wi-Fi Preferences > Turn on Wi-Fi automatically
+    //       note: Wifi Scanning must be off for this dialog to show
+    // CATEGORY: SETTINGS
+    // OS: P
+    WIFI_SCANNING_NEEDED_DIALOG = 1373;
+
+    // OPEN: Settings > System > Gestures > Swipe up gesture
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_GESTURE_SWIPE_UP = 1374;
+
+    // OPEN: Settings > Storage > Dialog to format a storage volume
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_VOLUME_FORMAT = 1375;
+
+    // OPEN: DND onboarding activity
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_ZEN_ONBOARDING = 1380;
+
+    // OPEN: Settings > Display > Auto brightness
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_AUTO_BRIGHTNESS = 1381;
+
+     // OPEN: Settings > Connected Devices > Bluetooth
+    // CATEGORY: SETTINGS
+    // OS: P
+    BLUETOOTH_FRAGMENT = 1390;
+
+    // Screen: DND Settings > Notifications
+    // OS: P
+    SETTINGS_ZEN_NOTIFICATIONS = 1400;
+
+    // An event category for slices.
+    // OPEN: Slice became visible.
+    // CLOSE: Slice became invisible.
+    // ACTION: Slice was tapped.
+    SLICE = 1401;
+
+    // OPEN: Settings -> Developer Options -> Disable Bluetooth A2DP hardware
+    // offload
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_BLUETOOTH_DISABLE_A2DP_HW_OFFLOAD = 1441;
+
     // OPEN: Settings homepage
     SETTINGS_HOMEPAGE = 1502;
 
+    // OPEN: Settings > Create shortcut(widget)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_CREATE_SHORTCUT = 1503;
+
+    // OPEN: Face Enroll introduction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_INTRO = 1506;
+
+    // OPEN: Face Enroll introduction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_ENROLLING = 1507;
+
+    // OPEN: Face Enroll introduction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_FINISHED = 1508;
+
+    // OPEN: Face Enroll sidecar
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_SIDECAR = 1509;
+
+    // OPEN: Settings > Add face > Error dialog
+    // OS: Q
+    DIALOG_FACE_ERROR = 1510;
+
+    // OPEN: Settings > Security > Face
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE = 1511;
+
+   // OPEN: Settings > Acessibility > HearingAid pairing instructions dialog
+    // CATEGORY: SETTINGS
+    // OS: Q
+    DIALOG_ACCESSIBILITY_HEARINGAID = 1512;
+
+    // OPEN: Settings > Add face
+    // OS: Q
+    FACE_ENROLL_PREVIEW = 1554;
+
+    // OPEN: Settings > Network & Internet > Wi-Fi > Add network
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_WIFI_ADD_NETWORK = 1556;
+
+    // OPEN: Settings > System > Input & Gesture > Reach up gesture
+    // OS: Q
+    SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557;
+
     // OPEN: Settings > System > Input & Gesture > Wake screen
     SETTINGS_GESTURE_WAKE_SCREEN = 1570;
 
@@ -74,6 +2079,19 @@
     // OPEN: Settings > Privacy
     TOP_LEVEL_PRIVACY = 1587;
 
+    // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions >
+    // Allow apps to override
+    // CATEGORY: SETTINGS
+    // OS: Q
+    NOTIFICATION_ZEN_MODE_OVERRIDING_APPS = 1588;
+
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions >
+    // Allow apps to override > Choose app
+    // CATEGORY: SETTINGS
+    // OS: Q
+    NOTIFICATION_ZEN_MODE_OVERRIDING_APP = 1589;
+
     // OPEN: Settings > Developer options > Disable > Info dialog
     DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
 
@@ -85,4 +2103,80 @@
 
     // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access
     SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
+
+
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_SETTINGS = 1604;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_SOUND_SETTINGS = 1605;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_DEFAULT_SETTINGS = 1606;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_NOTIFICATION_RESTRICTIONS = 1608;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_VIS_EFFECTS = 1609;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom > Allow messages
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_MESSAGES = 1610;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom > Allow calls
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_CALLS = 1611;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Click footer link if custom settings applied
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_SETTINGS_DIALOG = 1612;
+
+    // OPEN: Settings > Developer Options > Game Update Packages
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_GUP_DASHBOARD = 1613;
+
+    // OPEN: Settings > Accessibility > Vibration > Ring vibration
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACCESSIBILITY_VIBRATION_RING = 1620;
+
+    // OPEN: Settings > System > Input & Gesture > Skip songs
+    SETTINGS_GESTURE_SKIP = 1624;
+
+    // OPEN: Settings > System > Input & Gesture > Silence alerts
+    SETTINGS_GESTURE_SILENCE = 1625;
+
+    // OPEN: Settings > System > Input & Gesture > Tap to check
+    SETTINGS_GESTURE_TAP_SCREEN = 1626;
+
+    // OPEN: Settings > Network & internet > Click Mobile network to land on a page with a list of
+    // SIM/eSIM subscriptions.
+    MOBILE_NETWORK_LIST = 1627;
 }
diff --git a/core/proto/android/app/window_configuration.proto b/core/proto/android/app/window_configuration.proto
index 2d15552..6cc1a40 100644
--- a/core/proto/android/app/window_configuration.proto
+++ b/core/proto/android/app/window_configuration.proto
@@ -29,4 +29,5 @@
     optional .android.graphics.RectProto app_bounds = 1;
     optional int32 windowing_mode = 2;
     optional int32 activity_type = 3;
+    optional .android.graphics.RectProto bounds = 4;
 }
diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto
index d0c9226..76c240e 100644
--- a/core/proto/android/bluetooth/enums.proto
+++ b/core/proto/android/bluetooth/enums.proto
@@ -41,3 +41,18 @@
     ENABLE_DISABLE_REASON_USER_SWITCH = 8;
     ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9;
 }
+
+enum DirectionEnum {
+    DIRECTION_UNKNOWN = 0;
+    DIRECTION_OUTGOING = 1;
+    DIRECTION_INCOMING = 2;
+}
+
+// First item is the default value, other values follow Bluetooth spec definition
+enum LinkTypeEnum {
+    // Link type is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    LINK_TYPE_UNKNOWN = 0xFFF;
+    LINK_TYPE_SCO = 0x00;
+    LINK_TYPE_ACL = 0x01;
+    LINK_TYPE_ESCO = 0x02;
+}
diff --git a/core/proto/android/bluetooth/hci/enums.proto b/core/proto/android/bluetooth/hci/enums.proto
new file mode 100644
index 0000000..e1d96bb
--- /dev/null
+++ b/core/proto/android/bluetooth/hci/enums.proto
@@ -0,0 +1,519 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.bluetooth.hci;
+
+option java_outer_classname = "BluetoothHciProtoEnums";
+option java_multiple_files = true;
+
+// HCI command opcodes (OCF+OGF) from Bluetooth 5.0 specification Vol 2, Part E, Section 7
+// Original definition: system/bt/stack/include/hcidefs.h
+enum CommandEnum {
+    // Opcode is at most 2 bytes (0xFFFF), thus 0xFFFFF must not be a valid value
+    CMD_UNKNOWN = 0xFFFFF;
+    // Link control commands 0x0400
+    CMD_INQUIRY = 0x0401;
+    CMD_INQUIRY_CANCEL = 0x0402;
+    CMD_PERIODIC_INQUIRY_MODE = 0x0403;
+    CMD_EXIT_PERIODIC_INQUIRY_MODE = 0x0404;
+    CMD_CREATE_CONNECTION = 0x0405;
+    CMD_DISCONNECT = 0x0406;
+    CMD_ADD_SCO_CONNECTION = 0x0407; // Deprecated since Bluetooth 1.2
+    CMD_CREATE_CONNECTION_CANCEL = 0x0408;
+    CMD_ACCEPT_CONNECTION_REQUEST = 0x0409;
+    CMD_REJECT_CONNECTION_REQUEST = 0x040A;
+    CMD_LINK_KEY_REQUEST_REPLY = 0x040B;
+    CMD_LINK_KEY_REQUEST_NEG_REPLY = 0x040C;
+    CMD_PIN_CODE_REQUEST_REPLY = 0x040D;
+    CMD_PIN_CODE_REQUEST_NEG_REPLY = 0x040E;
+    CMD_CHANGE_CONN_PACKET_TYPE = 0x040F;
+    CMD_AUTHENTICATION_REQUESTED = 0x0411;
+    CMD_SET_CONN_ENCRYPTION = 0x0413;
+    CMD_CHANGE_CONN_LINK_KEY = 0x0415;
+    CMD_MASTER_LINK_KEY = 0x0417;
+    CMD_RMT_NAME_REQUEST = 0x0419;
+    CMD_RMT_NAME_REQUEST_CANCEL = 0x041A;
+    CMD_READ_RMT_FEATURES = 0x041B;
+    CMD_READ_RMT_EXT_FEATURES = 0x041C;
+    CMD_READ_RMT_VERSION_INFO = 0x041D;
+    CMD_READ_RMT_CLOCK_OFFSET = 0x041F;
+    CMD_READ_LMP_HANDLE = 0x0420;
+    CMD_SETUP_ESCO_CONNECTION = 0x0428;
+    CMD_ACCEPT_ESCO_CONNECTION = 0x0429;
+    CMD_REJECT_ESCO_CONNECTION = 0x042A;
+    CMD_IO_CAPABILITY_REQUEST_REPLY = 0x042B;
+    CMD_USER_CONF_REQUEST_REPLY = 0x042C;
+    CMD_USER_CONF_VALUE_NEG_REPLY = 0x042D;
+    CMD_USER_PASSKEY_REQ_REPLY = 0x042E;
+    CMD_USER_PASSKEY_REQ_NEG_REPLY = 0x042F;
+    CMD_REM_OOB_DATA_REQ_REPLY = 0x0430;
+    CMD_REM_OOB_DATA_REQ_NEG_REPLY = 0x0433;
+    CMD_IO_CAP_REQ_NEG_REPLY = 0x0434;
+    // BEGIN: AMP commands (not used in system/bt)
+    CMD_CREATE_PHYSICAL_LINK = 0x0435;
+    CMD_ACCEPT_PHYSICAL_LINK = 0x0436;
+    CMD_DISCONNECT_PHYSICAL_LINK = 0x0437;
+    CMD_CREATE_LOGICAL_LINK = 0x0438;
+    CMD_ACCEPT_LOGICAL_LINK = 0x0439;
+    CMD_DISCONNECT_LOGICAL_LINK = 0x043A;
+    CMD_LOGICAL_LINK_CANCEL = 0x043B;
+    CMD_FLOW_SPEC_MODIFY = 0x043C;
+    // END: AMP commands
+    CMD_ENH_SETUP_ESCO_CONNECTION = 0x043D;
+    CMD_ENH_ACCEPT_ESCO_CONNECTION = 0x043E;
+    CMD_TRUNCATED_PAGE = 0x043F;
+    CMD_TRUNCATED_PAGE_CANCEL = 0x0440;
+    CMD_SET_CLB = 0x0441;
+    CMD_RECEIVE_CLB = 0x0442;
+    CMD_START_SYNC_TRAIN = 0x0443;
+    CMD_RECEIVE_SYNC_TRAIN = 0x0444;
+    CMD_REM_OOB_EXTENDED_DATA_REQ_REPLY = 0x0445; // Not currently used in system/bt
+    // Link policy commands 0x0800
+    CMD_HOLD_MODE = 0x0801;
+    CMD_SNIFF_MODE = 0x0803;
+    CMD_EXIT_SNIFF_MODE = 0x0804;
+    CMD_PARK_MODE = 0x0805;
+    CMD_EXIT_PARK_MODE = 0x0806;
+    CMD_QOS_SETUP = 0x0807;
+    CMD_ROLE_DISCOVERY = 0x0809;
+    CMD_SWITCH_ROLE = 0x080B;
+    CMD_READ_POLICY_SETTINGS = 0x080C;
+    CMD_WRITE_POLICY_SETTINGS = 0x080D;
+    CMD_READ_DEF_POLICY_SETTINGS = 0x080E;
+    CMD_WRITE_DEF_POLICY_SETTINGS = 0x080F;
+    CMD_FLOW_SPECIFICATION = 0x0810;
+    CMD_SNIFF_SUB_RATE = 0x0811;
+    // Host controller baseband commands 0x0C00
+    CMD_SET_EVENT_MASK = 0x0C01;
+    CMD_RESET = 0x0C03;
+    CMD_SET_EVENT_FILTER = 0x0C05;
+    CMD_FLUSH = 0x0C08;
+    CMD_READ_PIN_TYPE = 0x0C09;
+    CMD_WRITE_PIN_TYPE = 0x0C0A;
+    CMD_CREATE_NEW_UNIT_KEY = 0x0C0B;
+    CMD_GET_MWS_TRANS_LAYER_CFG = 0x0C0C; // Deprecated (not used in spec)
+    CMD_READ_STORED_LINK_KEY = 0x0C0D;
+    CMD_WRITE_STORED_LINK_KEY = 0x0C11;
+    CMD_DELETE_STORED_LINK_KEY = 0x0C12;
+    CMD_CHANGE_LOCAL_NAME = 0x0C13;
+    CMD_READ_LOCAL_NAME = 0x0C14;
+    CMD_READ_CONN_ACCEPT_TOUT = 0x0C15;
+    CMD_WRITE_CONN_ACCEPT_TOUT = 0x0C16;
+    CMD_READ_PAGE_TOUT = 0x0C17;
+    CMD_WRITE_PAGE_TOUT = 0x0C18;
+    CMD_READ_SCAN_ENABLE = 0x0C19;
+    CMD_WRITE_SCAN_ENABLE = 0x0C1A;
+    CMD_READ_PAGESCAN_CFG = 0x0C1B;
+    CMD_WRITE_PAGESCAN_CFG = 0x0C1C;
+    CMD_READ_INQUIRYSCAN_CFG = 0x0C1D;
+    CMD_WRITE_INQUIRYSCAN_CFG = 0x0C1E;
+    CMD_READ_AUTHENTICATION_ENABLE = 0x0C1F;
+    CMD_WRITE_AUTHENTICATION_ENABLE = 0x0C20;
+    CMD_READ_ENCRYPTION_MODE = 0x0C21; // Deprecated
+    CMD_WRITE_ENCRYPTION_MODE = 0x0C22; // Deprecated
+    CMD_READ_CLASS_OF_DEVICE = 0x0C23;
+    CMD_WRITE_CLASS_OF_DEVICE = 0x0C24;
+    CMD_READ_VOICE_SETTINGS = 0x0C25;
+    CMD_WRITE_VOICE_SETTINGS = 0x0C26;
+    CMD_READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27;
+    CMD_WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28;
+    CMD_READ_NUM_BCAST_REXMITS = 0x0C29;
+    CMD_WRITE_NUM_BCAST_REXMITS = 0x0C2A;
+    CMD_READ_HOLD_MODE_ACTIVITY = 0x0C2B;
+    CMD_WRITE_HOLD_MODE_ACTIVITY = 0x0C2C;
+    CMD_READ_TRANSMIT_POWER_LEVEL = 0x0C2D;
+    CMD_READ_SCO_FLOW_CTRL_ENABLE = 0x0C2E;
+    CMD_WRITE_SCO_FLOW_CTRL_ENABLE = 0x0C2F;
+    CMD_SET_HC_TO_HOST_FLOW_CTRL = 0x0C31;
+    CMD_HOST_BUFFER_SIZE = 0x0C33;
+    CMD_HOST_NUM_PACKETS_DONE = 0x0C35;
+    CMD_READ_LINK_SUPER_TOUT = 0x0C36;
+    CMD_WRITE_LINK_SUPER_TOUT = 0x0C37;
+    CMD_READ_NUM_SUPPORTED_IAC = 0x0C38;
+    CMD_READ_CURRENT_IAC_LAP = 0x0C39;
+    CMD_WRITE_CURRENT_IAC_LAP = 0x0C3A;
+    CMD_READ_PAGESCAN_PERIOD_MODE = 0x0C3B; // Deprecated
+    CMD_WRITE_PAGESCAN_PERIOD_MODE = 0x0C3C; // Deprecated
+    CMD_READ_PAGESCAN_MODE = 0x0C3D; // Deprecated
+    CMD_WRITE_PAGESCAN_MODE = 0x0C3E; // Deprecated
+    CMD_SET_AFH_CHANNELS = 0x0C3F;
+    CMD_READ_INQSCAN_TYPE = 0x0C42;
+    CMD_WRITE_INQSCAN_TYPE = 0x0C43;
+    CMD_READ_INQUIRY_MODE = 0x0C44;
+    CMD_WRITE_INQUIRY_MODE = 0x0C45;
+    CMD_READ_PAGESCAN_TYPE = 0x0C46;
+    CMD_WRITE_PAGESCAN_TYPE = 0x0C47;
+    CMD_READ_AFH_ASSESSMENT_MODE = 0x0C48;
+    CMD_WRITE_AFH_ASSESSMENT_MODE = 0x0C49;
+    CMD_READ_EXT_INQ_RESPONSE = 0x0C51;
+    CMD_WRITE_EXT_INQ_RESPONSE = 0x0C52;
+    CMD_REFRESH_ENCRYPTION_KEY = 0x0C53;
+    CMD_READ_SIMPLE_PAIRING_MODE = 0x0C55;
+    CMD_WRITE_SIMPLE_PAIRING_MODE = 0x0C56;
+    CMD_READ_LOCAL_OOB_DATA = 0x0C57;
+    CMD_READ_INQ_TX_POWER_LEVEL = 0x0C58;
+    CMD_WRITE_INQ_TX_POWER_LEVEL = 0x0C59;
+    CMD_READ_ERRONEOUS_DATA_RPT = 0x0C5A;
+    CMD_WRITE_ERRONEOUS_DATA_RPT = 0x0C5B;
+    CMD_ENHANCED_FLUSH = 0x0C5F;
+    CMD_SEND_KEYPRESS_NOTIF = 0x0C60;
+    CMD_READ_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C61;
+    CMD_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C62;
+    CMD_SET_EVENT_MASK_PAGE_2 = 0x0C63;
+    CMD_READ_LOCATION_DATA = 0x0C64;
+    CMD_WRITE_LOCATION_DATA = 0x0C65;
+    CMD_READ_FLOW_CONTROL_MODE = 0x0C66;
+    CMD_WRITE_FLOW_CONTROL_MODE = 0x0C67;
+    CMD_READ_ENHANCED_TX_PWR_LEVEL = 0x0C68; // Not currently used in system/bt
+    CMD_READ_BE_FLUSH_TOUT = 0x0C69;
+    CMD_WRITE_BE_FLUSH_TOUT = 0x0C6A;
+    CMD_SHORT_RANGE_MODE = 0x0C6B;
+    CMD_READ_BLE_HOST_SUPPORT = 0x0C6C;
+    CMD_WRITE_BLE_HOST_SUPPORT = 0x0C6D;
+    CMD_SET_MWS_CHANNEL_PARAMETERS = 0x0C6E;
+    CMD_SET_EXTERNAL_FRAME_CONFIGURATION = 0x0C6F;
+    CMD_SET_MWS_SIGNALING = 0x0C70;
+    CMD_SET_MWS_TRANSPORT_LAYER = 0x0C71;
+    CMD_SET_MWS_SCAN_FREQUENCY_TABLE = 0x0C72;
+    CMD_SET_MWS_PATTERN_CONFIGURATION = 0x0C73;
+    CMD_SET_RESERVED_LT_ADDR = 0x0C74;
+    CMD_DELETE_RESERVED_LT_ADDR = 0x0C75;
+    CMD_WRITE_CLB_DATA = 0x0C76;
+    CMD_READ_SYNC_TRAIN_PARAM = 0x0C77;
+    CMD_WRITE_SYNC_TRAIN_PARAM = 0x0C78;
+    CMD_READ_SECURE_CONNS_SUPPORT = 0x0C79;
+    CMD_WRITE_SECURE_CONNS_SUPPORT = 0x0C7A;
+    CMD_READ_AUTHED_PAYLOAD_TIMEOUT = 0x0C7B; // Not currently used in system/bt
+    CMD_WRITE_AUTHED_PAYLOAD_TIMEOUT = 0x0C7C; // Not currently used in system/bt
+    CMD_READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D; // Not currently used in system/bt
+    CMD_READ_EXTENDED_PAGE_TIMEOUT = 0x0C7E; // Not currently used in system/bt
+    CMD_WRITE_EXTENDED_PAGE_TIMEOUT = 0x0C7F; // Not currently used in system/bt
+    CMD_READ_EXTENDED_INQUIRY_LENGTH = 0x0C80; // Not currently used in system/bt
+    CMD_WRITE_EXTENDED_INQUIRY_LENGTH = 0x0C81; // Not currently used in system/bt
+    // Informational parameter commands 0x1000
+    CMD_READ_LOCAL_VERSION_INFO = 0x1001;
+    CMD_READ_LOCAL_SUPPORTED_CMDS = 0x1002;
+    CMD_READ_LOCAL_FEATURES = 0x1003;
+    CMD_READ_LOCAL_EXT_FEATURES = 0x1004;
+    CMD_READ_BUFFER_SIZE = 0x1005;
+    CMD_READ_COUNTRY_CODE = 0x1007; // Deprecated
+    CMD_READ_BD_ADDR = 0x1009;
+    CMD_READ_DATA_BLOCK_SIZE = 0x100A;
+    CMD_READ_LOCAL_SUPPORTED_CODECS = 0x100B;
+    // Status parameter commands 0x1400
+    CMD_READ_FAILED_CONTACT_COUNTER = 0x1401;
+    CMD_RESET_FAILED_CONTACT_COUNTER = 0x1402;
+    CMD_GET_LINK_QUALITY = 0x1403;
+    CMD_READ_RSSI = 0x1405;
+    CMD_READ_AFH_CH_MAP = 0x1406;
+    CMD_READ_CLOCK = 0x1407;
+    CMD_READ_ENCR_KEY_SIZE = 0x1408;
+    CMD_READ_LOCAL_AMP_INFO = 0x1409;
+    CMD_READ_LOCAL_AMP_ASSOC = 0x140A;
+    CMD_WRITE_REMOTE_AMP_ASSOC = 0x140B;
+    CMD_GET_MWS_TRANSPORT_CFG = 0x140C; // Not currently used in system/bt
+    CMD_SET_TRIGGERED_CLK_CAPTURE = 0x140D; // Not currently used in system/bt
+    // Testing commands 0x1800
+    CMD_READ_LOOPBACK_MODE = 0x1801;
+    CMD_WRITE_LOOPBACK_MODE = 0x1802;
+    CMD_ENABLE_DEV_UNDER_TEST_MODE = 0x1803;
+    CMD_WRITE_SIMP_PAIR_DEBUG_MODE = 0x1804;
+    CMD_ENABLE_AMP_RCVR_REPORTS = 0x1807;
+    CMD_AMP_TEST_END = 0x1808;
+    CMD_AMP_TEST = 0x1809;
+    CMD_WRITE_SECURE_CONN_TEST_MODE = 0x180A; // Not currently used in system/bt
+    // BLE commands 0x2000
+    CMD_BLE_SET_EVENT_MASK = 0x2001;
+    CMD_BLE_READ_BUFFER_SIZE = 0x2002;
+    CMD_BLE_READ_LOCAL_SPT_FEAT = 0x2003;
+    CMD_BLE_WRITE_LOCAL_SPT_FEAT = 0x2004;
+    CMD_BLE_WRITE_RANDOM_ADDR = 0x2005;
+    CMD_BLE_WRITE_ADV_PARAMS = 0x2006;
+    CMD_BLE_READ_ADV_CHNL_TX_POWER = 0x2007;
+    CMD_BLE_WRITE_ADV_DATA = 0x2008;
+    CMD_BLE_WRITE_SCAN_RSP_DATA = 0x2009;
+    CMD_BLE_WRITE_ADV_ENABLE = 0x200A;
+    CMD_BLE_WRITE_SCAN_PARAMS = 0x200B;
+    CMD_BLE_WRITE_SCAN_ENABLE = 0x200C;
+    CMD_BLE_CREATE_LL_CONN = 0x200D;
+    CMD_BLE_CREATE_CONN_CANCEL = 0x200E;
+    CMD_BLE_READ_WHITE_LIST_SIZE = 0x200F;
+    CMD_BLE_CLEAR_WHITE_LIST = 0x2010;
+    CMD_BLE_ADD_WHITE_LIST = 0x2011;
+    CMD_BLE_REMOVE_WHITE_LIST = 0x2012;
+    CMD_BLE_UPD_LL_CONN_PARAMS = 0x2013;
+    CMD_BLE_SET_HOST_CHNL_CLASS = 0x2014;
+    CMD_BLE_READ_CHNL_MAP = 0x2015;
+    CMD_BLE_READ_REMOTE_FEAT = 0x2016;
+    CMD_BLE_ENCRYPT = 0x2017;
+    CMD_BLE_RAND = 0x2018;
+    CMD_BLE_START_ENC = 0x2019;
+    CMD_BLE_LTK_REQ_REPLY = 0x201A;
+    CMD_BLE_LTK_REQ_NEG_REPLY = 0x201B;
+    CMD_BLE_READ_SUPPORTED_STATES = 0x201C;
+    CMD_BLE_RECEIVER_TEST = 0x201D;
+    CMD_BLE_TRANSMITTER_TEST = 0x201E;
+    CMD_BLE_TEST_END = 0x201F;
+    CMD_BLE_RC_PARAM_REQ_REPLY = 0x2020;
+    CMD_BLE_RC_PARAM_REQ_NEG_REPLY = 0x2021;
+    CMD_BLE_SET_DATA_LENGTH = 0x2022;
+    CMD_BLE_READ_DEFAULT_DATA_LENGTH = 0x2023;
+    CMD_BLE_WRITE_DEFAULT_DATA_LENGTH = 0x2024;
+    CMD_BLE_GENERATE_DHKEY = 0x2026; // Not currently used in system/bt
+    CMD_BLE_ADD_DEV_RESOLVING_LIST = 0x2027;
+    CMD_BLE_RM_DEV_RESOLVING_LIST = 0x2028;
+    CMD_BLE_CLEAR_RESOLVING_LIST = 0x2029;
+    CMD_BLE_READ_RESOLVING_LIST_SIZE = 0x202A;
+    CMD_BLE_READ_RESOLVABLE_ADDR_PEER = 0x202B;
+    CMD_BLE_READ_RESOLVABLE_ADDR_LOCAL = 0x202C;
+    CMD_BLE_SET_ADDR_RESOLUTION_ENABLE = 0x202D;
+    CMD_BLE_SET_RAND_PRIV_ADDR_TIMOUT = 0x202E;
+    CMD_BLE_READ_MAXIMUM_DATA_LENGTH = 0x202F;
+    CMD_BLE_READ_PHY = 0x2030;
+    CMD_BLE_SET_DEFAULT_PHY = 0x2031;
+    CMD_BLE_SET_PHY = 0x2032;
+    CMD_BLE_ENH_RECEIVER_TEST = 0x2033;
+    CMD_BLE_ENH_TRANSMITTER_TEST = 0x2034;
+    CMD_BLE_SET_EXT_ADVERTISING_RANDOM_ADDRESS = 0x2035;
+    CMD_BLE_SET_EXT_ADVERTISING_PARAM = 0x2036;
+    CMD_BLE_SET_EXT_ADVERTISING_DATA = 0x2037;
+    CMD_BLE_SET_EXT_ADVERTISING_SCAN_RESP = 0x2038;
+    CMD_BLE_SET_EXT_ADVERTISING_ENABLE = 0x2039;
+    CMD_BLE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A;
+    CMD_BLE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B;
+    CMD_BLE_REMOVE_ADVERTISING_SET = 0x203C;
+    CMD_BLE_CLEAR_ADVERTISING_SETS = 0x203D;
+    CMD_BLE_SET_PERIODIC_ADVERTISING_PARAM = 0x203E;
+    CMD_BLE_SET_PERIODIC_ADVERTISING_DATA = 0x203F;
+    CMD_BLE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040;
+    CMD_BLE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041;
+    CMD_BLE_SET_EXTENDED_SCAN_ENABLE = 0x2042;
+    CMD_BLE_EXTENDED_CREATE_CONNECTION = 0x2043;
+    CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044;
+    CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045;
+    CMD_BLE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046;
+    CMD_BLE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = 0x2047;
+    CMD_BLE_RM_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = 0x2048;
+    CMD_BLE_CLEAR_PERIODIC_ADVERTISING_LIST = 0x2049;
+    CMD_BLE_READ_PERIODIC_ADVERTISING_LIST_SIZE = 0x204A;
+    CMD_BLE_READ_TRANSMIT_POWER = 0x204B;
+    CMD_BLE_READ_RF_COMPENS_POWER = 0x204C;
+    CMD_BLE_WRITE_RF_COMPENS_POWER = 0x204D;
+    CMD_BLE_SET_PRIVACY_MODE = 0x204E;
+    // Vendor specific commands 0xFC00 and above
+    // Android vendor specific commands defined in
+    // https://source.android.com/devices/bluetooth/hci_requirements#vendor-specific-capabilities
+    CMD_BLE_VENDOR_CAP = 0xFD53;
+    CMD_BLE_MULTI_ADV = 0xFD54;
+    CMD_BLE_BATCH_SCAN = 0xFD56;
+    CMD_BLE_ADV_FILTER = 0xFD57;
+    CMD_BLE_TRACK_ADV = 0xFD58;
+    CMD_BLE_ENERGY_INFO = 0xFD59;
+    CMD_BLE_EXTENDED_SCAN_PARAMS = 0xFD5A;
+    CMD_CONTROLLER_DEBUG_INFO = 0xFD5B;
+    CMD_CONTROLLER_A2DP_OPCODE = 0xFD5D;
+    CMD_BRCM_SET_ACL_PRIORITY = 0xFC57;
+    // Other vendor specific commands below here
+}
+
+// HCI event codes from the Bluetooth 5.0 specification Vol 2, Part 7, Section 7
+// Original definition: system/bt/stack/include/hcidefs.h
+enum EventEnum {
+    // Event is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    EVT_UNKNOWN = 0xFFF;
+    EVT_INQUIRY_COMP = 0x01;
+    EVT_INQUIRY_RESULT = 0x02;
+    EVT_CONNECTION_COMP = 0x03;
+    EVT_CONNECTION_REQUEST = 0x04;
+    EVT_DISCONNECTION_COMP = 0x05;
+    EVT_AUTHENTICATION_COMP = 0x06;
+    EVT_RMT_NAME_REQUEST_COMP = 0x07;
+    EVT_ENCRYPTION_CHANGE = 0x08;
+    EVT_CHANGE_CONN_LINK_KEY = 0x09;
+    EVT_MASTER_LINK_KEY_COMP = 0x0A;
+    EVT_READ_RMT_FEATURES_COMP = 0x0B;
+    EVT_READ_RMT_VERSION_COMP = 0x0C;
+    EVT_QOS_SETUP_COMP = 0x0D;
+    EVT_COMMAND_COMPLETE = 0x0E;
+    EVT_COMMAND_STATUS = 0x0F;
+    EVT_HARDWARE_ERROR = 0x10;
+    EVT_FLUSH_OCCURED = 0x11;
+    EVT_ROLE_CHANGE = 0x12;
+    EVT_NUM_COMPL_DATA_PKTS = 0x13;
+    EVT_MODE_CHANGE = 0x14;
+    EVT_RETURN_LINK_KEYS = 0x15;
+    EVT_PIN_CODE_REQUEST = 0x16;
+    EVT_LINK_KEY_REQUEST = 0x17;
+    EVT_LINK_KEY_NOTIFICATION = 0x18;
+    EVT_LOOPBACK_COMMAND = 0x19;
+    EVT_DATA_BUF_OVERFLOW = 0x1A;
+    EVT_MAX_SLOTS_CHANGED = 0x1B;
+    EVT_READ_CLOCK_OFF_COMP = 0x1C;
+    EVT_CONN_PKT_TYPE_CHANGE = 0x1D;
+    EVT_QOS_VIOLATION = 0x1E;
+    EVT_PAGE_SCAN_MODE_CHANGE = 0x1F; // Deprecated
+    EVT_PAGE_SCAN_REP_MODE_CHNG = 0x20;
+    EVT_FLOW_SPECIFICATION_COMP = 0x21;
+    EVT_INQUIRY_RSSI_RESULT = 0x22;
+    EVT_READ_RMT_EXT_FEATURES_COMP = 0x23;
+    EVT_ESCO_CONNECTION_COMP = 0x2C;
+    EVT_ESCO_CONNECTION_CHANGED = 0x2D;
+    EVT_SNIFF_SUB_RATE = 0x2E;
+    EVT_EXTENDED_INQUIRY_RESULT = 0x2F;
+    EVT_ENCRYPTION_KEY_REFRESH_COMP = 0x30;
+    EVT_IO_CAPABILITY_REQUEST = 0x31;
+    EVT_IO_CAPABILITY_RESPONSE = 0x32;
+    EVT_USER_CONFIRMATION_REQUEST = 0x33;
+    EVT_USER_PASSKEY_REQUEST = 0x34;
+    EVT_REMOTE_OOB_DATA_REQUEST = 0x35;
+    EVT_SIMPLE_PAIRING_COMPLETE = 0x36;
+    EVT_LINK_SUPER_TOUT_CHANGED = 0x38;
+    EVT_ENHANCED_FLUSH_COMPLETE = 0x39;
+    EVT_USER_PASSKEY_NOTIFY = 0x3B;
+    EVT_KEYPRESS_NOTIFY = 0x3C;
+    EVT_RMT_HOST_SUP_FEAT_NOTIFY = 0x3D;
+    EVT_BLE_META = 0x3E;
+    EVT_PHYSICAL_LINK_COMP = 0x40;
+    EVT_CHANNEL_SELECTED = 0x41;
+    EVT_DISC_PHYSICAL_LINK_COMP = 0x42;
+    EVT_PHY_LINK_LOSS_EARLY_WARNING = 0x43;
+    EVT_PHY_LINK_RECOVERY = 0x44;
+    EVT_LOGICAL_LINK_COMP = 0x45;
+    EVT_DISC_LOGICAL_LINK_COMP = 0x46;
+    EVT_FLOW_SPEC_MODIFY_COMP = 0x47;
+    EVT_NUM_COMPL_DATA_BLOCKS = 0x48;
+    EVT_AMP_TEST_START = 0x49; // Not currently used in system/bt
+    EVT_AMP_TEST_END = 0x4A; // Not currently used in system/bt
+    EVT_AMP_RECEIVER_RPT = 0x4B; // Not currently used in system/bt
+    EVT_SHORT_RANGE_MODE_COMPLETE = 0x4C;
+    EVT_AMP_STATUS_CHANGE = 0x4D;
+    EVT_SET_TRIGGERED_CLOCK_CAPTURE = 0x4E;
+    EVT_SYNC_TRAIN_CMPL = 0x4F; // Not currently used in system/bt
+    EVT_SYNC_TRAIN_RCVD = 0x50; // Not currently used in system/bt
+    EVT_CONNLESS_SLAVE_BROADCAST_RCVD = 0x51; // Not currently used in system/bt
+    EVT_CONNLESS_SLAVE_BROADCAST_TIMEOUT = 0x52; // Not currently used in system/bt
+    EVT_TRUNCATED_PAGE_CMPL = 0x53; // Not currently used in system/bt
+    EVT_SLAVE_PAGE_RES_TIMEOUT = 0x54; // Not currently used in system/bt
+    EVT_CONNLESS_SLAVE_BROADCAST_CHNL_MAP_CHANGE = 0x55; // Not currently used in system/bt
+    EVT_INQUIRY_RES_NOTIFICATION = 0x56; // Not currently used in system/bt
+    EVT_AUTHED_PAYLOAD_TIMEOUT = 0x57; // Not currently used in system/bt
+    EVT_SAM_STATUS_CHANGE = 0x58; // Not currently used in system/bt
+}
+
+// Bluetooth low energy related meta event codes
+// from the Bluetooth 5.0 specification Vol 2, Part E, Section 7.7.65
+// Original definition: system/bt/stack/include/hcidefs.h
+enum BleMetaEventEnum {
+    // BLE meta event code is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    BLE_EVT_UNKNOWN = 0xFFF;
+    BLE_EVT_CONN_COMPLETE_EVT = 0x01;
+    BLE_EVT_ADV_PKT_RPT_EVT = 0x02;
+    BLE_EVT_LL_CONN_PARAM_UPD_EVT = 0x03;
+    BLE_EVT_READ_REMOTE_FEAT_CMPL_EVT = 0x04;
+    BLE_EVT_LTK_REQ_EVT = 0x05;
+    BLE_EVT_RC_PARAM_REQ_EVT = 0x06;
+    BLE_EVT_DATA_LENGTH_CHANGE_EVT = 0x07;
+    BLE_EVT_READ_LOCAL_P256_PUB_KEY = 0x08; // Not currently used in system/bt
+    BLE_EVT_GEN_DHKEY_CMPL = 0x09; // Not currently used in system/bt
+    BLE_EVT_ENHANCED_CONN_COMPLETE_EVT = 0x0a;
+    BLE_EVT_DIRECT_ADV_EVT = 0x0b;
+    BLE_EVT_PHY_UPDATE_COMPLETE_EVT = 0x0c;
+    BLE_EVT_EXTENDED_ADVERTISING_REPORT_EVT = 0x0D;
+    BLE_EVT_PERIODIC_ADV_SYNC_EST_EVT = 0x0E;
+    BLE_EVT_PERIODIC_ADV_REPORT_EVT = 0x0F;
+    BLE_EVT_PERIODIC_ADV_SYNC_LOST_EVT = 0x10;
+    BLE_EVT_SCAN_TIMEOUT_EVT = 0x11;
+    BLE_EVT_ADVERTISING_SET_TERMINATED_EVT = 0x12;
+    BLE_EVT_SCAN_REQ_RX_EVT = 0x13;
+    BLE_EVT_CHNL_SELECTION_ALGORITHM = 0x14; // Not currently used in system/bt
+}
+
+// HCI status code from the Bluetooth 5.0 specification Vol 2, Part D.
+// Original definition: system/bt/stack/include/hcidefs.h
+enum StatusEnum {
+    // Status is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    STATUS_UNKNOWN = 0xFFF;
+    STATUS_SUCCESS = 0x00;
+    STATUS_ILLEGAL_COMMAND = 0x01;
+    STATUS_NO_CONNECTION = 0x02;
+    STATUS_HW_FAILURE = 0x03;
+    STATUS_PAGE_TIMEOUT = 0x04;
+    STATUS_AUTH_FAILURE = 0x05;
+    STATUS_KEY_MISSING = 0x06;
+    STATUS_MEMORY_FULL = 0x07;
+    STATUS_CONNECTION_TOUT = 0x08;
+    STATUS_MAX_NUM_OF_CONNECTIONS = 0x09;
+    STATUS_MAX_NUM_OF_SCOS = 0x0A;
+    STATUS_CONNECTION_EXISTS = 0x0B;
+    STATUS_COMMAND_DISALLOWED = 0x0C;
+    STATUS_HOST_REJECT_RESOURCES = 0x0D;
+    STATUS_HOST_REJECT_SECURITY = 0x0E;
+    STATUS_HOST_REJECT_DEVICE = 0x0F;
+    STATUS_HOST_TIMEOUT = 0x10;
+    STATUS_UNSUPPORTED_VALUE = 0x11;
+    STATUS_ILLEGAL_PARAMETER_FMT = 0x12;
+    STATUS_PEER_USER = 0x13;
+    STATUS_PEER_LOW_RESOURCES = 0x14;
+    STATUS_PEER_POWER_OFF = 0x15;
+    STATUS_CONN_CAUSE_LOCAL_HOST = 0x16;
+    STATUS_REPEATED_ATTEMPTS = 0x17;
+    STATUS_PAIRING_NOT_ALLOWED = 0x18;
+    STATUS_UNKNOWN_LMP_PDU = 0x19;
+    STATUS_UNSUPPORTED_REM_FEATURE = 0x1A;
+    STATUS_SCO_OFFSET_REJECTED = 0x1B;
+    STATUS_SCO_INTERVAL_REJECTED = 0x1C;
+    STATUS_SCO_AIR_MODE = 0x1D;
+    STATUS_INVALID_LMP_PARAM = 0x1E;
+    STATUS_UNSPECIFIED = 0x1F;
+    STATUS_UNSUPPORTED_LMP_FEATURE = 0x20;
+    STATUS_ROLE_CHANGE_NOT_ALLOWED = 0x21;
+    STATUS_LMP_RESPONSE_TIMEOUT = 0x22;
+    STATUS_LMP_STATUS_TRANS_COLLISION = 0x23;
+    STATUS_LMP_PDU_NOT_ALLOWED = 0x24;
+    STATUS_ENCRY_MODE_NOT_ACCEPTABLE = 0x25;
+    STATUS_UNIT_KEY_USED = 0x26;
+    STATUS_QOS_NOT_SUPPORTED = 0x27;
+    STATUS_INSTANT_PASSED = 0x28;
+    STATUS_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29;
+    STATUS_DIFF_TRANSACTION_COLLISION = 0x2A;
+    STATUS_UNDEFINED_0x2B = 0x2B; // Not used
+    STATUS_QOS_UNACCEPTABLE_PARAM = 0x2C;
+    STATUS_QOS_REJECTED = 0x2D;
+    STATUS_CHAN_CLASSIF_NOT_SUPPORTED = 0x2E;
+    STATUS_INSUFFCIENT_SECURITY = 0x2F;
+    STATUS_PARAM_OUT_OF_RANGE = 0x30;
+    STATUS_UNDEFINED_0x31 = 0x31; // Not used
+    STATUS_ROLE_SWITCH_PENDING = 0x32;
+    STATUS_UNDEFINED_0x33 = 0x33;
+    STATUS_RESERVED_SLOT_VIOLATION = 0x34;
+    STATUS_ROLE_SWITCH_FAILED = 0x35;
+    STATUS_INQ_RSP_DATA_TOO_LARGE = 0x36;
+    STATUS_SIMPLE_PAIRING_NOT_SUPPORTED = 0x37;
+    STATUS_HOST_BUSY_PAIRING = 0x38;
+    STATUS_REJ_NO_SUITABLE_CHANNEL = 0x39;
+    STATUS_CONTROLLER_BUSY = 0x3A;
+    STATUS_UNACCEPT_CONN_INTERVAL = 0x3B;
+    STATUS_ADVERTISING_TIMEOUT = 0x3C;
+    STATUS_CONN_TOUT_DUE_TO_MIC_FAILURE = 0x3D;
+    STATUS_CONN_FAILED_ESTABLISHMENT = 0x3E;
+    STATUS_MAC_CONNECTION_FAILED = 0x3F;
+    STATUS_LT_ADDR_ALREADY_IN_USE = 0x40;
+    STATUS_LT_ADDR_NOT_ALLOCATED = 0x41;
+    STATUS_CLB_NOT_ENABLED = 0x42;
+    STATUS_CLB_DATA_TOO_BIG = 0x43;
+    STATUS_OPERATION_CANCELED_BY_HOST = 0x44; // Not currently used in system/bt
+}
diff --git a/core/proto/android/bluetooth/hfp/enums.proto b/core/proto/android/bluetooth/hfp/enums.proto
new file mode 100644
index 0000000..d286e4b
--- /dev/null
+++ b/core/proto/android/bluetooth/hfp/enums.proto
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.bluetooth.hfp;
+
+option java_outer_classname = "BluetoothHfpProtoEnums";
+option java_multiple_files = true;
+
+enum ScoCodec {
+    SCO_CODEC_UNKNOWN = 0;
+    SCO_CODEC_CVSD = 1;
+    // Default codec behind Wide Band Speech
+    SCO_CODEC_MSBC = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a914369..4158577 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -436,11 +436,19 @@
         // Ordered GPU debug layer list for GLES
         // i.e. <layer1>:<layer2>:...:<layerN>
         optional SettingProto debug_layers_gles = 7;
-        // Apps opt in to load graphics driver from Game Update Package
-        // instead of native graphcis driver through developer options.
-        optional SettingProto gup_dev_opt_in_apps = 8;
-        // Apps on the black list that are forbidden to useGame Update Package.
-        optional SettingProto gup_black_list = 9;
+        // GUP - Game Update Package global preference for all Apps
+        // 0 = Default
+        // 1 = All Apps use Game Update Package
+        // 2 = All Apps use system graphics driver
+        optional SettingProto gup_dev_all_apps = 8;
+        // GUP - List of Apps selected to use Game Update Package
+        // i.e. <pkg1>,<pkg2>,...,<pkgN>
+        optional SettingProto gup_dev_opt_in_apps = 9;
+        // GUP - List of Apps selected not to use Game Update Package
+        // i.e. <pkg1>,<pkg2>,...,<pkgN>
+        optional SettingProto gup_dev_opt_out_apps = 10;
+        // GUP - List of Apps that are forbidden to use Game Update Package
+        optional SettingProto gup_blacklist = 11;
     }
     optional Gpu gpu = 59;
 
@@ -558,6 +566,8 @@
     // ringer mode.
     optional SettingProto mode_ringer = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    optional SettingProto apply_ramping_ringer = 147 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     message MultiSim {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -1015,7 +1025,9 @@
 
     optional SettingProto zram_enabled = 139 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    optional SettingProto app_ops_constants = 148 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 147;
+    // Next tag = 149;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 0e052fe..4bfd4d2 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -182,6 +182,7 @@
         optional SettingProto pulse_on_pick_up = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto pulse_on_long_press = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto pulse_on_double_tap = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto pulse_on_tap = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Doze doze = 21;
 
@@ -525,7 +526,10 @@
     }
     optional Zen zen = 71;
 
+    optional SettingProto skip_gesture_enabled = 74 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto silence_gesture_enabled = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 74;
+    // Next tag = 76;
 }
diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto
new file mode 100644
index 0000000..b70bb67
--- /dev/null
+++ b/core/proto/android/server/connectivity/data_stall_event.proto
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 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 com.android.server.connectivity;
+option java_multiple_files = true;
+option java_outer_classname = "DataStallEventProto";
+
+enum ProbeResult {
+    UNKNOWN = 0;
+    VALID = 1;
+    INVALID = 2;
+    PORTAL = 3;
+}
+
+enum ApBand {
+    AP_BAND_UNKNOWN = 0;
+    AP_BAND_2GHZ = 1;
+    AP_BAND_5GHZ = 2;
+}
+
+// Refer to definition in ServiceState.java.
+enum RadioTech {
+  RADIO_TECHNOLOGY_UNKNOWN = 0;
+  RADIO_TECHNOLOGY_GPRS = 1;
+  RADIO_TECHNOLOGY_EDGE = 2;
+  RADIO_TECHNOLOGY_UMTS = 3;
+  RADIO_TECHNOLOGY_IS95A = 4;
+  RADIO_TECHNOLOGY_IS95B = 5;
+  RADIO_TECHNOLOGY_1xRTT = 6;
+  RADIO_TECHNOLOGY_EVDO_0 = 7;
+  RADIO_TECHNOLOGY_EVDO_A = 8;
+  RADIO_TECHNOLOGY_HSDPA = 9;
+  RADIO_TECHNOLOGY_HSUPA = 10;
+  RADIO_TECHNOLOGY_HSPA = 11;
+  RADIO_TECHNOLOGY_EVDO_B = 12;
+  RADIO_TECHNOLOGY_EHRPD = 13;
+  RADIO_TECHNOLOGY_LTE = 14;
+  RADIO_TECHNOLOGY_HSPAP = 15;
+  RADIO_TECHNOLOGY_GSM = 16;
+  RADIO_TECHNOLOGY_TD_SCDMA = 17;
+  RADIO_TECHNOLOGY_IWLAN = 18;
+  RADIO_TECHNOLOGY_LTE_CA = 19;
+  RADIO_TECHNOLOGY_NR = 20;
+}
+
+// Cellular specific information.
+message CellularData {
+    // Indicate the radio technology at the time of data stall suspected.
+    optional RadioTech rat_type = 1;
+    // True if device is in roaming network at the time of data stall suspected.
+    optional bool is_roaming = 2;
+    // Registered network MccMnc when data stall happen
+    optional string network_mccmnc = 3;
+    // Indicate the SIM card carrier.
+    optional string sim_mccmnc = 4;
+    // Signal strength level at the time of data stall suspected.
+    optional int32 signal_strength = 5;
+}
+
+// Wifi specific information.
+message WifiData {
+    // Signal strength at the time of data stall suspected.
+    // RSSI range is between -55 to -110.
+    optional int32 signal_strength = 1;
+    // AP band.
+    optional ApBand wifi_band = 2;
+}
+
+message DnsEvent {
+    // The dns return code.
+    repeated int32 dns_return_code = 1;
+    // Indicate the timestamp of the dns event.
+    repeated int64 dns_time = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 0ec8c1a..7f3ea7a 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -403,18 +403,23 @@
         optional bool is_charging = 1;
         optional bool is_in_parole = 2;
 
+        // List of UIDs currently in the foreground.
+        repeated int32 foreground_uids = 3;
+
         message TrackedJob {
             option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
             optional JobStatusDumpProto.Bucket effective_standby_bucket = 3;
-            optional bool has_quota = 4;
+            // If the job started while the app was in the TOP state.
+            optional bool is_top_started_job = 4;
+            optional bool has_quota = 5;
             // The amount of time that this job has remaining in its quota. This
             // can be negative if the job is out of quota.
-            optional int64 remaining_quota_ms = 5;
+            optional int64 remaining_quota_ms = 6;
         }
-        repeated TrackedJob tracked_jobs = 3;
+        repeated TrackedJob tracked_jobs = 4;
 
         message Package {
             option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -456,7 +461,7 @@
 
             repeated TimingSession saved_sessions = 3;
         }
-        repeated PackageStats package_stats = 4;
+        repeated PackageStats package_stats = 5;
     }
     message StorageController {
         option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/server/location/enums.proto b/core/proto/android/server/location/enums.proto
index b6dc589..943ff18 100644
--- a/core/proto/android/server/location/enums.proto
+++ b/core/proto/android/server/location/enums.proto
@@ -28,3 +28,105 @@
     GPS_SIGNAL_QUALITY_POOR = 0;
     GPS_SIGNAL_QUALITY_GOOD = 1;
 }
+
+// A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc.
+enum GnssNiType {
+    VOICE = 1;
+    UMTS_SUPL = 2;
+    UMTS_CTRL_PLANE = 3;
+    EMERGENCY_SUPL = 4;
+};
+
+// GNSS NI responses, used to define the response in NI structures.
+enum GnssUserResponseType {
+    RESPONSE_ACCEPT = 1;
+    RESPONSE_DENY = 2;
+    RESPONSE_NORESP = 3;
+};
+
+// GNSS NI data encoding scheme.
+enum GnssNiEncodingType {
+    ENC_NONE = 0;
+    ENC_SUPL_GSM_DEFAULT = 1;
+    ENC_SUPL_UTF8 = 2;
+    ENC_SUPL_UCS2 = 3;
+    ENC_UNKNOWN = -1;
+};
+
+// Protocol stack that initiated the non-framework location request.
+enum NfwProtocolStack {
+    // Cellular control plane requests.
+    CTRL_PLANE = 0;
+    // All types of SUPL requests.
+    SUPL = 1;
+    // All types of requests from IMS.
+    IMS = 10;
+    // All types of requests from SIM.
+    SIM = 11;
+    // Requests from other protocol stacks.
+    OTHER_PROTOCOL_STACK = 100;
+};
+
+// Source initiating/receiving the location information.
+enum NfwRequestor  {
+    // Wireless service provider.
+    CARRIER = 0;
+    // Device manufacturer.
+    OEM = 10;
+    // Modem chipset vendor.
+    MODEM_CHIPSET_VENDOR = 11;
+    // GNSS chipset vendor.
+    GNSS_CHIPSET_VENDOR = 12;
+    // Other chipset vendor.
+    OTHER_CHIPSET_VENDOR = 13;
+    // Automobile client.
+    AUTOMOBILE_CLIENT = 20;
+    // Other sources.
+    OTHER_REQUESTOR = 100;
+};
+
+// Indicates whether location information was provided for this request.
+enum NfwResponseType {
+    // Request rejected because framework has not given permission for this use case.
+    REJECTED = 0;
+    // Request accepted but could not provide location because of a failure.
+    ACCEPTED_NO_LOCATION_PROVIDED = 1;
+    // Request accepted and location provided.
+    ACCEPTED_LOCATION_PROVIDED = 2;
+};
+
+// The SUPL mode.
+enum SuplMode {
+    // Mobile Station Based.
+    MSB = 0x01;
+    // Mobile Station Assisted.
+    MSA = 0x02;
+};
+
+// Enum that hold the bit masks for various LTE Positioning Profile settings (LPP_PROFILE
+// configuration parameter). If none of the bits in the enum are set, the default setting is
+// Radio Resource Location Protocol(RRLP).
+enum LppProfile {
+    // Enable LTE Positioning Protocol user plane.
+    USER_PLANE = 0x01;
+    // Enable LTE Positioning Protocol Control plane.
+    CONTROL_PLANE = 0x02;
+};
+
+// Positioning protocol on A-Glonass system.
+enum GlonassPosProtocol {
+    // Radio Resource Control(RRC) control-plane.
+    RRC_CPLANE = 0x01;
+    // Radio Resource Location user-plane.
+    RRLP_CPLANE = 0x02;
+    // LTE Positioning Protocol User plane.
+    LPP_UPLANE = 0x04;
+};
+
+// Configurations of how GPS functionalities should be locked when user turns off GPS On setting.
+enum GpsLock {
+    // Lock Mobile Originated GPS functionalitues.
+    MO = 0x01;
+    // Lock Network Initiated GPS functionalities.
+    NI = 0x02;
+};
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index 4ecf52c..7f96d70 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -111,6 +111,7 @@
         optional EnabledState enabled_state = 7;
         optional string last_disabled_app_caller = 8;
         optional string suspending_package = 9;
+        optional int32 distraction_flags = 10;
     }
 
     // Name of package. e.g. "com.android.providers.telephony".
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index 71ebcc1..da801ff 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -43,7 +43,7 @@
  * Data model from /frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
  * This proto is defined based on the writeToParcel method.
  *
- * Next Tag: 10
+ * Next Tag: 11
  */
 message ProcessStatsSectionProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -76,6 +76,9 @@
     }
     repeated Status status = 7;
 
+    // Number of pages available of various types and sizes, representation fragmentation.
+    repeated ProcessStatsAvailablePagesProto available_pages = 10;
+
     // Stats for each process.
     repeated ProcessStatsProto process_stats = 8;
 
@@ -83,6 +86,24 @@
     repeated ProcessStatsPackageProto package_stats = 9;
 }
 
+// Next Tag: 5
+message ProcessStatsAvailablePagesProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Node these pages are in (as per /proc/pagetypeinfo)
+    optional int32 node = 1;
+
+    // Zone these pages are in (as per /proc/pagetypeinfo)
+    optional string zone = 2;
+
+    // Label for the type of these pages (as per /proc/pagetypeinfo)
+    optional string label = 3;
+
+    // Distribution of number of pages available by order size.  First entry in array is
+    // order 0, second is order 1, etc.  Each order increase is a doubling of page size.
+    repeated int32 pages_per_order = 4;
+}
+
 // Next Tag: 10
 message ProcessStatsStateProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/stats/docsui/docsui_enums.proto b/core/proto/android/stats/docsui/docsui_enums.proto
index 6cb606a..655b5e3 100644
--- a/core/proto/android/stats/docsui/docsui_enums.proto
+++ b/core/proto/android/stats/docsui/docsui_enums.proto
@@ -33,13 +33,14 @@
     MIME_UNKNOWN = 0;
     MIME_NONE = 1;
     MIME_ANY = 2;
-    MIME_AUDIO = 3;
-    MIME_IMAGE = 4;
-    MIME_MESSAGE = 5;
-    MIME_MULTIPART = 6;
-    MIME_TEXT = 7;
-    MIME_VIDEO = 8;
-    MIME_OTHER = 9;
+    MIME_APPLICATION = 3;
+    MIME_AUDIO = 4;
+    MIME_IMAGE = 5;
+    MIME_MESSAGE = 6;
+    MIME_MULTIPART = 7;
+    MIME_TEXT = 8;
+    MIME_VIDEO = 9;
+    MIME_OTHER = 10;
 }
 
 enum Root {
@@ -163,6 +164,8 @@
     ACTION_EXTRACT_TO = 29;
     ACTION_VIEW_IN_APPLICATION = 30;
     ACTION_INSPECTOR = 31;
+    ACTION_SEARCH_CHIP = 32;
+    ACTION_SEARCH_HISTORY = 33;
 }
 
 enum InvalidScopedAccess {
@@ -172,3 +175,20 @@
     SCOPED_DIR_ACCESS_ERROR = 3;
     SCOPED_DIR_ACCESS_DEPRECATED = 4;
 }
+
+enum SearchType {
+    TYPE_UNKNOWN = 0;
+    TYPE_CHIP_IMAGES = 1;
+    TYPE_CHIP_AUDIOS = 2;
+    TYPE_CHIP_VIDEOS = 3;
+    TYPE_CHIP_DOCS = 4;
+    TYPE_SEARCH_HISTORY = 5;
+    TYPE_SEARCH_STRING = 6;
+}
+
+enum SearchMode {
+    SEARCH_UNKNOWN = 0;
+    SEARCH_KEYWORD = 1;
+    SEARCH_CHIPS = 2;
+    SEARCH_KEYWORD_N_CHIPS = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0778304..ea0c8e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -51,6 +51,7 @@
     <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
     <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
     <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+    <protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" />
     <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.UID_REMOVED" />
     <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
@@ -1185,6 +1186,7 @@
     <!-- Used for permissions that are associated with activity recognition.
          TODO(zezeozue). STOPSHIP: Add icon -->
     <permission-group android:name="android.permission-group.ACTIVITY_RECOGNITION"
+        android:icon="@drawable/perm_group_activity_recognition"
         android:label="@string/permgrouplab_activityRecognition"
         android:description="@string/permgroupdesc_activityRecognition"
         android:request="@string/permgrouprequest_activityRecognition"
@@ -2208,8 +2210,9 @@
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
         android:protectionLevel="signature|installer" />
 
-    <!-- @SystemApi Allows an application to start an activity within its managed profile from
-         the personal profile.
+    <!-- @SystemApi Allows an application to start its own activities, but on a different profile
+         associated with the user. For example, an application running on the main profile of a user
+         can start an activity on a managed profile of that user.
          This permission is not available to third party applications.
          @hide -->
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
@@ -2256,7 +2259,7 @@
 
     <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
     <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
     <permission android:name="android.permission.ACTIVITY_EMBEDDING"
@@ -2268,6 +2271,11 @@
     <permission android:name="android.permission.START_ANY_ACTIVITY"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to start activities from background
+         @hide -->
+    <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
+        android:protectionLevel="signature|privileged|vendorPrivileged|oem" />
+
     <!-- @SystemApi Must be required by activities that handle the intent action
          {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
          hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
@@ -2526,6 +2534,16 @@
     <permission android:name="android.permission.WRITE_GSERVICES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide Allows an application to modify config settings.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WRITE_DEVICE_CONFIG"
+        android:protectionLevel="signature|configurator"/>
+
+    <!-- @SystemApi @hide Allows an application to read config settings.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_DEVICE_CONFIG"
+        android:protectionLevel="signature|preinstalled" />
+
     <!-- @SystemApi @TestApi Allows an application to call
         {@link android.app.ActivityManager#forceStopPackage}.
         @hide -->
@@ -3039,6 +3057,15 @@
     <permission android:name="android.permission.BIND_TEXT_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Must be required by a AttentionService
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_ATTENTION_SERVICE"
+                android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.BIND_ATTENTION_SERVICE" />
+
     <!-- Must be required by a {@link android.net.VpnService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature
@@ -3190,9 +3217,10 @@
 
     <!-- @SystemApi Required to add or remove another application as a device admin.
          <p>Not for use by third-party applications.
-         @hide -->
+         @hide
+         @removed -->
     <permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- @SystemApi Allows an app to reset the device password.
          <p>Not for use by third-party applications.
@@ -3514,19 +3542,25 @@
     <permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to provide remote displays.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.REMOTE_DISPLAY_PROVIDER"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to capture video output.
          <p>Not for use by third-party applications.</p>
           @hide
           @removed -->
     <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- Allows an application to capture secure video output.
          <p>Not for use by third-party applications.</p>
           @hide
           @removed -->
     <permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- Allows an application to know what content is playing and control its playback.
          <p>Not for use by third-party applications due to privacy of media consumption</p>  -->
@@ -3861,7 +3895,7 @@
 
     <!-- @SystemApi @hide Allows managing apk level rollbacks. -->
     <permission android:name="android.permission.MANAGE_ROLLBACKS"
-        android:protectionLevel="signature|installer" />
+        android:protectionLevel="signature|verifier" />
 
     <!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
     <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
@@ -3906,7 +3940,8 @@
     <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
         android:protectionLevel="signature|privileged|appop" />
 
-    <!-- Marker permission for applications that wish to access notification policy.
+    <!-- Marker permission for applications that wish to access notification policy. This permission
+        is not supported on managed profiles.
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
@@ -4254,6 +4289,16 @@
     <permission android:name="android.permission.MANAGE_CONTENT_CAPTURE"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to manage the content suggestions service.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
+         android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to manage the app predictions service.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_APP_PREDICTIONS"
+         android:protectionLevel="signature" />
+
     <!-- Allows an app to set the theme overlay in /vendor/overlay
          being used.
          @hide  <p>Not for use by third-party applications.</p> -->
@@ -4334,6 +4379,11 @@
     <permission android:name="android.permission.BIND_SMS_APP_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- @hide Permission that allows configuring appops.
+     <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MANAGE_APPOPS"
+                android:protectionLevel="signature" />
+
     <!-- @hide Permission that allows background clipboard access.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
@@ -4715,6 +4765,11 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.ZramWriteback"
+                 android:exported="false"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service android:name="com.android.server.backup.FullBackupJob"
                  android:exported="true"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
diff --git a/core/res/res/drawable/ic_account_circle.xml b/core/res/res/drawable/ic_account_circle.xml
index f7317db..71691ad 100644
--- a/core/res/res/drawable/ic_account_circle.xml
+++ b/core/res/res/drawable/ic_account_circle.xml
@@ -14,18 +14,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48.0dp"
-        android:height="48.0dp"
-        android:viewportWidth="20.0"
-        android:viewportHeight="20.0">
-    <group
-        android:translateX="-2"
-        android:translateY="-2" >
-        <path
-            android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"
-            android:fillColor="#FFFFFFFF" />
-        <path
-            android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"
-            android:fillColor="#FFFFFFFF" />
-    </group>
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"/>
 </vector>
diff --git a/core/res/res/drawable/ic_battery.xml b/core/res/res/drawable/ic_battery.xml
new file mode 100644
index 0000000..bd40f4d
--- /dev/null
+++ b/core/res/res/drawable/ic_battery.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 1dad977..5ab5045 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -15,22 +15,11 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="20dp"
-        android:height="20dp"
-        android:viewportWidth="48"
-        android:viewportHeight="48">
-
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
     <path
-        android:fillType="evenOdd"
-        android:strokeColor="#1A73E8"
-        android:strokeWidth="4"
-        android:pathData="M 24 2 C 36.1502644963 2 46 11.8497355037 46 24 C 46 36.1502644963 36.1502644963 46 24 46 C 11.8497355037 46 2 36.1502644963 2 24 C 2 11.8497355037 11.8497355037 2 24 2 Z" />
-    <group
-        android:translateX="10.400000"
-        android:translateY="10.400000">
-        <path
-            android:fillColor="#1A73E8"
-            android:strokeWidth="1"
-            android:pathData="M24.2971429,5.38947368 L18.9485714,5.38947368 L18.9485714,2.80902256 C18.9485714,1.37687218 17.7585143,0.228571429 16.2742857,0.228571429 L10.9257143,0.228571429 C9.44148571,0.228571429 8.25142857,1.37687218 8.25142857,2.80902256 L8.25142857,5.38947368 L2.90285714,5.38947368 C1.41862857,5.38947368 0.241942857,6.53777444 0.241942857,7.96992481 L0.228571429,22.162406 C0.228571429,23.5945564 1.41862857,24.7428571 2.90285714,24.7428571 L24.2971429,24.7428571 C25.7813714,24.7428571 26.9714286,23.5945564 26.9714286,22.162406 L26.9714286,7.96992481 C26.9714286,6.53777444 25.7813714,5.38947368 24.2971429,5.38947368 Z M13.6,17.0015038 C12.1291429,17.0015038 10.9257143,15.8403008 10.9257143,14.4210526 C10.9257143,13.0018045 12.1291429,11.8406015 13.6,11.8406015 C15.0708571,11.8406015 16.2742857,13.0018045 16.2742857,14.4210526 C16.2742857,15.8403008 15.0708571,17.0015038 13.6,17.0015038 Z M16.2742857,5.38947368 L10.9257143,5.38947368 L10.9257143,2.80902256 L16.2742857,2.80902256 L16.2742857,5.38947368 Z" />
-    </group>
+        android:fillColor="@*android:color/accent_device_default_light"
+        android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM10,4h4v2h-4V4zM20,19H4V8h16V19z"/>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/perm_group_activity_recognition.xml b/core/res/res/drawable/perm_group_activity_recognition.xml
new file mode 100644
index 0000000..0ade6c6
--- /dev/null
+++ b/core/res/res/drawable/perm_group_activity_recognition.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1
+2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9
+0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5 .1 -.8 .1 l-5.2
+2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" />
+</vector>
diff --git a/core/res/res/drawable/perm_group_sensors.xml b/core/res/res/drawable/perm_group_sensors.xml
index ce36c13..e4663d7 100644
--- a/core/res/res/drawable/perm_group_sensors.xml
+++ b/core/res/res/drawable/perm_group_sensors.xml
@@ -19,11 +19,11 @@
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
-
-    <path
-        android:fillColor="#000000"
-        android:pathData="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1
-2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9
-0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5 .1 -.8 .1 l-5.2
-2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" />
-</vector>
\ No newline at end of file
+  <path
+      android:fillColor="#000000"
+      android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3C4.42,3 2,5.42 2,
+                        8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5C22,
+                        5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1l-0.1,-0.1C7.14,14.24 4,11.39 4,8.5C4,6.5 5.5,
+                        5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5C20,
+                        11.39 16.86,14.24 12.1,18.55z"/>
+</vector>
diff --git a/core/res/res/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml
deleted file mode 100644
index be5d9b4..0000000
--- a/core/res/res/layout/notification_template_ambient_header.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<!-- hack to work around <include /> not being supported at the top level -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:paddingStart="@dimen/notification_extra_margin_ambient"
-    android:paddingEnd="@dimen/notification_extra_margin_ambient"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-    <include
-        layout="@layout/notification_template_header"
-        android:theme="@style/Theme.DeviceDefault.Notification.Ambient"/>
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
deleted file mode 100644
index 2c6064e..0000000
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:tag="ambient"
-    android:paddingStart="@dimen/notification_extra_margin_ambient"
-    android:paddingEnd="@dimen/notification_extra_margin_ambient"
-    >
-    <include layout="@layout/notification_template_ambient_header"
-             android:theme="@style/Theme.DeviceDefault.Notification.Ambient" />
-
-    <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="top"
-            android:layout_marginTop="@dimen/notification_content_margin_top"
-            android:layout_marginBottom="@dimen/notification_action_list_height"
-            android:paddingTop="4dp"
-            android:paddingBottom="6dp"
-            android:clipToPadding="false"
-            android:orientation="vertical">
-
-        <LinearLayout
-            android:id="@+id/notification_main_column"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="top"
-            android:layout_weight="1"
-            android:paddingStart="@dimen/notification_content_margin_start"
-            android:paddingEnd="@dimen/notification_content_margin_end"
-            android:clipToPadding="false"
-            android:minHeight="@dimen/notification_min_content_height"
-            android:orientation="vertical"
-            >
-            <TextView android:id="@+id/title"
-                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="top|center_horizontal"
-                android:singleLine="true"
-                android:ellipsize="marquee"
-                android:fadingEdge="horizontal"
-                android:textSize="@dimen/notification_ambient_title_text_size"
-                android:textColor="#ffffffff"
-            />
-            <TextView android:id="@+id/text"
-                android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
-                android:singleLine="false"
-                android:layout_weight="1"
-                android:gravity="top|center_horizontal"
-                android:visibility="gone"
-                android:textSize="@dimen/notification_ambient_text_size"
-                android:textColor="#eeffffff"
-                android:layout_marginTop="4dp"
-                android:ellipsize="end"
-                android:maxLines="3"
-            />
-        </LinearLayout>
-        <FrameLayout android:id="@+id/actions_container"
-                     android:layout_width="match_parent"
-                     android:layout_height="wrap_content"
-                     android:layout_gravity="bottom">
-            <com.android.internal.widget.NotificationActionListLayout
-                android:id="@+id/actions"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/notification_action_list_height"
-                android:paddingEnd="4dp"
-                android:orientation="horizontal"
-                android:gravity="center"
-                android:visibility="gone"
-            />
-        </FrameLayout>
-    </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index adb2b62..30c10b1 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -76,16 +76,14 @@
         android:layout_height="wrap_content"
         android:layout_toRightOf="@+id/minutes"
         android:layout_alignBaseline="@+id/minutes"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="0dp"
         android:orientation="vertical"
         android:baselineAlignedChildIndex="1">
         <RadioButton
             android:id="@+id/am_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingLeft="4dp"
-            android:paddingRight="4dp"
             android:paddingTop="8dp"
             android:paddingBottom="8dp"
             android:layout_marginBottom="-8dp"
@@ -101,8 +99,6 @@
             android:id="@+id/pm_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingLeft="4dp"
-            android:paddingRight="4dp"
             android:paddingTop="8dp"
             android:paddingBottom="8dp"
             android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3f23da6..c4616c5 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Maak oop met"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Maak oop met %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Maak oop"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Gee toegang tot oop <xliff:g id="HOST">%1$s</xliff:g>-skakels met"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Gee toegang tot oop <xliff:g id="HOST">%1$s</xliff:g>-skakels met <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Verleen toegang"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Redigeer met"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Redigeer met %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Wysig"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Aanvaar oproep?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Altyd"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Net een keer"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Instellings"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s steun nie werkprofiel nie"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Kleurkorreksie"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Toeganklikheidskortpad het <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Toeganklikheidskortpad het <xliff:g id="SERVICE_NAME">%1$s</xliff:g> afgeskakel"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gebruik weer toeganklikheidskortpad om die huidige toeganklikheidskenmerk te begin"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Druk en hou albei volumesleutels drie sekondes lank om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruik"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Kies \'n kenmerk om te gebruik wanneer jy op die Toeganklikheid-knoppie tik:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Raak en hou die Toeganklikheid-knoppie om kenmerke te verander."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Vergroting"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofoon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"wys tans bo-oor ander programme op jou skerm"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Roetinemodus-inligtingkennisgewing"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Battery kan afloop voordat dit normaalweg gelaai word"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Batterybespaarder is geaktiveer om batterylewe te verleng"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Laai tans"</string>
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index ca1aabd..25c23b2 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"ክፈት በ"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"ክፈት በ%1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"ክፈት"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"የ<xliff:g id="HOST">%1$s</xliff:g> አገናኞችን በዚህ ለመክፈት መዳረሻ ይስጡ፦"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"የ<xliff:g id="HOST">%1$s</xliff:g> አገናኞችን በ<xliff:g id="APPLICATION">%2$s</xliff:g> ለመክፈት መዳረሻ ይስጡ"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"መዳረሻ ስጥ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ያርትዑ በ"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"ያርትዑ በ%1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ያርትዑ"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"ጥሪ ተቀበል?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ዘወትር"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"አንዴ ብቻ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ቅንብሮች"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s የስራ መገለጫ አይደግፍም"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ጡባዊ ተኮ"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ቴሌቪዥን"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"የቀለም ማስተካከያ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"የተደራሽነት አቋራጭ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን አብርቶታል"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"የተደራሽነት አቋራጭ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን አጥፍቶታል"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"አሁን ያለውን የተደራሽነት ባህሪ ለመጀመር እንደገና የተደራሽነት አቋራጭን ይጠቀሙ"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን ለመጠቀም ለሦስት ሰከንዶች ሁለቱንም የድምፅ ቁልፎች ተጭነው ይያዙ"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"የተደራሽነት አዝራርን መታ በሚያደርጉበት ጊዜ ጥቅም ላይ የሚውለውን ባህሪ ይምረጡ፦"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ባህሪያትን ለመለወጥ የተደራሽነት አዝራሩን ይንኩ እና ይያዙት።"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ማጉላት"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"ካሜራ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"ማይክሮፎን"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"በማያዎ ላይ በሌሎች መተግበሪያዎች ላይ በማሳየት ላይ"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"በመጫን ላይ"</string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 4557563..25902bb 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1208,6 +1208,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"فتح باستخدام"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"‏فتح باستخدام %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"فتح"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"منح إمكانية الوصول لفتح روابط <xliff:g id="HOST">%1$s</xliff:g> باستخدام"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"منح إمكانية الوصول لفتح روابط <xliff:g id="HOST">%1$s</xliff:g> باستخدام <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"منح إذن الوصول"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"تعديل باستخدام"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"‏تعديل باستخدام %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"تعديل"</string>
@@ -1658,6 +1661,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"هل تريد قبول المكالمة؟"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"دومًا"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"مرة واحدة فقط"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"الإعدادات"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"‏لا يدعم %1$s الملفات الشخصية للعمل"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"الجهاز اللوحي"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"التلفزيون"</string>
@@ -1741,7 +1745,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"تصحيح الألوان"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"شغَّل اختصار إمكانية الوصول خدمة <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"أوقف اختصار إمكانية الوصول خدمة <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"استخدِم اختصار إمكانية الوصول لبدء ميزة إمكانية الوصول الحالية."</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"اضغط مع الاستمرار على مفتاحي مستوى الصوت لمدة 3 ثوانٍ لاستخدام <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"يمكنك اختيار إحدى الميزات لاستخدامها عند النقر على زر إمكانية الوصول:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"لتغيير الميزات، يمكنك لمس زر \"إمكانية الوصول\" مع الاستمرار."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"التكبير"</string>
@@ -2115,5 +2119,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"كاميرا"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"ميكروفون"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"العرض فوق التطبيقات الأخرى على شاشتك"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"إشعار معلومات \"وضع سلسلة الإجراءات\""</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"قد تنفد طاقة البطارية قبل الشحن المعتاد"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"تم تفعيل \"توفير شحن البطارية\" لإطالة عمرها."</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"جارٍ التحميل"</string>
 </resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index a4f950a..12d9677 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"ইয়াৰ জৰিয়তে খোলক"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$sৰ জৰিয়তে খোলক"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"খোলক"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ইয়াৰ দ্বাৰা <xliff:g id="HOST">%1$s</xliff:g> লিংক খুলিবলৈ এক্সেছ দিয়ক"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g>ৰ দ্বাৰা <xliff:g id="HOST">%1$s</xliff:g> লিংক খুলিবলৈ এক্সেছ দিয়ক"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"এক্সেছ দিয়ক"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ইয়াৰ দ্বাৰা সম্পাদনা কৰক"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$sৰদ্বাৰা সম্পাদনা কৰক"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"সম্পাদনা কৰক"</string>
@@ -1257,7 +1260,7 @@
     <string name="network_available_sign_in" msgid="1848877297365446605">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
     <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
     <skip />
-    <string name="wifi_no_internet" msgid="8938267198124654938">"ৱাই-ফাইত ইন্টাৰনেট নাই"</string>
+    <string name="wifi_no_internet" msgid="8938267198124654938">"ৱাই-ফাইত ইণ্টাৰনেট নাই"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"অধিক বিকল্পৰ বাবে টিপক"</string>
     <string name="wifi_softap_config_change" msgid="8475911871165857607">"আপোনাৰ হটস্পট ছেটিংসমূহত কৰা সালসলনি"</string>
     <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"আপোনাৰ হটস্পটৰ বেণ্ড সলনি কৰা হ’ল।"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"কল স্বীকাৰ কৰিবনে?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"সদায়"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"মাত্ৰ এবাৰ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ছেটিংসমূহ"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$sএ কৰ্মস্থানৰ প্ৰ\'ফাইল সমৰ্থন নকৰে।"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"টেবলেট"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"টিভি"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"ৰং শুধৰণী"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাটটোৱে <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ক অন কৰিছে"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাটটোৱে <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ক অফ কৰিছে"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"বর্তমানৰ সাধ্য সুবিধাসমূহৰ আৰম্ভ কৰিবলৈ সাধ্য সুবিধাসমূহৰ শ্বৰ্টকাট পুনৰ ব্যৱহাৰ কৰক"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবলৈ দুয়োটা ভলিউম বুটাম তিনি ছেকেণ্ডৰ বাবে হেঁচি ৰাখক"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"আপুনি দিব্যাংগসকলৰ সুবিধাৰ বুটামটো টিপিলে ব্যৱহাৰ কৰিবলগীয়া কোনো সুবিধা বাছক:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"সুবিধাসমূহ সলনি কৰিবলৈ দিব্যাংগসকলৰ সুবিধাৰ বুটামটো স্পৰ্শ কৰি থাকক।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"বিবৰ্ধন"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"কেমেৰা"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"মাইক্ৰ\'ফ\'ন"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"স্ক্ৰীণত অইন এপৰ ওপৰত দেখুৱাওক"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"ল’ড হৈ আছে"</string>
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 076ea8d..1bcce98 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Bununla açın"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ilə açın"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Açın"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Bu tətbiqlə <xliff:g id="HOST">%1$s</xliff:g> linklərini açmağa icazə verin:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> ilə <xliff:g id="HOST">%1$s</xliff:g> linklərini açmağa icazə verin"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"İcazə verin"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Bununla düzəliş edin:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ilə düzəliş edin"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Redaktə edin"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Zəngi qəbul edək?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Həmişə"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Sadəcə bir dəfə"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Ayarlar"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s iş profilini dəstəkləmir"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planşet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Rəng korreksiyası"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Əlçatımlıq Qısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini aktiv etdi"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Əlçatımlıq Qısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini deaktiv etdi"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Cari əlçatımlılıq funksiyasını yenidən başlatmaq üçün Əlçatımlılıq Qısayolundan istifadə edin"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Əlçatımlılıq düyməsinə kliklədikdə istifadə etmək üçün funksiya seçin:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Funksiyaları dəyişmək üçün Əlçatımlılıq düyməsinə basıb saxlayın."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Böyütmə"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ekrandakı digər tətbiqlərdə göstərin"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Rejim üçün məlumat bildirişi"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Batareya həmişəki vaxtdan əvvəl bitə bilər"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Enerjiyə Qənaət rejimi batareya istifadəsinin müddətini artırmaq üçün aktiv edilir"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Yüklənir"</string>
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0a94acf..57ee03d 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1148,6 +1148,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Otvorite pomoću"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Otvorite pomoću aplikacije %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Otvori"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Dozvolite da se linkovi <xliff:g id="HOST">%1$s</xliff:g> otvaraju pomoću"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Dozvolite da <xliff:g id="APPLICATION">%2$s</xliff:g> otvara linikove <xliff:g id="HOST">%1$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Dozvoli pristup"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Izmenite pomoću"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Izmenite pomoću aplikacije %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Izmeni"</string>
@@ -1589,6 +1592,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Želite li da prihvatite poziv?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Uvek"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Samo jednom"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Podešavanja"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ne podržava poslovni profil"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1669,7 +1673,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcija boja"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Upotrebite ponovo prečicu za pristupačnost da biste pokrenuli aktuelnu funkciju pristupačnosti"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izaberite funkciju koja će se koristiti kada dodirnete dugme za pristupačnost:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pritisnite i zadržite dugme za pristupačnost da biste menjali funkcije."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Uvećanje"</string>
@@ -2010,5 +2014,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"prikazuje se na ekranu dok koristite druge aplikacije"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Obaveštenje o informacijama Rutinskog režima"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Baterija će se možda isprazniti pre uobičajenog punjenja"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Ušteda baterije je aktivirana da bi se produžilo trajanje baterije"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Učitava se"</string>
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index cfe076d..77de542 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -79,7 +79,7 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Налады ідэнтыфікатару АВН па змаўчанні: не абмяжавана. Наступны выклік: не абмежавана"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Служба не прадастаўляецца."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"Вы не можаце змяніць налады ідэнтыфікатара абанента, якi тэлефануе."</string>
-    <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Мабільны інтэрнэт недаступны"</string>
+    <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Мабільная перадача даных недаступная"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="6855466023161191166">"Экстранныя выклікі недаступныя"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Няма сэрвісу галасавых выклікаў"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="8037246983606545202">"Галасавыя або экстранныя выклікі недаступныя"</string>
@@ -435,7 +435,7 @@
     <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Акрамя доступу да прыкладнага ці дакладнага месцазнаходжання праграма можа мець доступ да вызначэння геалакацыі ў фонавым рэжыме працы. На гэта патрабуецца дазвол."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"змяняць налады аудыё"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дазваляе прыкладанням змяняць глабальныя налады гуку, такія як моц і тое, што дынамік выкарыстоўваецца для выхаду."</string>
-    <string name="permlab_recordAudio" msgid="3876049771427466323">"запісваць аўдыё"</string>
+    <string name="permlab_recordAudio" msgid="3876049771427466323">"запіс аўдыя"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Гэта праграма можа у любы час запісваць аўдыя, выкарыстоўваючы мікрафон."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"адпраўляць каманды на SIM-карту"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Дазваляе праграме адпраўляць каманды SIM-карце. Гэта вельмі небяспечна."</string>
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Адкрыць з дапамогай"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Адкрыць з дапамогай %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Адкрыць"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Адкрываць спасылкі на сэрвіс <xliff:g id="HOST">%1$s</xliff:g> з дапамогай"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Адкрываць спасылкі на сэрвіс <xliff:g id="HOST">%1$s</xliff:g> з дапамогай праграмы \"<xliff:g id="APPLICATION">%2$s</xliff:g>\""</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Даць доступ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Рэдагаваць з дапамогай"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Рэдагаваць з дапамогай %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Рэдагаваць"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Прыняць выклік?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Заўсёды"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Толькі адзін раз"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Налады"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s не падтрымлівае працоўны профіль"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшэт"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ТБ"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Карэкцыя колеру"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> быў уключаны з дапамогай камбінацыі хуткага доступу для спецыяльных магчымасцей"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> быў адключаны з дапамогай камбінацыі хуткага доступу для спецыяльных магчымасцей"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Каб запусціць функцыю спецыяльных магчымасцей, паўторна скарыстайце ярлык спецыяльных магчымасцей"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Каб карыстацца сэрвісам \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце і ўтрымлівайце на працягу трох секунд абедзве клавішы гучнасці"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Выберыце функцыю для выкарыстання пры націску кнопкі \"Спецыяльныя магчымасці\":"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Каб змяняць функцыі, краніце і ўтрымлівайце кнопку \"Спецыяльныя магчымасці\"."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Павелічэнне"</string>
@@ -2045,5 +2049,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Мікрафон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"паказваецца паверх іншых праграм на экране"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Загрузка"</string>
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 8897bac..a360eae 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Отваряне чрез"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Отваряне чрез %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Отваряне"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Предоставяне на достъп за отваряне на връзките от <xliff:g id="HOST">%1$s</xliff:g> с(ъс)"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Предоставяне на достъп за отваряне на връзките от <xliff:g id="HOST">%1$s</xliff:g> с(ъс) <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Даване на достъп"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Редактиране чрез"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Редактиране чрез %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Редактиране"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Да се приеме ли обаждането?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Винаги"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Само веднъж"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Настройки"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s не поддържа служебен потребителски профил"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевизор"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Коригиране на цветовете"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Прекият път за достъпност включи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Прекият път за достъпност изключи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Използвайте отново прекия път към функцията за достъпност, за да стартирате текущата"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"За да използвате <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, натиснете двата бутона за силата на звука и ги задръжте за 3 секунди"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изберете функция, която да използвате, когато докоснете бутона за достъпност:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"За да промените функции, докоснете и задръжте бутона за достъпност."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ниво на мащаба"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Микрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"се показва върху други приложения на екрана"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Зарежда се"</string>
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 26a3ba0..ea53d19 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"এর মাধ্যমে খুলুন"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s দিয়ে খুলুন"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"খুলুন"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ব্যবহার করে <xliff:g id="HOST">%1$s</xliff:g> লিঙ্ক খুলতে অ্যাক্সেস দিন"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> ব্যবহার করে <xliff:g id="HOST">%1$s</xliff:g> লিঙ্ক খুলতে অ্যাক্সেস দিন"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"অ্যাক্সেস দিন"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"এর মাধ্যমে সম্পাদনা করুন"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s দিয়ে সম্পাদনা করুন"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"সম্পাদনা করুন"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"কল গ্রহণ করবেন?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"সবসময়"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"শুধু একবার"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"সেটিংস"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s কর্মস্থলের প্রোফাইল সমর্থন করে না।"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ট্যাবলেট"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"টিভি"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"রঙ সংশোধন"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে চালু করেছে"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে বন্ধ করেছে"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"বর্তমান অ্যাক্সেসিবিলিটি বৈশিষ্ট্য শুরু করার জন্য অ্যাক্সেসিবিলিটি শর্টকাটটি আবার ব্যবহার করুন"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যবহার করতে ভলিউম কী বোতাম ৩ সেকেন্ডের জন্য চেপে ধরে রাখুন"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"অ্যাক্সেসযোগ্যতা বোতামের সাহায্যে যে বৈশিষ্ট্যটি নিয়ন্ত্রণ করতে চান, সেটি বেছে নিন:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"বৈশিষ্ট্যগুলি পরিবর্তন করতে অ্যাক্সেসযোগ্যতা বোতামটি ট্যাপ করে ধরে রাখুন।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"বড় করে দেখা"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"ক্যামেরা"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"মাইক্রোফোন"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"স্ক্রিনে অন্যান্য অ্যাপের উপরে দেখানো হচ্ছে"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"লোড হচ্ছে"</string>
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index e336cd0..a3d2ac3 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1148,6 +1148,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Otvori koristeći"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Otvori koristeći %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Otvori"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Dozvolite pristup za otvaranje linkova hosta <xliff:g id="HOST">%1$s</xliff:g> pomoću"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Dozvolite pristup za otvaranje linkova hosta <xliff:g id="HOST">%1$s</xliff:g> pomoću aplikacije <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Dozvoli pristup"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Uredi koristeći"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Uredi koristeći %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Uredi"</string>
@@ -1591,6 +1594,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Prihvatiti poziv?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Uvijek"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Samo ovaj put"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Postavke"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ne podržava poslovni profil"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1671,7 +1675,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Ispravka boja"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Ponovo koristite Prečicu za pristupačnost da započnete trenutnu funkciju pristupačnosti"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Pritisnite obje tipke za podešavanje jačine zvuka i držite ih pritisnutim tri sekunde da koristite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Da promijenite funkcije, dodirnite i držite dugme Pristupačnost."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Uvećanje"</string>
@@ -2012,5 +2016,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"prikazivanje preko drugih aplikacija na ekranu"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Obavještenje za informacije Rutinskog načina"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Moguće je da će se baterija isprazniti prije uobičajenog punjenja"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Ušteda baterije je aktivirana da bi se produžio vijek trajanja baterije"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Učitavanje"</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 35c223b..1b1335d 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Obre amb"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Obre amb %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Obre"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Dona accés per obrir enllaços de <xliff:g id="HOST">%1$s</xliff:g> amb"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Dona accés per obrir enllaços de <xliff:g id="HOST">%1$s</xliff:g> amb <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Dona accés"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edita amb"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edita amb %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edita"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vols acceptar la trucada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Només una vegada"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Configuració"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s no admet perfils professionals."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tauleta"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televisor"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Correcció del color"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"La drecera d\'accessibilitat ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"La drecera d\'accessibilitat ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Torna a utilitzar la drecera d\'accessibilitat per iniciar la funció d\'accessibilitat actual"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Mantén premudes les dues tecles de volum durant 3 segons per fer servir <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Tria la funció que s\'utilitzarà quan toquis el botó Accessibilitat:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Per canviar les funcions, toca i mantén premut el botó Accessibilitat."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliació"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Càmera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Micròfon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"es mostra sobre altres aplicacions a la pantalla"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificació d\'informació del mode de rutina"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"És possible que la bateria s\'esgoti abans de la càrrega habitual"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"S\'ha activat l\'estalvi de bateria per allargar-ne la durada"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"S\'està carregant"</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0061ca7..6a65be3 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Otevřít v aplikaci"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Otevřít v aplikaci %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Otevřít"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Udělte přístup k otevírání odkazů <xliff:g id="HOST">%1$s</xliff:g> pomocí aplikace"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Udělte přístup k otevírání odkazů <xliff:g id="HOST">%1$s</xliff:g> pomocí aplikace <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Udělit přístup"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Upravit v aplikaci"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Upravit v aplikaci %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Upravit"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Přijmout hovor?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Vždy"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Pouze jednou"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Nastavení"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s pracovní profily nepodporuje."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televize"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Oprava barev"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Zkratka přístupnosti zapnula službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Zkratka přístupnosti vypnula službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Znovu použijte zkratku přístupnosti, čímž spustíte aktuální funkci pro usnadnění přístupu"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Chcete-li používat službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tři sekundy podržte stisknutá obě tlačítka hlasitosti"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Určete, jakou funkci aktivujete klepnutím na tlačítko Přístupnost:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Chcete-li vybrat jinou funkci, podržte tlačítko Přístupnost."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Zvětšení"</string>
@@ -2045,5 +2049,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Fotoaparát"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"zobrazení přes ostatní aplikace na obrazovce"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Informační oznámení režimu sledu činností"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Baterie se možná vybije před obvyklým časem nabití"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Byl aktivován spořič baterie za účelem prodloužení výdrže"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Načítání"</string>
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 2795efe..ae50f32 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -87,7 +87,7 @@
     <string name="NetworkPreferenceSwitchSummary" msgid="509327194863482733">"Prøv at skifte dit foretrukne netværk. Tryk for skifte."</string>
     <string name="EmergencyCallWarningTitle" msgid="813380189532491336">"Det er ikke muligt at foretage nødopkald"</string>
     <string name="EmergencyCallWarningSummary" msgid="1899692069750260619">"Det er ikke muligt at foretage nødopkald via Wi‑Fi"</string>
-    <string name="notification_channel_network_alert" msgid="4427736684338074967">"Underretninger"</string>
+    <string name="notification_channel_network_alert" msgid="4427736684338074967">"Notifikationer"</string>
     <string name="notification_channel_call_forward" msgid="2419697808481833249">"Viderestilling af opkald"</string>
     <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Nødtilbagekaldstilstand"</string>
     <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Status for mobildata"</string>
@@ -187,7 +187,7 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Din arbejdsprofil er ikke længere tilgængelig på denne enhed"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"For mange mislykkede adgangskodeforsøg"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Dette er en administreret enhed"</string>
-    <string name="network_logging_notification_text" msgid="7930089249949354026">"Din organisation administrerer denne enhed og kan overvåge netværkstrafik. Tryk for at se oplysninger."</string>
+    <string name="network_logging_notification_text" msgid="7930089249949354026">"Din organisation administrerer denne enhed og kan overvåge netværkstrafik. Tryk for at se info."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Enheden slettes"</string>
     <string name="factory_reset_message" msgid="9024647691106150160">"Administrationsappen kan ikke bruges. Enheden vil nu blive ryddet. \n\nKontakt din organisations administrator, hvis du har spørgsmål."</string>
     <string name="printing_disabled_by" msgid="8936832919072486965">"Udskrivning er deaktiveret af <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -249,7 +249,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
     <string name="global_action_lockdown" msgid="1099326950891078929">"Lukning"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
-    <string name="notification_hidden_text" msgid="6351207030447943784">"Ny underretning"</string>
+    <string name="notification_hidden_text" msgid="6351207030447943784">"Ny notifikation"</string>
     <string name="notification_channel_virtual_keyboard" msgid="6969925135507955575">"Virtuelt tastatur"</string>
     <string name="notification_channel_physical_keyboard" msgid="7297661826966861459">"Fysisk tastatur"</string>
     <string name="notification_channel_security" msgid="7345516133431326347">"Sikkerhed"</string>
@@ -262,22 +262,22 @@
     <string name="notification_channel_network_available" msgid="4531717914138179517">"Tilgængeligt netværk"</string>
     <string name="notification_channel_vpn" msgid="8330103431055860618">"VPN-status"</string>
     <string name="notification_channel_device_admin" msgid="1568154104368069249">"Enhedsadministration"</string>
-    <string name="notification_channel_alerts" msgid="4496839309318519037">"Underretninger"</string>
+    <string name="notification_channel_alerts" msgid="4496839309318519037">"Notifikationer"</string>
     <string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo til udstilling i butik"</string>
     <string name="notification_channel_usb" msgid="9006850475328924681">"USB-forbindelse"</string>
     <string name="notification_channel_heavy_weight_app" msgid="6218742927792852607">"Appen kører"</string>
     <string name="notification_channel_foreground_service" msgid="3931987440602669158">"Apps, der bruger batteri"</string>
     <string name="foreground_service_app_in_background" msgid="1060198778219731292">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruger batteri"</string>
     <string name="foreground_service_apps_in_background" msgid="7175032677643332242">"<xliff:g id="NUMBER">%1$d</xliff:g> apps bruger batteri"</string>
-    <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tryk for at se oplysninger om batteri- og dataforbrug"</string>
+    <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tryk for at se info om batteri- og dataforbrug"</string>
     <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
     <string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
     <string name="user_owner_label" msgid="8836124313744349203">"Skift til personlig profil"</string>
     <string name="managed_profile_label" msgid="8947929265267690522">"Skift til arbejdsprofil"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
-    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"have adgang til dine kontaktpersoner"</string>
-    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til dine kontaktpersoner?"</string>
+    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"have adgang til dine kontakter"</string>
+    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til dine kontakter?"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Placering"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"få adgang til enhedens placering"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til enhedens placering?"</string>
@@ -393,11 +393,11 @@
     <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Tillader, at appen kan sende klæbende udsendelser, der forbliver tilbage, når udsendelsen er slut. Overdreven brug kan gøre din tablet langsom eller ustabil ved at tvinge den til at bruge for meget hukommelse."</string>
     <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Giver appen lov til at sende klæbende udsendelser, som ikke forsvinder, når udsendelsen er slut. Overdreven brug kan gøre fjernsynet langsomt eller ustabilt ved at få det til at bruge for meget hukommelse."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Tillader, at appen kan sende klæbende udsendelser, der forbliver tilbage, når udsendelsen er slut. Overdreven brug kan gøre din telefon langsom eller ustabil ved at tvinge den til at bruge for meget hukommelse."</string>
-    <string name="permlab_readContacts" msgid="8348481131899886131">"læse dine kontaktpersoner"</string>
+    <string name="permlab_readContacts" msgid="8348481131899886131">"læse dine kontakter"</string>
     <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillader, at appen kan læse data om de kontakter, der er gemt på din tablet, f.eks. hvor ofte du har ringet til, sendt mail til eller på anden måde kommunikeret med bestemte personer. Med denne tilladelse kan apps gemme dine kontaktdata, og skadelige apps kan dele kontaktdata uden din viden."</string>
     <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Giver appen lov til at læse data om dine kontakter, der er gemt på dit tv, herunder hvor ofte du har ringet, mailet eller på andre måder kommunikeret med bestemte personer. Denne tilladelse gør det muligt for apps at gemme dine kontaktoplysninger, og ondsindede apps kan dele kontaktoplysninger uden din viden."</string>
     <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillader, at appen kan læse data om de kontakter, der er gemt på din telefon, f.eks. hvor ofte du har ringet til, sendt mail til eller på anden måde kommunikeret med bestemte personer. Med denne tilladelse kan apps gemme dine kontaktdata, og skadelige apps kan dele kontaktdata uden din viden."</string>
-    <string name="permlab_writeContacts" msgid="5107492086416793544">"ændre dine kontaktpersoner"</string>
+    <string name="permlab_writeContacts" msgid="5107492086416793544">"ændre dine kontakter"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillader, at appen kan ændre data om de kontakter, der er gemt på din tablet, f.eks. hvor ofte du har ringet til dem, sendt dem en mail eller på anden måde kommunikeret med bestemte kontakter. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
     <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Giver appen lov til at ændre data om dine kontakter, der er gemt på dit tv, herunder hvor ofte du har ringet, mailet eller på anden måde kommunikeret med bestemte kontakter. Denne tilladelse gør det muligt for apps at slette kontaktoplysninger."</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillader, at appen kan ændre data om de kontakter, der er gemt på din telefon, f.eks. hvor ofte du har ringet til dem, sendt en mail til dem eller på anden måde kommunikeret med bestemte kontakter. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
@@ -624,10 +624,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Tillader, at appen kan administrere netværkspolitikker og definere appspecifikke regler."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"skift afregning af netværksbrug"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Tillader, at appen kan ændre den måde, som netværksforbrug udregnes på i forhold til apps. Anvendes ikke af normale apps."</string>
-    <string name="permlab_accessNotifications" msgid="7673416487873432268">"adgang til underretninger"</string>
-    <string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillader, at appen kan hente, undersøge og rydde underretninger, f.eks. dem, der er sendt af andre apps."</string>
-    <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"forpligte sig til en underretningslyttertjeneste"</string>
-    <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Tillader brugeren at forpligte sig til en underretningslyttertjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
+    <string name="permlab_accessNotifications" msgid="7673416487873432268">"adgang til notifikationer"</string>
+    <string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillader, at appen kan hente, undersøge og rydde notifikationer, f.eks. dem, der er sendt af andre apps."</string>
+    <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"forpligte sig til en notifikationslyttertjeneste"</string>
+    <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Tillader brugeren at forpligte sig til en notifikationslyttertjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
     <string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"oprette binding til en tjeneste til formidling af betingelser"</string>
     <string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Tillader, at brugeren opretter en binding til det øverste niveau af grænsefladen i en tjeneste til formidling af betingelser. Dette bør aldrig være nødvendigt for almindelige apps."</string>
     <string name="permlab_bindDreamService" msgid="4153646965978563462">"fastlås til en drømmetjeneste"</string>
@@ -1098,7 +1098,7 @@
     <string name="sms" msgid="4560537514610063430">"Send besked"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Send en besked til det valgte telefonnummer"</string>
     <string name="add_contact" msgid="7867066569670597203">"Tilføj"</string>
-    <string name="add_contact_desc" msgid="4830217847004590345">"Føj til kontaktpersoner"</string>
+    <string name="add_contact_desc" msgid="4830217847004590345">"Føj til kontakter"</string>
     <string name="view_calendar" msgid="979609872939597838">"Se"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Se det valgte tidspunkt i kalenderen"</string>
     <string name="add_calendar_event" msgid="1953664627192056206">"Planlæg"</string>
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Åbn med"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Åbn med %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Åbn"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Giv adgang til at åbne <xliff:g id="HOST">%1$s</xliff:g>-link med"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Giv adgang til at åbne <xliff:g id="HOST">%1$s</xliff:g>-links med <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Giv adgang"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Rediger med"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Rediger med %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Rediger"</string>
@@ -1214,19 +1217,19 @@
     <string name="volume_call" msgid="3941680041282788711">"Lydstyrke for opkald"</string>
     <string name="volume_bluetooth_call" msgid="2002891926351151534">"Lydstyrke for Bluetooth under opkald"</string>
     <string name="volume_alarm" msgid="1985191616042689100">"Lydstyrke for alarm"</string>
-    <string name="volume_notification" msgid="2422265656744276715">"Lydstyrke for underretninger"</string>
+    <string name="volume_notification" msgid="2422265656744276715">"Lydstyrke for notifikationer"</string>
     <string name="volume_unknown" msgid="1400219669770445902">"Lydstyrke"</string>
     <string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Lydstyrke for bluetooth"</string>
     <string name="volume_icon_description_ringer" msgid="3326003847006162496">"Lydstyrke for ringetone"</string>
     <string name="volume_icon_description_incall" msgid="8890073218154543397">"Lydstyrke for opkald"</string>
     <string name="volume_icon_description_media" msgid="4217311719665194215">"Lydstyrke for medier"</string>
-    <string name="volume_icon_description_notification" msgid="7044986546477282274">"Lydstyrke for underretninger"</string>
+    <string name="volume_icon_description_notification" msgid="7044986546477282274">"Lydstyrke for notifikationer"</string>
     <string name="ringtone_default" msgid="3789758980357696936">"Standardringetone"</string>
     <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Standard (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
     <string name="ringtone_silent" msgid="7937634392408977062">"Ingen"</string>
     <string name="ringtone_picker_title" msgid="3515143939175119094">"Ringetoner"</string>
     <string name="ringtone_picker_title_alarm" msgid="6473325356070549702">"Alarmlyde"</string>
-    <string name="ringtone_picker_title_notification" msgid="4837740874822788802">"Underretningslyde"</string>
+    <string name="ringtone_picker_title_notification" msgid="4837740874822788802">"Notifikationslyde"</string>
     <string name="ringtone_unknown" msgid="3914515995813061520">"Ukendt"</string>
     <plurals name="wifi_available" formatted="false" msgid="7900333017752027322">
       <item quantity="one">Tilgængelige Wi-Fi-netværk</item>
@@ -1436,10 +1439,10 @@
     <string name="accessibility_binding_label" msgid="4148120742096474641">"Hjælpefunktioner"</string>
     <string name="wallpaper_binding_label" msgid="1240087844304687662">"Baggrund"</string>
     <string name="chooser_wallpaper" msgid="7873476199295190279">"Skift baggrund"</string>
-    <string name="notification_listener_binding_label" msgid="2014162835481906429">"Underretningslytter"</string>
+    <string name="notification_listener_binding_label" msgid="2014162835481906429">"Notifikationslytter"</string>
     <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR-lyttefunktion"</string>
     <string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Tjeneste til formidling af betingelser"</string>
-    <string name="notification_ranker_binding_label" msgid="774540592299064747">"Tjeneste til rangering af underretninger"</string>
+    <string name="notification_ranker_binding_label" msgid="774540592299064747">"Tjeneste til rangering af notifikationer"</string>
     <string name="vpn_title" msgid="19615213552042827">"VPN er aktiveret."</string>
     <string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="vpn_text" msgid="1610714069627824309">"Tryk for at administrere netværket."</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vil du besvare opkaldet?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Altid"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Kun én gang"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Indstillinger"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s understøtter ikke arbejdsprofil"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tv"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Korriger farve"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Genvejen til hjælpefunktioner aktiverede <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Genvejen til hjælpefunktioner deaktiverede <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Brug Genvej til hjælpefunktioner for at starte den aktuelle hjælpefunktion"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Hold begge lydstyrkeknapper nede i tre sekunder for at bruge <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Vælg, hvilken funktion du vil bruge, når du trykker på knappen Hjælpefunktioner:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Tryk på knappen Hjælpefunktioner, og hold fingeren nede for at skifte funktioner."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Forstørrelse"</string>
@@ -1858,7 +1862,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valgt</item>
     </plurals>
     <string name="default_notification_channel_label" msgid="5929663562028088222">"Uden kategori"</string>
-    <string name="importance_from_user" msgid="7318955817386549931">"Du angiver, hvor vigtige disse underretninger er."</string>
+    <string name="importance_from_user" msgid="7318955817386549931">"Du angiver, hvor vigtige disse notifikationer er."</string>
     <string name="importance_from_person" msgid="9160133597262938296">"Dette er vigtigt på grund af de personer, det handler om."</string>
     <string name="user_creation_account_exists" msgid="1942606193570143289">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en ny bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
     <string name="user_creation_adding" msgid="4482658054622099197">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en ny bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g> (der findes allerede en bruger med denne konto)?"</string>
@@ -1873,7 +1877,7 @@
     <string name="app_suspended_default_message" msgid="123166680425711887">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er ikke tilgængelig lige nu. Dette administreres af <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
     <string name="app_suspended_more_details" msgid="1131804827776778187">"Få flere oplysninger"</string>
     <string name="work_mode_off_title" msgid="1118691887588435530">"Skal arbejdsprofilen slås til?"</string>
-    <string name="work_mode_off_message" msgid="5130856710614337649">"Dine arbejdsapps, underretninger, data og andre funktioner til din arbejdsprofil deaktiveres"</string>
+    <string name="work_mode_off_message" msgid="5130856710614337649">"Dine arbejdsapps, notifikationer, data og andre funktioner til din arbejdsprofil deaktiveres"</string>
     <string name="work_mode_turn_on" msgid="2062544985670564875">"Slå til"</string>
     <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Denne app er lavet til en ældre version af Android og fungerer muligvis ikke korrekt. Prøv at søge efter opdateringer, eller kontakt udvikleren."</string>
     <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Søg efter opdatering"</string>
@@ -1962,11 +1966,11 @@
     <string name="harmful_app_warning_title" msgid="8982527462829423432">"Der er registreret en skadelig app"</string>
     <string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> anmoder om tilladelse til at vise eksempler fra <xliff:g id="APP_2">%2$s</xliff:g>"</string>
     <string name="screenshot_edit" msgid="7867478911006447565">"Rediger"</string>
-    <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"Telefonen vil vibrere ved opkald og underretninger"</string>
-    <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"Der afspilles ikke lyd ved opkald og underretninger"</string>
+    <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"Telefonen vil vibrere ved opkald og notifikationer"</string>
+    <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"Der afspilles ikke lyd ved opkald og notifikationer"</string>
     <string name="notification_channel_system_changes" msgid="5072715579030948646">"Systemændringer"</string>
     <string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"Forstyr ikke"</string>
-    <string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"Nyhed! Forstyr ikke skjuler underretninger"</string>
+    <string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"Nyhed! Forstyr ikke skjuler notifikationer"</string>
     <string name="zen_upgrade_notification_visd_content" msgid="5533674060311631165">"Tryk for at få flere oplysninger og foretage ændringer."</string>
     <string name="zen_upgrade_notification_title" msgid="3799603322910377294">"Tilstanden Forstyr ikke blev ændret"</string>
     <string name="zen_upgrade_notification_content" msgid="1794994264692424562">"Tryk for at se, hvad der er blokeret."</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"vises over andre apps på din skærm"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notifikation med oplysninger om rutinetilstand"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Enheden løber muligvis tør for batteri, inden du normalt oplader den"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Batterisparefunktion er aktiveret for at forlænge batteritiden"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Indlæser"</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 6087438..895eaa1 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Öffnen mit"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Mit %1$s öffnen"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Öffnen"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Zugriff zum Öffnen von <xliff:g id="HOST">%1$s</xliff:g>-Links erlauben"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Zugriff zum Öffnen von <xliff:g id="HOST">%1$s</xliff:g>-Links mit <xliff:g id="APPLICATION">%2$s</xliff:g> erlauben"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Zugriff erlauben"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Bearbeiten mit"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Mit %1$s bearbeiten"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Bearbeiten"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Anruf annehmen?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Immer"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Nur diesmal"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Einstellungen"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"Das Arbeitsprofil wird von %1$s nicht unterstützt."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Farbkorrektur"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde durch die Bedienungshilfenverknüpfung aktiviert"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde durch die Bedienungshilfenverknüpfung deaktiviert"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Verwende noch einmal die Verknüpfung für Bedienungshilfen, um die aktuelle Bedienungshilfe zu starten."</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Halten Sie beide Lautstärketasten drei Sekunden lang gedrückt, um <xliff:g id="SERVICE_NAME">%1$s</xliff:g> zu verwenden"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Wähle eine Funktion aus, die verwendet wird, wenn du auf die Schaltfläche für die Bedienungshilfen tippst:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Um die Funktionen zu ändern, halte die Schaltfläche für die Bedienungshilfen gedrückt."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Vergrößerung"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"wird über anderen Apps auf dem Bildschirm angezeigt"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Infomitteilung zum Ablaufmodus"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Wird geladen"</string>
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index f899309..441435b 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Άνοιγμα με"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Άνοιγμα με %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Άνοιγμα"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Παραχώρηση πρόσβασης για το άνοιγμα συνδέσμων <xliff:g id="HOST">%1$s</xliff:g> με"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Παραχώρηση πρόσβασης για το άνοιγμα συνδέσμων <xliff:g id="HOST">%1$s</xliff:g> με την εφαρμογή <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Παροχή πρόσβασης"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Επεξεργασία με"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Επεξεργασία με %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Επεξεργασία"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Αποδοχή κλήσης;"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Πάντα"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Μόνο μία φορά"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Ρυθμίσεις"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"Το προφίλ εργασίας δεν υποστηρίζεται από %1$s"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Τηλεόραση"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Διόρθωση χρωμάτων"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Η συντόμευση προσβασιμότητας ενεργοποίησε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Η συντόμευση προσβασιμότητας απενεργοποίησε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Χρησιμοποιήστε τη Συντόμευση προσβασιμότητας ξανά, για να ξεκινήσετε την τρέχουσα λειτουργία προσβασιμότητας"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Πατήστε παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα, ώστε να χρησιμοποιήσετε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Επιλέξτε μια λειτουργία που θα χρησιμοποιείται κατά το πάτημα του κουμπιού \"Προσβασιμότητα\"."</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Για να αλλάξετε λειτουργίες, αγγίξτε παρατεταμένα το κουμπί \"Προσβασιμότητα\"."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Μεγιστοποίηση"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Κάμερα"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Μικρόφωνο"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"εμφανίζεται πάνω σε άλλες εφαρμογές στην οθόνη σας"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Ειδοποίηση πληροφοριών λειτουργίας Ρουτίνας"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Η μπαταρία μπορεί να εξαντληθεί πριν από τη συνηθισμένη φόρτιση"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Η Εξοικονόμηση μπαταρίας ενεργοποιήθηκε για την επέκταση της διάρκειας ζωής της μπαταρίας"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Φόρτωση"</string>
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7288a80..8b49cf1 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Open with"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Open with %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Open"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Give access"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edit with"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edit with %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Accept call?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Always"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Just once"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Settings"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s doesn\'t support work profile"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch &amp; hold the Accessibility button."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Camera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microphone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"displaying over other apps on your screen"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Loading"</string>
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 0481755..888bc26 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Open with"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Open with %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Open"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Give access"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edit with"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edit with %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Accept call?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Always"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Just once"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Settings"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s doesn\'t support work profile"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch &amp; hold the Accessibility button."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Camera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microphone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"displaying over other apps on your screen"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Loading"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7288a80..8b49cf1 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Open with"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Open with %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Open"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Give access"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edit with"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edit with %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Accept call?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Always"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Just once"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Settings"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s doesn\'t support work profile"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch &amp; hold the Accessibility button."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Camera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microphone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"displaying over other apps on your screen"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Loading"</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7288a80..8b49cf1 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Open with"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Open with %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Open"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Give access to open <xliff:g id="HOST">%1$s</xliff:g> links with <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Give access"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edit with"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edit with %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Accept call?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Always"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Just once"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Settings"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s doesn\'t support work profile"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch &amp; hold the Accessibility button."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Camera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microphone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"displaying over other apps on your screen"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Loading"</string>
 </resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 5d3b466..b4f1156d0 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1126,6 +1126,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎Open with‎‏‎‎‏‎"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎Open with %1$s‎‏‎‎‏‎"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‎Open‎‏‎‎‏‎"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎Give access to open ‎‏‎‎‏‏‎<xliff:g id="HOST">%1$s</xliff:g>‎‏‎‎‏‏‏‎ links with‎‏‎‎‏‎"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎Give access to open ‎‏‎‎‏‏‎<xliff:g id="HOST">%1$s</xliff:g>‎‏‎‎‏‏‏‎ links with ‎‏‎‎‏‏‎<xliff:g id="APPLICATION">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎Give access‎‏‎‎‏‎"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‏‏‎Edit with‎‏‎‎‏‎"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎Edit with %1$s‎‏‎‎‏‎"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎Edit‎‏‎‎‏‎"</string>
@@ -1564,6 +1567,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎Accept call?‎‏‎‎‏‎"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‎Always‎‏‎‎‏‎"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎Just once‎‏‎‎‏‎"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎Settings‎‏‎‎‏‎"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎%1$s doesn\'t support work profile‎‏‎‎‏‎"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎Tablet‎‏‎‎‏‎"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎TV‎‏‎‎‏‎"</string>
@@ -1643,7 +1647,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎Color Correction‎‏‎‎‏‎"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎Accessibility Shortcut turned ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ on‎‏‎‎‏‎"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎Accessibility Shortcut turned ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ off‎‏‎‎‏‎"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎Use Accessibility Shortcut again to start the current accessibility feature‎‏‎‎‏‎"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎Press and hold both volume keys for three seconds to use ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎Choose a feature to use when you tap the Accessibility button:‎‏‎‎‏‎"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎To change features, touch &amp; hold the Accessibility button.‎‏‎‎‏‎"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎Magnification‎‏‎‎‏‎"</string>
@@ -1973,5 +1977,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎Camera‎‏‎‎‏‎"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎Microphone‎‏‎‎‏‎"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎displaying over other apps on your screen‎‏‎‎‏‎"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎Routine Mode info notification‎‏‎‎‏‎"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‏‎Battery may run out before usual charge‎‏‎‎‏‎"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎Battery Saver activated to extend battery life‎‏‎‎‏‎"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎Loading‎‏‎‎‏‎"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index d369da5..e821fca 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Abrir con"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Abrir con %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Abrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Otorgar acceso para abrir vínculos de <xliff:g id="HOST">%1$s</xliff:g> con"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Otorgar acceso para abrir vínculos de <xliff:g id="HOST">%1$s</xliff:g> con <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Otorgar acceso"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editar con"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editar con %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Editar"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"¿Aceptar la llamada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Siempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Solo una vez"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Configuración"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s no admite perfiles de trabajo."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de color"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"El acceso directo de accesibilidad activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"El acceso directo de accesibilidad desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Vuelve a usar el acceso directo de accesibilidad para iniciar la función de accesibilidad actual"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Elige una función para usar cuando presionas el botón Accesibilidad:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para cambiar funciones, mantén presionado el botón Accesibilidad."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliación"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Cámara"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Micrófono"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"se superpone a otras apps en tu pantalla"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificación de información del modo de Rutinas"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Es posible que la batería se agote antes de la carga habitual"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Se activó el Ahorro de batería para extender la duración de la batería"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Cargando"</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9174248..3f93565 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Abrir con"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Abrir con %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Abrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Permitir acceso para abrir enlaces de <xliff:g id="HOST">%1$s</xliff:g> con"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Permitir acceso para abrir enlaces de <xliff:g id="HOST">%1$s</xliff:g> con <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Permitir acceso"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editar con"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editar con %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Cambiar"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"¿Aceptar la llamada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Siempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Solo una vez"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Ajustes"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s no admite perfiles de trabajo"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de color"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"El acceso directo a accesibilidad ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"El acceso directo a accesibilidad ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Usa de nuevo la combinación de teclas de accesibilidad para iniciar la función de accesibilidad actual"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Selecciona la función que se utilizará cuando toques el botón Accesibilidad:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para cambiar las funciones, mantén pulsado el botón Accesibilidad."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliar"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Cámara"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Micrófono"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"se muestra sobre otras aplicaciones que haya en la pantalla"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificación sobre el modo rutina"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Es posible que te quedes sin batería antes de lo habitual"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Se ha activado el ahorro de batería para aumentar la duración de la batería"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Cargando"</string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index f20b35f..466ead0 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Avamine:"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Avamine rakendusega %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ava"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Juurdepääsu andmine, et avada üksuse <xliff:g id="HOST">%1$s</xliff:g> lingid"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Juurdepääsu andmine, et avada üksuse <xliff:g id="HOST">%1$s</xliff:g> lingid rakendusega <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Juudep. andmine"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Muutmine:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Muutmine rakendusega %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Muuda"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Kas vastata kõnele?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Alati"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Ainult üks kord"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Seaded"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ei toeta tööprofiili"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tahvelarvuti"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Teler"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Värviparandus"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Juurdepääsetavuse otsetee lülitas teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> sisse"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Juurdepääsetavuse otsetee lülitas teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> välja"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Praeguse juurdepääsufunktsiooni käivitamiseks kasutage juurdpääsetavuse otseteed uuesti"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Valige, millist funktsiooni kasutada, kui vajutate nuppu Juurdepääsetavus:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Funktsioonide muutmiseks puudutage pikalt nuppu Juurdepääsetavus."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Suurendus"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kaamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"teie ekraanil muude rakenduste peal kuvamine"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Laadimine"</string>
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 153eeae..c478bab 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Ireki honekin:"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Irekin %1$s aplikazioarekin"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ireki"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Eman <xliff:g id="HOST">%1$s</xliff:g> estekak irekitzeko baimena aplikazio honi:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Eman <xliff:g id="APPLICATION">%2$s</xliff:g> aplikazioari <xliff:g id="HOST">%1$s</xliff:g> irekitzeko baimena"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Eman sarbidea"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editatu honekin:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editatu %1$s aplikazioarekin"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Editatu"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Deia onartu nahi duzu?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Beti"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Behin soilik"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Ezarpenak"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s abiarazleak ez du laneko profil hau onartzen"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tableta"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Telebista"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Koloreen zuzenketa"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Erabilerraztasun-lasterbideak <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu du"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Erabilerraztasun-lasterbideak <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu du"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Uneko erabilerraztasun-eginbidea abiarazteko, erabili erabilerraztasun-lasterbidea berriro"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> erabiltzeko, eduki sakatuta bolumen-tekla biak hiru segundoz"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoia sakatzean:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Eginbideak aldatzeko, eduki sakatuta Erabilerraztasuna botoia."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Lupa"</string>
@@ -1976,5 +1980,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofonoa"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"pantailako beste aplikazioen gainean bistaratzen"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Ohitura moduaren informazio-jakinarazpena"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Baliteke bateria ohi baino lehenago agortzea"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Bateria-aurrezlea aktibatuta dago bateriaren iraupena luzatzeko"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Kargatzen"</string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 76845c8..c38f1d2 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"باز کردن با"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"‏باز کردن با %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"باز کردن"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ارائه دسترسی برای باز کردن پیوندهای <xliff:g id="HOST">%1$s</xliff:g> با"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"ارائه دسترسی برای باز کردن پیوندهای <xliff:g id="HOST">%1$s</xliff:g> با<xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ارائه دسترسی"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ویرایش با"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"‏ویرایش با %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ویرایش"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"تماس را می‌پذیرید؟"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"همیشه"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"فقط این بار"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"تنظیمات"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"‏%1$s از نمایه کاری پشتیبانی نمی‌کند"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"رایانهٔ لوحی"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"تلویزیون"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"تصحیح رنگ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"«میان‌بر دسترس‌پذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را روشن کرد"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"«میان‌بر دسترس‌پذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را خاموش کرد"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"برای راه‌اندازی ویژگی دسترس‌پذیری کنونی، دوباره از «میان‌بر دسترس‌پذیری» استفاده کنید"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"برای استفاده از <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید صدا را فشار دهید و سه ثانیه نگه دارید"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"قابلیتی را انتخاب کنید که هنگام ضربه زدن روی دکمه «دسترس‌پذیری» استفاده می‌شود:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"برای تغییر دادن قابلیت‌ها، دکمه «دسترس‌پذیری» را لمس کنید و نگه‌دارید."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"درشت‌نمایی"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"دوربین"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"میکروفون"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"نمایش روی برنامه‌های دیگر در صفحه‌نمایش"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"درحال بارگیری"</string>
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 16c96cc..ac6d8dc 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Avaa sovelluksessa"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Avaa sovelluksessa %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Avaa"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Salli linkkien (<xliff:g id="HOST">%1$s</xliff:g>) avaaminen:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Salli linkkien (<xliff:g id="HOST">%1$s</xliff:g>) avaaminen: <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Salli"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Muokkaa sovelluksessa"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Muokkaa sovelluksessa %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Muokkaa"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vastataanko puheluun?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Aina"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Vain kerran"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Asetukset"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ei tue työprofiilia"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletti"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televisio"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Värinkorjaus"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> otettiin käyttöön esteettömyystilan pikanäppäimellä."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> poistettiin käytöstä esteettömyystilan pikanäppäimellä."</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Valitse esteettömyystilan oikopolku uudelleen käynnistääksesi esteettömyysominaisuuden"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Voit käyttää palvelua <xliff:g id="SERVICE_NAME">%1$s</xliff:g> painamalla molempia äänenvoimakkuuspainikkeita kolmen sekunnin ajan"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Valitse toiminto, jonka Esteettömyys-painike aktivoi:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Jos haluat muokata ominaisuuksia, kosketa Esteettömyys-painiketta pitkään."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Suurennus"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofoni"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"näkyy näytöllä muiden sovellusten päällä"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Ladataan"</string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 6fb1f12..8383078 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Ouvrir avec"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Ouvrir avec %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ouvrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Accorder l\'accès pour ouvrir les liens de <xliff:g id="HOST">%1$s</xliff:g> avec"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Accorder l\'accès pour ouvrir les liens de <xliff:g id="HOST">%1$s</xliff:g> avec <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Accorder l\'accès"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Modifier avec"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Modifier avec %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Modifier"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Prendre l\'appel?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Toujours"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Une seule fois"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Paramètres"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ne prend pas en charge le profil professionnel"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablette"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Télévision"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Correction des couleurs"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Le raccourci d\'accessibilité a activé la fonction <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Le raccourci d\'accessibilité a désactivé la fonction <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Utilisez à nouveau le raccourci d\'accessibilité pour démarrer la fonctionnalité d\'accessibilité actuelle"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choisissez une fonctionnalité à utiliser lorsque vous touchez le bouton d\'accessibilité :"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pour changer des fonctionnalités, maintenez le doigt sur le bouton d\'accessibilité."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Zoom"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Appareil photo"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microphone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"superpose du contenu par-dessus d\'autres applications à l\'écran"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Chargement en cours…"</string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index fa8c7f0..3e87d88 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Ouvrir avec"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Ouvrir avec %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ouvrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Autoriser l\'ouverture des liens <xliff:g id="HOST">%1$s</xliff:g> avec"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Autoriser l\'ouverture des liens <xliff:g id="HOST">%1$s</xliff:g> avec <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Accorder l\'accès"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Modifier avec"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Modifier avec %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Modifier"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Prendre l\'appel ?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Toujours"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Une seule fois"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Paramètres"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s n\'est pas compatible avec le profil professionnel."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablette"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Téléviseur"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Correction des couleurs"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Le raccourci d\'accessibilité a activé <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Le raccourci d\'accessibilité a désactivé <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Utilisez à nouveau le raccourci d\'accessibilité pour démarrer la fonctionnalité d\'accessibilité actuelle"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Appuyez de manière prolongée sur les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choisissez une fonctionnalité à utiliser lorsque vous appuyez sur le bouton d\'accessibilité :"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pour changer des fonctionnalités, appuyez de manière prolongée sur le bouton d\'accessibilité."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Agrandissement"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Caméra"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Micro"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"se superpose aux autres applications sur l\'écran"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notification d\'information du mode Routine"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Vous risquez d\'être à court de batterie plus tôt que prévu"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Économiseur de batterie activé pour prolonger l\'autonomie"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Chargement…"</string>
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 2c9b09d..1e78401 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -32,7 +32,7 @@
     <string name="unknownName" msgid="6867811765370350269">"Descoñecido"</string>
     <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Correo de voz"</string>
     <string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
-    <string name="mmiError" msgid="5154499457739052907">"Problema de conexión ou código MMI non válido."</string>
+    <string name="mmiError" msgid="5154499457739052907">"Problema de conexión ou código MMI non-válido."</string>
     <string name="mmiFdnError" msgid="5224398216385316471">"A operación está restrinxida a números de marcación fixa."</string>
     <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"Non se pode cambiar a configuración do desvío de chamadas desde o teléfono mentres estás en itinerancia."</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"Activouse o servizo."</string>
@@ -150,7 +150,7 @@
     <string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: non desviada"</string>
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: non reenviada"</string>
     <string name="fcComplete" msgid="3118848230966886575">"Código de función completo"</string>
-    <string name="fcError" msgid="3327560126588500777">"Problema de conexión ou código de función non válido"</string>
+    <string name="fcError" msgid="3327560126588500777">"Problema de conexión ou código de función non-válido"</string>
     <string name="httpErrorOk" msgid="1191919378083472204">"Aceptar"</string>
     <string name="httpError" msgid="7956392511146698522">"Produciuse un erro na rede."</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"Non se puido atopar o URL."</string>
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Abrir con"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Abrir con %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Abrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Conceder acceso para abrir ligazóns de <xliff:g id="HOST">%1$s</xliff:g> con"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Conceder acceso para abrir ligazóns de <xliff:g id="HOST">%1$s</xliff:g> con <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Conceder acceso"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editar con"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editar con %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Editar"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Aceptar chamada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Só unha vez"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Configuración"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s non admite o perfil de traballo"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tableta"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televisión"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de cor"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atallo de accesibilidade activou <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atallo de accesibilidade desactivou <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Para iniciar a función de accesibilidade actual, utiliza de novo o atallo de accesibilidade"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Mantén premidas as teclas do volume durante tres segudos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolle que función queres utilizar cando toques o botón Accesibilidade:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para cambiar as funcións, mantén premido o botón Accesibilidade."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliación"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Cámara"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Micrófono"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"mostrando outras aplicacións na pantalla"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Cargando"</string>
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index da8a6e5..958183d 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"આની સાથે ખોલો"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s સાથે ખોલો"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"ખોલો"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"આના વડે <xliff:g id="HOST">%1$s</xliff:g>ની લિંક ખોલવા માટે ઍક્સેસ આપો"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> વડે <xliff:g id="HOST">%1$s</xliff:g>ની લિંક ખોલવા માટે ઍક્સેસ આપો"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ઍક્સેસ આપો"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"આનાથી સંપાદિત કરો"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s સાથે સંપાદિત કરો"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"સંપાદિત કરો"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"કૉલ સ્વીકારીએ?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"હંમેશા"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"ફક્ત એક વાર"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"સેટિંગ"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s કાર્ય પ્રોફાઇલનું સમર્થન કરતું નથી"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ટેબ્લેટ"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"રંગ સુધારણા"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ઍક્સેસિબિલિટી શૉર્ટકટે <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ઍક્સેસિબિલિટી શૉર્ટકટે <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"વર્તમાન ઍક્સેસિબિલિટી સુવિધાને શરૂ કરવા માટે ફરીથી ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરો"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>નો ઉપયોગ કરવા માટે બન્ને વૉલ્યૂમ કીને ત્રણ સેકન્ડ સુધી દબાવી રાખો"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"જ્યારે તમે ઍક્સેસિબિલિટી બટન પર ટૅપ કરો, ત્યારે ઉપયોગ કરવાની સુવિધા પસંદ કરો:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"સુવિધાઓ બદલવા માટે, ઍક્સેસિબિલિટી બટન દબાવી રાખો."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"વિસ્તૃતીકરણ"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"કૅમેરા"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"માઇક્રોફોન"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"આ તમારી સ્ક્રીન પર અન્ય ઍપની ઉપર દેખાશે"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"લોડિંગ"</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 4349c09..6a247af 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"इसमें खोलें"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s में खोलें"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"खोलें"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"इससे <xliff:g id="HOST">%1$s</xliff:g> लिंक खोलने का एक्सेस दें"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> से <xliff:g id="HOST">%1$s</xliff:g> लिंक खोलने का एक्सेस दें"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"एक्सेस दें"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"इसके ज़रिये बदलाव करें"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s की मदद से बदलाव करें"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"बदलाव करें"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"कॉल स्वीकार करें?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"हमेशा"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"केवल एक बार"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"सेटिंग"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s कार्य प्रोफ़ाइल का समर्थन नहीं करता"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"टैबलेट"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"टीवी"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"रंग में सुधार करने की सुविधा"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"सुलभता शॉर्टकट ने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू किया"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"सुलभता शॉर्टकट ने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद किया"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"मौजूदा सुलभता सुविधा शुरू करने के लिए \'सुलभता शॉर्टकट\' का फिर से इस्तेमाल करें"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> इस्तेमाल करने के लिए आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"सुलभता बटन पर टैप करते समय इस्तेमाल की जाने वाली सुविधा चुनें:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"सुविधाओं में बदलाव करने के लिए, सुलभता बटन को दबाकर रखें."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"बड़ा करना"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"कैमरा"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"माइक्रोफ़ोन"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"आपकी स्क्रीन पर, इस्तेमाल हो रहे दूसरे ऐप्लिकेशन के ऊपर दिखाया जा रहा है"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"प्राेफ़ाइल लोड हो रही है"</string>
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 2747521..92fdbba 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1148,6 +1148,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Otvaranje pomoću aplikacije"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Otvaranje pomoću aplikacije %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Otvori"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Omogućite pristup za otvaranje <xliff:g id="HOST">%1$s</xliff:g> veza pomoću aplikacije"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Omogućite pristup za otvaranje <xliff:g id="HOST">%1$s</xliff:g> veza pomoću aplikacije <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Omogući pristup"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Uređivanje pomoću aplikacije"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Uređivanje pomoću aplikacije %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Uredi"</string>
@@ -1589,6 +1592,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Prihvatiti poziv?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Uvijek"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Samo jednom"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Postavke"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ne podržava radni profil"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletno računalo"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televizor"</string>
@@ -1669,7 +1673,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcija boje"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečac pristupačnosti uključio je uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečac pristupačnosti isključio je uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Ponovo upotrijebite prečac pristupačnosti da biste pokrenuli trenutačnu značajku pristupačnosti"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Pritisnite i zadržite tipke za glasnoću na tri sekunde da biste koristili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite značajku koju ćete upotrebljavati kada dodirnete gumb Pristupačnost:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Da biste promijenili značajke, dodirnite i zadržite gumb Pristupačnost."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Povećavanje"</string>
@@ -2010,5 +2014,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Fotoaparat"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"prikazuje se preko drugih aplikacija na zaslonu"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Učitavanje"</string>
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 10538f4..c04d4fe 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Megnyitás a következővel:"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Megnyitás a következővel: %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Megnyitás"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Engedély megadása, hogy a(z) <xliff:g id="HOST">%1$s</xliff:g> linkek a következő alkalmazásban nyíljanak meg:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Engedély megadása, hogy a(z) <xliff:g id="HOST">%1$s</xliff:g> linkek a következő alkalmazásban nyíljanak meg: <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Engedély adása"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Szerkesztés a következővel:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Szerkesztés a következővel: %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Szerkesztés"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Fogadja a hívást?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Mindig"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Csak egyszer"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Beállítások"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"A(z) %1$s nem támogatja a munkaprofilokat."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Táblagép"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Színkorrekció"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"A Kisegítő lehetőségek gyorsparancsa bekapcsolta a következő szolgáltatást: <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"A Kisegítő lehetőségek gyorsparancsa kikapcsolta a következő szolgáltatást: <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Használja újból a Kisegítő lehetőségek gyorsparancsát az aktuális kisegítő lehetőségek elindításához"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> használatához tartsa lenyomva három másodpercig a két hangerőgombot"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Válassza ki a Kisegítő lehetőségek gombra koppintáskor használni kívánt funkciót:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"A funkciók módosításához tartsa lenyomva a Kisegítő lehetőségek gombot."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Nagyítás"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"megjelenítés a képernyőn lévő egyéb alkalmazások előtt"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Betöltés"</string>
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index fbb5f4b..84280ac 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Բացել հետևյալ ծրագրով՝"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Բացել ծրագրով՝ %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Բացել"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Թույլատրեք, որ <xliff:g id="HOST">%1$s</xliff:g> տիրույթը հղումները բացվեն հետևյալ հավելվածում՝"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Թույլատրեք, որ <xliff:g id="HOST">%1$s</xliff:g> տիրույթի հղումները բացվեն <xliff:g id="APPLICATION">%2$s</xliff:g> հավելվածում"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Թույլատրել"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Խմբագրել հետևյալ ծրագրով՝"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Խմբագրել հետևյալով՝ %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Փոփոխել"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Ընդունե՞լ զանգը:"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Միշտ"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Միայն մեկ անգամ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Կարգավորումներ"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s-ը չի աջակցում աշխատանքային պրոֆիլներ"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Գրասալիկ"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Հեռուստացույց"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Գունաշտկում"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Մատչելիության դյուրանցումն միացրել է <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Մատչելիության դյուրանցումն անջատել է <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Հատուկ գործառույթների դյուրանցումը գործարկելու համար նորից օգտագործեք համապատասխան դյուրանցումը"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"«<xliff:g id="SERVICE_NAME">%1$s</xliff:g>» ծառայությունն օգտագործելու համար սեղմեք և 3 վայրկյան պահեք ձայնի ուժգնության երկու կոճակները"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Ընտրեք որևէ գործառույթ, որը կօգտագործվի Հատուկ գործառույթներ կոճակին հպելու դեպքում՝"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Գործառույթները փոխելու համար հպեք և պահեք Հատուկ գործառույթներ կոճակը։"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Խոշորացում"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Տեսախցիկ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Խոսափող"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ցուցադրվում է մյուս հավելվածների վերևում"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Բեռնում"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3c6e347..905d3f2 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Buka dengan"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Buka dengan %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Buka"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Berikan akses untuk membuka link <xliff:g id="HOST">%1$s</xliff:g> dengan"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Berikan akses untuk membuka link <xliff:g id="HOST">%1$s</xliff:g> dengan <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Berikan akses"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edit dengan"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edit dengan %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Terima panggilan?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Selalu"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Hanya sekali"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Setelan"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s tidak mendukung profil kerja"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Koreksi Warna"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan Aksesibilitas mengaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Aksesibilitas menonaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gunakan Pintasan Aksesibilitas lagi untuk memulai fitur aksesibilitas saat ini"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Tekan dan tahan kedua tombol volume selama tiga detik untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih fitur yang akan digunakan saat menge-tap tombol Aksesibilitas:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Untuk mengubah fitur, sentuh &amp; tahan tombol Aksesibilitas."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Pembesaran"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ditampilkan di atas aplikasi lain di layar"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notifikasi info Mode Rutinitas"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Baterai mungkin habis sebelum pengisian daya biasanya"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Penghemat Baterai diaktifkan untuk memperpanjang masa pakai baterai"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Memuat"</string>
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index f638ff8..432fc7e 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Opna með"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Opna með %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Opna"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Veita aðgang til að opna <xliff:g id="HOST">%1$s</xliff:g> tengla með"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Veita aðgang til að opna <xliff:g id="HOST">%1$s</xliff:g> tengla með <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Veita aðgang"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Breyta með"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Breyta með %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Breyta"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Samþykkja símtal?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Alltaf"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Bara einu sinni"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Stillingar"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s styður ekki vinnusnið"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Spjaldtölva"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Sjónvarp"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Litaleiðrétting"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Flýtileið aðgengisstillingar kveikti á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Flýtileið aðgengisstillingar slökkti á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Notaðu flýtileið aðgengisstillingar aftur til að opna aðgengiseiginleika"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Haltu báðum hljóðstyrkstökkunum inni í þrjár sekúndur til að nota <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Veldu eiginleika sem á að nota þegar ýtt er á aðgengishnappinn:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Haltu fingri á aðgengishnappinum til að breyta eiginleikum."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Stækkun"</string>
@@ -1976,5 +1980,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Myndavél"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Hljóðnemi"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"birt yfir öðrum forritum á skjánum"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Upplýsingatilkynning aðgerðastillingar"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Rafhlaðan kann að tæmast áður en hún kemst í hleðslu"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Kveikt á rafhlöðusparnaði til að lengja endingu rafhlöðunnar"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Hleður"</string>
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index c9c8f0c..d76049d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Apri con"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Apri con %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Apri"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Dai l\'accesso per aprire i link di <xliff:g id="HOST">%1$s</xliff:g> con"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Dai l\'accesso per aprire i link di <xliff:g id="HOST">%1$s</xliff:g> con <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Fornisci accesso"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Modifica con"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Modifica con %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Modifica"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Accettare la chiamata?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Solo una volta"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Impostazioni"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s non supporta il profilo di lavoro"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Correzione del colore"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"La scorciatoia Accessibilità ha attivato <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"La scorciatoia Accessibilità ha disattivato <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Usa di nuovo la scorciatoia Accessibilità per avviare l\'attuale funzione di accessibilità"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Tieni premuti entrambi i tasti del volume per tre secondi per utilizzare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Scegli una funzione da usare quando tocchi il pulsante Accessibilità:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Per cambiare le funzioni, tocca e tieni premuto il pulsante Accessibilità."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ingrandimento"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Fotocamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microfono"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"si sovrappone ad altre app sullo schermo"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notifica di informazioni sulla modalità Routine"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"La batteria potrebbe esaurirsi prima della ricarica abituale"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Risparmio energetico attivo per far durare di più la batteria"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Caricamento"</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 26f6988f..df9631e 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"פתח באמצעות"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"‏פתח באמצעות %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"פתח"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"הענקת גישה לפתיחת קישורים של <xliff:g id="HOST">%1$s</xliff:g> באמצעות"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"הענקת גישה לפתיחת קישורים של <xliff:g id="HOST">%1$s</xliff:g> באמצעות <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"הענקת גישה"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ערוך באמצעות"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"‏ערוך באמצעות %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"עריכה"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"האם לקבל את השיחה?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"תמיד"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"רק פעם אחת"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"הגדרות"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"‏%1$s אינו תומך בפרופיל עבודה"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"טאבלט"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"טלוויזיה"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"תיקון צבעים"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל על-ידי קיצור הדרך לנגישות"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת על-ידי קיצור הדרך לנגישות"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"יש להשתמש שוב במקש הקיצור לנגישות כדי להפעיל את תכונת הנגישות הנוכחית"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"יש ללחוץ לחיצה ארוכה על שני לחצני עוצמת הקול למשך שלוש שניות כדי להשתמש בשירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"בחר תכונה שתופעל כשתלחץ על הלחצן \'נגישות\':"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"כדי להחליף תכונה, גע בלחצן \'נגישות\' והחזק אותו."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"הגדלה"</string>
@@ -2045,5 +2049,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"מצלמה"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"מיקרופון"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"תצוגה מעל אפליקציות אחרות במסך"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"בטעינה"</string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 45587cb..991ef13 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"アプリで開く"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$sで開く"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"開く"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"<xliff:g id="HOST">%1$s</xliff:g> のリンクを開くには、アクセス権を付与してください"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> で <xliff:g id="HOST">%1$s</xliff:g> のリンクを開くには、アクセス権を付与してください"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"アクセス権を付与"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"編集に使用"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$sで編集"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"編集"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"通話を受けますか?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"常時"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"1回のみ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"設定"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$sは仕事用プロファイルをサポートしていません"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"タブレット"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"テレビ"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"色補正"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ユーザー補助機能のショートカットにより <xliff:g id="SERVICE_NAME">%1$s</xliff:g> は ON になっています"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ユーザー補助機能のショートカットにより <xliff:g id="SERVICE_NAME">%1$s</xliff:g> は OFF になっています"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"現在のユーザー補助機能を開始するには、ユーザー補助機能のショートカットをもう一度使用してください"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を使用するには、音量大と音量小の両方のボタンを 3 秒間長押ししてください"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"[ユーザー補助] ボタンをタップした場合に使用する機能を選択してください。"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"機能を変更するには、[ユーザー補助] ボタンを押し続けてください。"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"拡大"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"カメラ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"マイク"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"画面の他のアプリの上に重ねて表示"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"読み込んでいます"</string>
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 07df9fc..cfce254 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"გახსნა აპით"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s-ით გახსნა"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"გახსნა"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"<xliff:g id="HOST">%1$s</xliff:g> ბმულების შემდეგი აპით გახსნის წვდომის მინიჭება:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="HOST">%1$s</xliff:g> ბმულების <xliff:g id="APPLICATION">%2$s</xliff:g>-ით გახსნის წვდომის მინიჭება"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"წვდომის მინიჭება"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"რედაქტირება აპით:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"რედაქტირება %1$s-ით"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"რედაქტირება"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"უპასუხებთ ზარს?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ყოველთვის"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"მხოლოდ ერთხელ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"პარამეტრები"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s მხარს არ უჭერს სამუშაო პროფილს"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ტაბლეტი"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ტელევიზია"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"ფერთა კორექცია"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"მარტივი წვდომის მალსახმობმა ჩართო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"მარტივი წვდომის მალსახმობმა გამორთო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"მარტივი წვდომის ამჟამინდელი ფუნქციის გასაშვებად გამოიყენეთ მარტივი წვდომის მალსახმობი"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> რომ გამოიყენოთ, დააჭირეთ ხმის ორივე ღილაკზე 3 წამის განმავლობაში"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"აირჩიეთ მარტივი წვდომის ღილაკზე შეხებისას გამოსაყენებელი ფუნქცია:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ფუნქციების შესაცვლელად ხანგრძლივად შეეხეთ მარტივი წვდომის ღილაკს."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"გადიდება"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"კამერა"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"მიკროფონი"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"სხვა აპების გადაფარვით ჩანს თქვენს ეკრანზე"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"იტვირთება"</string>
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index c622094..fb70fa9 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Басқаша ашу"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s қолданбасымен ашу"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ашу"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"<xliff:g id="HOST">%1$s</xliff:g> сілтемелерін келесі қолданбамен ашу үшін рұқсат беру:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="HOST">%1$s</xliff:g> сілтемелерін <xliff:g id="APPLICATION">%2$s</xliff:g> қолданбасымен ашу үшін рұқсат беру"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Рұқсат беру"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Келесімен өңдеу"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s көмегімен өңдеу"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Өңдеу"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Қоңырауды қабылдау?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Үнемі"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Бір рет қана"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Параметрлер"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s жұмыс профилін қолдамайды"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшет"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ТД"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Түсті түзету"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Арнайы мүмкіндіктер таңбашасы <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін қосты"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Арнайы мүмкіндіктер таңбашасы <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін өшірді"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Арнайы мүмкіндіктер функциясын іске қосу үшін оның таңбашасын пайдаланыңыз"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"\"Арнайы мүмкіндіктер\" түймесін түрткенде пайдаланатын мүмкіндікті таңдаңыз:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Мүмкіндіктерді өзгерту үшін \"Арнайы мүмкіндіктер\" түймесін түртіп, ұстап тұрыңыз."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ұлғайту"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Микрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"экранда басқа қолданбалардың үстінен көрсету"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Жүктелуде"</string>
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index ccc243d..9cc64ab 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1130,6 +1130,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"បើក​ជា​មួយ"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"បើក​ជាមួយ %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"បើក"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ផ្ដល់​សិទ្ធិ​ចូលប្រើ ដើម្បី​បើកតំណ <xliff:g id="HOST">%1$s</xliff:g> តាមរយៈ"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"ផ្ដល់​សិទ្ធិ​ចូល​ប្រើ ដើម្បី​បើកតំណ <xliff:g id="HOST">%1$s</xliff:g> តាមរយៈ <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ផ្តល់​សិទ្ធិ​ចូល​ប្រើ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"កែសម្រួល​ជាមួយ"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"កែសម្រួល​ជាមួយ​ %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"កែសម្រួល"</string>
@@ -1568,6 +1571,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"ទទួល​ការ​ហៅ​?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ជា​និច្ច"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"តែ​ម្ដង"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ការកំណត់"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s មិន​គាំទ្រ​ប្រវត្តិរូប​ការងារ"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"កុំព្យូទ័រ​បន្ទះ"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ទូរទស្សន៍"</string>
@@ -1647,7 +1651,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"ការ​កែ​ពណ៌"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ផ្លូវកាត់​ភាព​ងាយ​ស្រួល​បាន​បើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ផ្លូវកាត់​ភាព​ងាយ​ស្រួល​បាន​បិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ប្រើផ្លូវកាត់​ភាពងាយស្រួល​ម្ដងទៀត ដើម្បី​ចាប់ផ្ដើម​មុខងារ​ភាពងាយប្រើ​បច្ចុប្បន្ន"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"ចុចគ្រាប់ចុច​កម្រិត​សំឡេងទាំងពីរ​ឱ្យជាប់រយៈពេលបីវិនាទី ដើម្បីប្រើ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ជ្រើសរើស​មុខងារ​ដែលត្រូវ​ប្រើ នៅពេល​ដែល​អ្នកចុច​ប៊ូតុង​ភាពងាយស្រួល៖"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ដើម្បី​ផ្លាស់ប្តូរ​មុខងារ សូម​ចុច​ប៊ូតុង​ភាព​ងាយស្រួល​ឲ្យ​ជាប់។"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ការ​ពង្រីក"</string>
@@ -1977,5 +1981,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"កាមេរ៉ា"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"មីក្រូហ្វូន"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"កំពុងបង្ហាញ​ពីលើកម្មវិធីផ្សេងទៀត​នៅលើអេក្រង់​របស់អ្នក"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"កំពុងផ្ទុក"</string>
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 835070a..d6cde26 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"ಇದರ ಮೂಲಕ ತೆರೆಯಿರಿ"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ಜೊತೆಗೆ ತೆರೆಯಿರಿ"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"ತೆರೆ"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ಮೂಲಕ <xliff:g id="HOST">%1$s</xliff:g> ಲಿಂಕ್‌ಗಳನ್ನು ತೆರೆಯಲು ಪ್ರವೇಶವನ್ನು ನೀಡಿ"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> ಮೂಲಕ <xliff:g id="HOST">%1$s</xliff:g> ಲಿಂಕ್‌ಗಳನ್ನು ತೆರೆಯಲು ಪ್ರವೇಶವನ್ನು ನೀಡಿ"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ಪ್ರವೇಶ ಅನುಮತಿಸಿ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ಇವರ ಜೊತೆಗೆ ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ಜೊತೆಗೆ ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ಎಡಿಟ್"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"ಕರೆ ಸ್ವೀಕರಿಸುವುದೇ?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ಯಾವಾಗಲೂ"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"ಒಮ್ಮೆ ಮಾತ್ರ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ಕೆಲಸದ ಪ್ರೊಫೈಲ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ಟ್ಯಾಬ್ಲೆಟ್‌‌"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ಟಿವಿ"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್‌ಕಟ್‌, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಿದೆ"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್‌ಕಟ್‌, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಫ್ ಮಾಡಿದೆ"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ಪ್ರಸ್ತುತ ಪ್ರವೇಶದ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಪ್ರಾರಂಭಿಸಲು ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್‌ಕಟ್‌ ಅನ್ನು ಪುನಃ ಬಳಸಿ"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸಲು ಎರಡೂ ಧ್ವನಿ ಕೀಗಳನ್ನು ಮೂರು ಸೆಕೆಂಡ್‌ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ನೀವು ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್ ಟ್ಯಾಪ್ ಮಾಡಿದಾಗ ಬಳಸುವುದಕ್ಕಾಗಿ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಆರಿಸಿ:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಬದಲಾಯಿಸಲು, ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್ ಒತ್ತಿಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ಹಿಗ್ಗಿಸುವಿಕೆ"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"ಕ್ಯಾಮರಾ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"ಮೈಕ್ರೋಫೋನ್‌"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೂಲಕ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತಿದೆ"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8886815..05ab555 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"연결 프로그램"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s(으)로 열기"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"열기"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"다음으로 <xliff:g id="HOST">%1$s</xliff:g> 링크를 열려면 액세스 권한 부여"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g>(으)로 <xliff:g id="HOST">%1$s</xliff:g> 링크를 열려면 액세스 권한 부여"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"권한 부여"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"편집 프로그램:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s(으)로 수정"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"수정"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"통화를 수락하시겠습니까?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"항상"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"한 번만"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"설정"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s에서 직장 프로필을 지원하지 않습니다."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"태블릿"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"색상 보정"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"접근성 단축키로 인해 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"접근성 단축키로 인해 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"현재 접근성 기능을 시작하려면 접근성 단축키를 다시 사용하세요"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 서비스를 사용하려면 두 볼륨 키를 3초 동안 길게 누르세요"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"접근성 버튼을 탭할 때 사용할 기능을 선택하세요."</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"기능을 변경하려면 접근성 버튼을 길게 터치하세요."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"확대"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"카메라"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"마이크"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"화면에서 다른 앱 위에 표시"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"로드 중"</string>
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index de49741..88f0ef2 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Төмөнкү менен ачуу"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s менен ачуу"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ачуу"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Төмөнкү колдонмо менен <xliff:g id="HOST">%1$s</xliff:g> шилтемелерин  ачууга уруксат берүү"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> колдонмосу менен <xliff:g id="HOST">%1$s</xliff:g> шилтемелерин  ачууга уруксат берүү"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Мүмкүнчүлүк берүү"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Төмөнкү менен түзөтүү"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s менен түзөтүү"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Түзөтүү"</string>
@@ -1568,6 +1571,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Чалуу кабыл алынсынбы?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Дайыма"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Бир жолу гана"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Жөндөөлөр"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s жумуш профилин колдоого албайт"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшет"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Сыналгы"</string>
@@ -1647,7 +1651,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Түсүн тууралоо"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Атайын мүмкүнчүлүктөр кыска жолу <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын күйгүздү"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Атайын мүмкүнчүлүктөр кыска жолу <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын өчүрдү"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Учурдагы атайын мүмкүнчүлүктөр функциясын иштетүү үчүн кыска жолду кайра колдонуңуз"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын колдонуу үчүн үнүн чоңойтуп/кичирейтүү баскычтарын үч секунд коё бербей басып туруңуз"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Атайын мүмкүнчүлүктөр баскычын таптаганыңызда иштетиле турган функцияны тандаңыз:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Функцияларды өзгөртүү үчүн Атайын мүмкүнчүлүктөр баскычын басып, кармап туруңуз."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Чоңойтуу"</string>
@@ -1977,5 +1981,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Микрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"экрандагы башка терезелердин үстүнөн көрсөтүлүүдө"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Жүктөлүүдө"</string>
 </resources>
diff --git a/core/res/res/values-land/dimens_permission_controller.xml b/core/res/res/values-land/dimens_permission_controller.xml
deleted file mode 100644
index 2146241..0000000
--- a/core/res/res/values-land/dimens_permission_controller.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
-  -->
-
-<!-- Landscape dimensions for the permission grant dialog. -->
-<resources>
-    <!-- Assuming the dimension of a sailfish, this yields 95% width in splitscreen and 65% in
-         landscape -->
-    <dimen name="permissionGrantDialogWeight">8.6</dimen>
-    <dimen name="permissionGrantDialogWidth">334dp</dimen>
-</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 9356302..7a5ab96 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"ເປີດໂດຍໃຊ້"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"ເປີດ​ໂດຍ​ໃຊ້ %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"ເປີດ"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ໃຫ້ສິດອະນຸຍາດເພື່ອເປີດລິ້ງ <xliff:g id="HOST">%1$s</xliff:g> ດ້ວຍ"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"ໃຫ້ສິດອະນຸຍາດເພື່ອເປີດລິ້ງ <xliff:g id="HOST">%1$s</xliff:g> ດ້ວຍ <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ໃຫ້ສິດອະນຸຍາດ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"​ແກ້​ໄຂ​ໃນ"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"ແກ້​ໄຂ​ໃນ %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ແກ້ໄຂ"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"ຮັບການໂທບໍ່?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ທຸກຄັ້ງ"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"ຄັ້ງດຽວ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ການຕັ້ງຄ່າ"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ບໍ່​ຮອງ​ຮັບ​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ​ຂອງ​ທ່ານ"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ແທັບເລັດ"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ໂທລະພາບ"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"ການແກ້ໄຂຄ່າສີ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ໃຊ້ປຸ່ມລັດການຊ່ວຍເຂົ້າເຖິງອີກເທື່ອໜຶ່ງເພື່ອຊອກຫາຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງໃນປັດຈຸບັນ"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"ກົດປຸ່ມສຽງທັງສອງພ້ອມກັນຄ້າງໄວ້ສາມວິນາທີເພື່ອໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ເລືອກຄຸນສົມບັດທີ່ຈະໃຊ້ເມື່ອທ່ານແຕະປຸ່ມການຊ່ວຍເຂົ້າເຖິງ:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ເພື່ອປ່ຽນຄຸນສົມບັດ, ໃຫ້ແຕະປຸ່ມການຊ່ວຍເຂົ້າເຖິງຄ້າງໄວ້."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ການຂະຫຍາຍ"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"ກ້ອງ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"ໄມໂຄຣໂຟນ"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ສະແດງຜົນບັງແອັບອື່ນຢູ່ໜ້າຈໍຂອງທ່ານ"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"ກຳລັງໂຫລດ"</string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 8870ebb..90ed406 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Atidaryti naudojant"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Atidaryti naudojant %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Atidaryti"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Suteikite prieigą atidaryti <xliff:g id="HOST">%1$s</xliff:g> nuorodas naudojant"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Suteikite prieigą atidaryti <xliff:g id="HOST">%1$s</xliff:g> nuorodas naudojant „<xliff:g id="APPLICATION">%2$s</xliff:g>“"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Suteikti prieigą"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Redaguoti naudojant"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Redaguoti naudojant %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Redaguoti"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Priimti skambutį?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Visada"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Tik kartą"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Nustatymai"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s nepalaiko darbo profilio"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planšetinis kompiuteris"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Spalvų taisymas"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pritaikymo neįgaliesiems sparčiuoju klavišu buvo įjungta „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pritaikymo neįgaliesiems sparčiuoju klavišu buvo išjungta „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Norėdami įjungti dabartinę pritaikymo neįgaliesiems funkciją, dar kartą naudokite spartųjį pritaikymo neįgaliesiems klavišą"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Jei norite naudoti „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite abu garsumo klavišus ir palaikykite tris sekundes"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pasirinkite funkciją, kuri bus naudojama, kai paliesite pritaikymo neįgaliesiems mygtuką:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Jei norite pakeisti funkcijas, palieskite ir palaikykite pritaikymo neįgaliesiems mygtuką."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Didinimas"</string>
@@ -2045,5 +2049,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Fotoaparatas"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofonas"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"rodo virš kitų programų jūsų ekrane"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Įkeliama"</string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 5a1432b..e58b462 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1148,6 +1148,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Atvērt, izmantojot"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Atvērt, izmantojot %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Atvērt"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Piekļuves piešķiršana, lai atvērtu <xliff:g id="HOST">%1$s</xliff:g> saites lietojumprogrammā"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Piekļuves piešķiršana, lai atvērtu <xliff:g id="HOST">%1$s</xliff:g> saites lietojumprogrammā <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Atļaut piekļuvi"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Rediģēt, izmantojot"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Rediģēt, izmantojot %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Rediģēt"</string>
@@ -1589,6 +1592,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vai atbildēt uz zvanu?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Vienmēr"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Tikai vienreiz"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Iestatījumi"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"Programma %1$s neatbalsta darba profilus"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planšetdators"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1669,7 +1673,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Krāsu korekcija"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pieejamības saīsne aktivizēja lietotni <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pieejamības saīsne deaktivizēja lietotni <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Lai sāktu izmantot pašreizējo pieejamības funkciju, vēlreiz izmantojiet pieejamības saīsni."</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Lai izmantotu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, nospiediet abus skaļuma taustiņus un turiet tos trīs sekundes."</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izvēlieties funkciju, ko izmantot, kad pieskaraties pogai Pieejamība."</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Lai mainītu funkcijas, pieskarieties pogai Pieejamība un turiet to."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Palielinājums"</string>
@@ -2010,5 +2014,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofons"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"rāda pāri citām lietotnēm jūsu ekrānā"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Ielāde"</string>
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 769ac43..8bede29 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Отвори со"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Отвори со %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Отвори"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Дајте пристап да се отвораат линкови на <xliff:g id="HOST">%1$s</xliff:g> со"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Дајте пристап да се отвораат линкови на <xliff:g id="HOST">%1$s</xliff:g> со <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Дозволи пристап"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Измени со"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Измени со %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Измени"</string>
@@ -1569,6 +1572,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Прифати повик?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Секогаш"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Само еднаш"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Поставки"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s не поддржува работен профил"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевизор"</string>
@@ -1648,7 +1652,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Корекција на бои"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Кратенката за пристапност ја вклучи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Кратенката за пристапност ја исклучи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Повторно употребете ја кратенката за пристапност за да ја стартувате тековната функција за пристапност"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Притиснете ги и задржете ги двете копчиња за јачина на звукот во траење од три секунди за да користите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изберете функција за користење кога ќе го допрете копчето за „Пристапност“."</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"За променување функции, допрете го и задржете го копчето за „Пристапност“."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Зголемување"</string>
@@ -1978,5 +1982,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Микрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"се прикажува преку други апликации на вашиот екран"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Известување за информации за режимот за рутини"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Батеријата може да се потроши пред вообичаеното време за полнење"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Активиран е „Штедачот на батерија“ за да се продолжи траењето на батеријата"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Се вчитува"</string>
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 620718d..190341b 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"ഇത് ഉപയോഗിച്ച് തുറക്കുക"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ഉപയോഗിച്ച് തുറക്കുക"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"തുറക്കുക"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ഇനിപ്പറയുന്നത് ഉപയോഗിച്ച്, <xliff:g id="HOST">%1$s</xliff:g> ലിങ്കുകൾ തുറക്കാൻ ആക്‌സ‌സ് നൽകുക"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> ഉപയോഗിച്ച്, <xliff:g id="HOST">%1$s</xliff:g> ലിങ്കുകൾ തുറക്കാൻ ആക്‌സ‌സ് നൽകുക"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ആക്‌സസ് നൽകുക"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ഇത് ഉപയോഗിച്ച് എഡിറ്റുചെയ്യുക"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ഉപയോഗിച്ച് എഡിറ്റുചെയ്യുക"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"എഡിറ്റുചെയ്യുക"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"കോൾ സ്വീകരിക്കണോ?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"എല്ലായ്പ്പോഴും"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"ഒരിക്കൽ മാത്രം"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ക്രമീകരണം"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s, ഔദ്യോഗിക പ്രൊഫൈലിനെ പിന്തുണയ്‌ക്കുന്നില്ല"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ടാബ്‌ലെറ്റ്"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ടിവി"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"വർണ്ണം ക്രമീകരിക്കൽ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ഉപയോഗസഹായിക്കുള്ള കുറുക്കുവഴി <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓൺ ചെയ്തിരിക്കുന്നു"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ഉപയോഗസഹായിക്കുള്ള കുറുക്കുവഴി <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫ് ചെയ്തിരിക്കുന്നു"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"നിലവിലെ ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കാൻ വീണ്ടും ഉപയോഗസഹായി കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഉപയോഗിക്കാൻ, രണ്ട് വോളിയം കീകളും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"നിങ്ങൾ ഉപയോഗസഹായി ബട്ടൺ ടാപ്പുചെയ്യുമ്പോൾ ഉപയോഗിക്കുന്നതിന് ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ഫീച്ചറുകൾ മാറ്റുന്നതിന് ഉപയോഗസഹായി ബട്ടൺ സ്‌പർശിച്ചുപിടിക്കുക."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"മാഗ്നിഫിക്കേഷൻ"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"ക്യാമറ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"മൈക്രോഫോൺ"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"നിങ്ങളുടെ സ്‌ക്രീനിലെ മറ്റ് ആപ്പുകൾക്ക് മുകളിൽ പ്രദർശിപ്പിക്കുന്നു"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"ലോഡ് ചെയ്യുന്നു"</string>
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index c0b23fc..f22974d 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Нээх"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ашиглан нээх"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Нээх"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"<xliff:g id="HOST">%1$s</xliff:g>-н холбоосыг дараахаар нээх хандалт өгөх"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="HOST">%1$s</xliff:g>-н холбоосыг <xliff:g id="APPLICATION">%2$s</xliff:g>-р нээх хандалт өгөх"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Хандалт өгөх"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Засварлах"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ашиглан засварлах"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Засах"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Дуудлагыг зөвшөөрөх үү?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Байнга"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Нэг удаа"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Тохиргоо"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ажлын профайлыг дэмждэггүй"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tелевиз"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Өнгөний засвар"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Хүртээмжийн товчлол <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаасан"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Хүртээмжийн товчлол <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраасан"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Одоогийн хүртээмжит онцлогийг эхлүүлэхийн тулд нэвтрэлтийн товчлолыг ашиглах"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г ашиглахын тулд дууны түвшнийг ихэсгэх, багасгах түлхүүрийг 3 секундийн турш зэрэг дарна уу"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Хүртээмжийн товчлуурыг товших үедээ ашиглах онцлогийг сонгоно уу:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Онцлогийг өөрчлөхийн тулд Хүртээмжийн товчлуурыг дараад хүлээнэ үү."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Томруулах"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камер"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Микрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"таны дэлгэцэд бусад аппын дээр харуулж байна"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Ачаалж байна"</string>
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index f3d6b4b..1792887 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"यासह उघडा"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s सह उघडा"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"उघडा"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"सह <xliff:g id="HOST">%1$s</xliff:g> लिंक उघडण्याचा अ‍ॅक्सेस द्या"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> सह <xliff:g id="HOST">%1$s</xliff:g> लिंक उघडण्याचा अ‍ॅक्सेस द्या"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"अ‍ॅक्सेस द्या"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"सह संपादित करा"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s सह संपादित करा"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"संपादित करा"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"कॉल स्वीकारायचा?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"नेहमी"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"फक्त एकदाच"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"सेटिंग्ज"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s कार्य प्रोफाईलचे समर्थन करीत नाही"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"टॅबलेट"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"टीव्ही"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"रंग सुधारणा"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"प्रवेशयोग्यता शॉर्टकटने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> चालू केली"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"प्रवेशयोग्यता शॉर्टकटने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केली"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"सध्याचे अॅक्सेसिबिलिटी वैशिष्‍ट्य पुन्हा सुरू करण्‍यासाठी अॅक्सेसिबिलिटी शॉर्टकट वापरा"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"तुम्ही प्रवेशयोग्यता बटण दाबल्यावर वापरण्यासाठी वैशिष्ट्य निवडा:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"वैशिष्ट्ये बदलण्यासाठी, प्रवेशयोग्यता बटणाला स्पर्श करा आणि धरून ठेवा."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"मोठे करणे"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"कॅमेरा"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"मायक्रोफोन"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"तुमच्‍या स्‍क्रीनवर इतर अॅप्‍सवर डिस्‍प्‍ले करत आहे"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"लोड होत आहे"</string>
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 0eb022a..1055ca6 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Buka dengan"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Buka dengan %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Buka"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Berikan akses untuk membuka pautan <xliff:g id="HOST">%1$s</xliff:g> dengan"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Berikan akses untuk membuka pautan <xliff:g id="HOST">%1$s</xliff:g> dengan <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Berikan akses"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edit dengan"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edit dengan %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Terima panggilan?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sentiasa"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Hanya sekali"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Tetapan"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s tidak menyokong profil kerja"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Pembetulan Warna"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan kebolehaksesan menghidupkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Kebolehaksesan mematikan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gunakan Pintasan Kebolehaksesan sekali lagi untuk memulakan ciri kebolehaksesan semasa"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Tekan dan tahan kedua-dua kekunci kelantangan selama tiga saat untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih ciri yang hendak digunakan apabila anda mengetik butang Kebolehaksesan:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Untuk menukar ciri, sentuh &amp; tahan butang Kebolehaksesan."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Pembesaran"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"dipaparkan di atas apl lain pada skrin anda"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Memuatkan"</string>
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 1c5f0c7..651affc 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"...ဖြင့် ဖွင့်မည်"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ဖြင့် ဖွင့်မည်"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"ဖွင့်ပါ"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"<xliff:g id="HOST">%1$s</xliff:g> လင့်ခ်များကို အ​ောက်ပါဖြင့် ဖွင့်ခွင့်ပေးပါ-"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="HOST">%1$s</xliff:g> လင့်ခ်များကို <xliff:g id="APPLICATION">%2$s</xliff:g> ဖြင့် ဖွင့်ခွင့်ပေးပါ"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ဖွင့်ခွင့်ပေးရန်"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"...နှင့် တည်းဖြတ်ရန်"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s နှင့် တည်းဖြတ်ရန်"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"တည်းဖြတ်ပါ"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"ဖုန်းခေါ်ဆိုမှုကို လက်ခံမလား?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"အမြဲတမ်း"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"တစ်ခါတည်း"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ဆက်တင်များ"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s က အလုပ်ပရိုဖိုင်ကို မပံ့ပိုးပါ။"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"တက်ဘလက်"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"တီဗွီ"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"အရောင်ပြင်ဆင်ခြင်း"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်သည် <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို ဖွင့်လိုက်ပါသည်"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်သည် <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို ပိတ်လိုက်ပါသည်"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"လက်ရှိ အသုံးလွယ်ရေး ဝန်ဆောင်မှုကို စတင်ရန် အသုံးလွယ်ရေး ဖြတ်လမ်းလင့်ခ်ကို သုံးပါ"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို သုံးရန် အသံအတိုးအလျှော့ ခလုတ်နှစ်ခုလုံးကို သုံးစက္ကန့်ကြာ ဖိထားပါ"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"အများသုံးစွဲနိုင်မှု ခလုတ်ကို တို့သည့်အခါ အသုံးပြုမည့် ဝန်ဆောင်မှုကို ရွေးချယ်ပါ−"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ဝန်ဆောင်မှုများကို ပြောင်းလဲရန် အများသုံးစွဲနိုင်မှု ခလုတ်ကို တို့၍ ထိထားပါ။"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ချဲ့ခြင်း"</string>
@@ -1976,5 +1980,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"ကင်မရာ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"မိုက်ခရိုဖုန်း"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"သင့်မျက်နှာပြင်ပေါ်ရှိ အခြားအက်ပ်များပေါ်တွင် ပြသခြင်း"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"ပုံမှန်မုဒ်အတွက် အချက်အလက်ပြသည့် အကြောင်းကြားချက်"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"ပုံမှန်အားသွင်းမှုမပြုလုပ်မီ ဘက်ထရီကုန်သွားနိုင်သည်"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"ဘက်ထရီသက်တမ်းကို တိုးမြှင့်ရန် \'ဘက်ထရီအားထိန်း\' စတင်ပြီးပါပြီ"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"တင်နေသည်"</string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 233b98a..1f70767 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Åpne med"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Åpne med %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Åpne"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Gi tilgang til å åpne <xliff:g id="HOST">%1$s</xliff:g>-linker med"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Gi tilgang til å åpne <xliff:g id="HOST">%1$s</xliff:g>-linker med <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Gi tilgang"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Rediger med"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Rediger med %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Endre"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vil du besvare anropet?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Alltid"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Bare én gang"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Innstillinger"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s støtter ikke arbeidsprofiler"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Nettbrett"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Google TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Fargekorrigering"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Snarveien for tilgjengelighet slo på <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Snarveien for tilgjengelighet slo av <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Bruk tilgjengelighetssnarveien igjen for å starte den nåværende tilgjengelighetsfunksjonen"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Trykk og hold inne begge volumtastene i tre sekunder for å bruke <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Velg en funksjon du vil bruke når du trykker på Tilgjengelighet-knappen:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"For å endre funksjoner, trykk på og hold inne Tilgjengelighet-knappen."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Forstørring"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"vises over andre apper på skjermen"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Laster inn"</string>
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 0b2362a..d278fbe 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1132,6 +1132,9 @@
     <!-- no translation found for whichViewApplicationNamed (2286418824011249620) -->
     <skip />
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"खोल्नुहोस्"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"निम्नमार्फत <xliff:g id="HOST">%1$s</xliff:g>का लिंकहरू खोल्न पहुँच दिनुहोस्‌"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g>मार्फत <xliff:g id="HOST">%1$s</xliff:g>का लिंकहरू खोल्न पहुँच दिनुहोस्‌"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"पहुँच दिनुहोस्"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"सँग सम्पादन गर्नुहोस्"</string>
     <!-- String.format failed for translation -->
     <!-- no translation found for whichEditApplicationNamed (1775815530156447790) -->
@@ -1572,6 +1575,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"कल स्वीकार गर्नुहुन्छ?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"सधैँ"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"एक पटक मात्र"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"सेटिङहरू"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s  कार्य प्रोफाइल समर्थन गर्दैन"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ट्याब्लेट"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1651,7 +1655,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"रङ सच्याउने सुविधा"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"पहुँचको सर्टकटले <xliff:g id="SERVICE_NAME">%1$s</xliff:g> लाई सक्रिय पार्‍यो"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"पहुँचको सर्टकटले <xliff:g id="SERVICE_NAME">%1$s</xliff:g> लाई निष्क्रिय पार्‍यो"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"हालको पहुँचसम्बन्धी सुविधा प्रयोग गर्न फेरि पहुँचसम्बन्धी सर्टकट प्रयोग गर्नुहोस्"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> प्रयोग गर्न दुवै भोल्युम कुञ्जीहरूलाई तीन सेकेन्डसम्म थिचिराख्नुहोस्"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"तपाईंले पहुँच सम्बन्धी बटनलाई ट्याप गर्दा प्रयोग गर्नुपर्ने सुविधा रोज्नुहोस्:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"सुविधाहरूलाई बदल्न, पहुँच सम्बन्धी बटनलाई छोएर थिची राख्नुहोस्।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"म्याग्निफिकेसन"</string>
@@ -1981,5 +1985,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"क्यामेरा"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"माइक्रोफोन"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमा प्रदर्शन गरिँदै छ"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"लोड गर्दै"</string>
 </resources>
diff --git a/core/res/res/values-night/themes_permission_controller.xml b/core/res/res/values-night/themes_permission_controller.xml
deleted file mode 100644
index a071927..0000000
--- a/core/res/res/values-night/themes_permission_controller.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
-  -->
-
-<!-- themes for the permission grant dialog. -->
-<resources>
-    <style name="Theme.DeviceDefault.PermissionGrantApp"
-           parent="@style/Theme.DeviceDefault.Panel">
-        <item name="windowIsFloating">false</item>
-        <item name="windowTranslucentStatus">true</item>
-        <item name="backgroundDimEnabled">true</item>
-        <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
-    </style>
-
-    <style name="Theme.DeviceDefault.PermissionGrant"
-           parent="@style/Theme.DeviceDefault.Dialog">
-        <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-    </style>
-</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 996b700..752fbc2 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Openen met"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Openen met %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Openen"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Toegang verlenen om links naar <xliff:g id="HOST">%1$s</xliff:g> te openen met"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Toegang verlenen om links naar <xliff:g id="HOST">%1$s</xliff:g> te openen met <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Toegang geven"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Bewerken met"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Bewerken met %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Bewerken"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Gesprek accepteren?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Altijd"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Één keer"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Instellingen"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ondersteunt werkprofielen niet"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tv"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Kleurcorrectie"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ingeschakeld"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gebruik de snelkoppeling voor toegankelijkheid nogmaals om de huidige toegankelijkheidsfunctie te starten"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Houd beide volumetoetsen drie seconden ingedrukt om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruiken"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Kies een functie om te gebruiken wanneer je op de knop Toegankelijkheid tikt:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Als je functies wilt wijzigen, tik je op de knop Toegankelijkheid en houd je deze vast."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Vergroting"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Camera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microfoon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"wordt weergegeven vóór andere apps op je scherm"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Informatiemelding voor routinemodus"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"De batterij raakt mogelijk leeg voordat deze normaal gesproken wordt opgeladen"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Batterijbesparing is geactiveerd om de batterijduur te verlengen"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Laden"</string>
 </resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 27b7d5d..f2c0f62 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"ସହିତ ଖୋଲନ୍ତୁ"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ସହିତ ଖୋଲନ୍ତୁ"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"ଖୋଲନ୍ତୁ"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ଏହା ସହିତ ଲିଙ୍କ ଥିବା <xliff:g id="HOST">%1$s</xliff:g> ଖୋଲିବା ପାଇଁ ଆକ୍ସେସ୍ ଦିଅନ୍ତୁ"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> ସହିତ ଲିଙ୍କ ଥିବା <xliff:g id="HOST">%1$s</xliff:g> ଖୋଲିବା ପାଇଁ ଆକ୍ସେସ୍ ଦିଅନ୍ତୁ"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ଆକ୍ସେସ୍‌ ଦିଅନ୍ତୁ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ସହିତ ଏଡିଟ୍‌ କରନ୍ତୁ"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$sରେ ସଂଶୋଧନ କରନ୍ତୁ"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ଏଡିଟ୍‌ କରନ୍ତୁ"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"କଲ୍‍ ସ୍ୱୀକାର କରିବେ?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ସର୍ବଦା"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"ଥରେ ମାତ୍ର"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ସେଟିଂସ୍"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ୱର୍କ ପ୍ରୋଫାଇଲ୍‌କୁ ସପୋର୍ଟ କରୁନାହିଁ"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ଟାବଲେଟ୍‌"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ଆକ୍ସେସିବିଲିଟୀ ଶର୍ଟକଟ୍‍ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଅନ୍‍ କରାଯାଇଛି"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ଆକ୍ସେସିବିଲିଟୀ ଶର୍ଟକଟ୍‍ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଅଫ୍‍ କରାଯାଇଛି"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ବୈଶିଷ୍ଟ୍ୟ ଆରମ୍ଭ କରିବାକୁ ପୁଣି ଆକ୍ସେସିବିଲିଟୀ ସର୍ଟ୍‍କର୍ଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍‍ କୀ ଦବାଇ ଧରି ରଖନ୍ତୁ"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ଆପଣ ଆକ୍ସେସବିଲିଟି ବଟନ୍‍ ଟାପ୍‍ କରିବା ବେଳେ ଏକ ବୈଶିଷ୍ଟ୍ୟ ବ୍ୟବହାର କରିବାକୁ ବାଛନ୍ତୁ:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ବୈଶିଷ୍ଟ୍ୟ ବଦଳାଇବାକୁ, ଆକ୍ସେସବିଲିଟି ବଟନ୍‍ ସ୍ପର୍ଶ କରନ୍ତୁ ଓ ଧରିରଖନ୍ତୁ।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ମ୍ୟାଗ୍ନିଫିକେସନ୍‍"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"କ୍ୟାମେରା"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"ମାଇକ୍ରୋଫୋନ୍"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍ ଉପରେ ଥିବା ଅନ୍ୟ ଆପ୍‌ ଉପରେ ଦେଖାଦେବ"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"ଲୋଡ୍ ହେଉଛି"</string>
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index eacf006..266b226 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"ਨਾਲ ਖੋਲ੍ਹੋ"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ਨਾਲ ਖੋਲ੍ਹੋ"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"ਖੋਲ੍ਹੋ"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ਇਸ ਨਾਲ <xliff:g id="HOST">%1$s</xliff:g> ਲਿੰਕਾਂ ਨੂੰ ਖੋਲ੍ਹਣ ਦੀ ਪਹੁੰਚ ਦਿਓ"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> ਨਾਲ <xliff:g id="HOST">%1$s</xliff:g> ਲਿੰਕਾਂ ਨੂੰ ਖੋਲ੍ਹਣ ਦੀ ਪਹੁੰਚ ਦਿਓ"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ਪਹੁੰਚ ਦਿਓ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"ਇਸ ਨਾਲ ਸੰਪਾਦਨ ਕਰੋ"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ਨਾਲ ਸੰਪਾਦਨ ਕਰੋ"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ਸੰਪਾਦਨ ਕਰੋ"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"ਕੀ ਕਾਲ ਸਵੀਕਾਰ ਕਰਨੀ ਹੈ?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ਹਮੇਸ਼ਾਂ"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"ਕੇਵਲ ਇੱਕ ਵਾਰ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ਟੈਬਲੈੱਟ"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"ਰੰਗ ਸੁਧਾਈ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਨੇ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਨੇ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ਮੌਜੂਦਾ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ \'ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ\' ਦੀ ਦੁਬਾਰਾ ਵਰਤੋਂ ਕਰੋ"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ 3 ਸਕਿੰਟਾਂ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਟੈਪ ਕੀਤੇ ਜਾਣ \'ਤੇ ਵਰਤਣ ਲਈ ਕੋਈ ਵਿਸ਼ੇਸ਼ਤਾ ਚੁਣੋ:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ਵੱਡਦਰਸ਼ੀਕਰਨ"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"ਕੈਮਰਾ"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਹੋਰਾਂ ਐਪਾਂ ਉੱਪਰ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b2bb7b0..a86d5a6 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Otwórz w aplikacji"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Otwórz w aplikacji %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Otwórz"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Przyznaj uprawnienia do otwierania linków z <xliff:g id="HOST">%1$s</xliff:g> w:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Przyznaj uprawnienia do otwierania linków z <xliff:g id="HOST">%1$s</xliff:g> w aplikacji <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Udziel uprawnień"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Edytuj w aplikacji"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Edytuj w aplikacji %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edytuj"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Odebrać połączenie?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Zawsze"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Tylko raz"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Ustawienia"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s nie obsługuje profilu do pracy"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Telewizor"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcja kolorów"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Użyj ponownie skrótu ułatwień dostępu, by uruchomić bieżące ułatwienie dostępu"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Naciśnij i przytrzymaj oba przyciski głośności przez trzy sekundy, by użyć usługi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Wybierz funkcję używaną po kliknięciu przycisku ułatwień dostępu."</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Aby zmienić funkcje, kliknij i przytrzymaj przycisk ułatwień dostępu."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Powiększenie"</string>
@@ -2045,5 +2049,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Aparat"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"wyświetla się nad innymi aplikacjami na ekranie"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Powiadomienie z informacją o trybie rutynowym"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Bateria może się wyczerpać przed zwykłą porą ładowania"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Włączono Oszczędzanie baterii, by wydłużyć czas pracy na baterii"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Ładuję"</string>
 </resources>
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/core/res/res/values-port/dimens_permission_controller.xml
deleted file mode 100644
index af28713..0000000
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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.
-  -->
-
-<!-- portrait dimensions for the permission grant dialog. -->
-<resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
-</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index b1a1d39..44f8478 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Abrir com"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Abrir com %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Abrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Conceder acesso para abrir links de <xliff:g id="HOST">%1$s</xliff:g> com"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Conceder acesso para abrir links de <xliff:g id="HOST">%1$s</xliff:g> com o app <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Conceder acesso"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editar com"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editar com %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Editar"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Aceitar chamada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Só uma vez"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Configurações"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s não aceita perfis de trabalho"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Correção de cor"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atalho de acessibilidade ativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use o atalho de acessibilidade novamente para iniciar o recurso de acessibilidade atual"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha um recurso a ser usado ao tocar no botão Acessibilidade:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para alterar os recursos, mantenha o botão Acessibilidade pressionado."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliação"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Câmera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microfone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"exibindo sobre outros apps na sua tela"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificação de informação do modo rotina"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"A bateria pode acabar antes da recarga normal"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"A \"Economia de bateria\" foi ativada para aumentar a duração da carga"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Carregando"</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index ec4cf8c..f7ffa1d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Abrir com"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Abrir com %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Abrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Conceda acesso para abrir links de <xliff:g id="HOST">%1$s</xliff:g> com"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Conceda acesso para abrir links de <xliff:g id="HOST">%1$s</xliff:g> com a aplicação <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Conceder acesso"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editar com"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editar com %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Editar"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Aceitar chamada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Apenas uma vez"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Definições"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s não suporta o perfil de trabalho"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Correção da cor"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O Atalho de acessibilidade ativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O Atalho de acessibilidade desativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Utilize novamente o atalho de acessibilidade para iniciar a funcionalidade de acessibilidade atual."</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Prima sem soltar as teclas de volume durante três segundos para utilizar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha uma funcionalidade para utilizar quando tocar no botão Acessibilidade:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para alterar as funcionalidades, toque sem soltar no botão Acessibilidade."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliação"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Câmara"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microfone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"sobrepõe-se a outras aplicações no ecrã"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificação de informações do Modo rotina"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Pode ficar sem bateria antes do carregamento habitual"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Poupança de bateria ativada para prolongar a duração da bateria"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"A carregar…"</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index b1a1d39..44f8478 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Abrir com"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Abrir com %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Abrir"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Conceder acesso para abrir links de <xliff:g id="HOST">%1$s</xliff:g> com"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Conceder acesso para abrir links de <xliff:g id="HOST">%1$s</xliff:g> com o app <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Conceder acesso"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editar com"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editar com %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Editar"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Aceitar chamada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Só uma vez"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Configurações"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s não aceita perfis de trabalho"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Correção de cor"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atalho de acessibilidade ativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use o atalho de acessibilidade novamente para iniciar o recurso de acessibilidade atual"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha um recurso a ser usado ao tocar no botão Acessibilidade:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para alterar os recursos, mantenha o botão Acessibilidade pressionado."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliação"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Câmera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microfone"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"exibindo sobre outros apps na sua tela"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificação de informação do modo rotina"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"A bateria pode acabar antes da recarga normal"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"A \"Economia de bateria\" foi ativada para aumentar a duração da carga"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Carregando"</string>
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 5b36b9a..f3303c4 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1148,6 +1148,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Deschideți cu"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Deschideți cu %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Deschideți"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Permiteți accesul pentru a deschide linkurile <xliff:g id="HOST">%1$s</xliff:g> cu"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Permiteți accesul pentru a deschide linkurile <xliff:g id="HOST">%1$s</xliff:g> cu <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Permiteți accesul"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Editați cu"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editați cu %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Editați"</string>
@@ -1589,6 +1592,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Acceptați apelul?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Întotdeauna"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Numai o dată"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Setări"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s nu acceptă profilul de serviciu"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletă"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1669,7 +1673,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Corecția culorii"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Comanda rapidă de accesibilitate a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Comanda rapidă de accesibilitate a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Folosiți din nou comanda rapidă Accesibilitate pentru a porni funcția de accesibilitate prezentă"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Alegeți o funcție pe care să o folosiți când atingeți butonul Accesibilitate:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pentru a schimba funcțiile, atingeți lung butonul Accesibilitate."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Mărire"</string>
@@ -2010,5 +2014,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Cameră foto"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Microfon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"se afișează peste alte aplicații de pe ecran"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Se încarcă"</string>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ac7908c..fde1c5d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Открыть с помощью приложения:"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Открыть с помощью приложения \"%1$s\""</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Открыть"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Предоставьте доступ, чтобы открывать ссылки <xliff:g id="HOST">%1$s</xliff:g> в приложении"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Предоставьте доступ, чтобы открывать ссылки <xliff:g id="HOST">%1$s</xliff:g> в приложении <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Открыть доступ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Редактировать с помощью приложения:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Редактировать с помощью приложения \"%1$s\""</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Изменить"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Ответить?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Всегда"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Только сейчас"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Настройки"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s не поддерживает рабочие профили"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшетный ПК"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевизор"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Коррекция цвета"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Сервис <xliff:g id="SERVICE_NAME">%1$s</xliff:g> включен"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Сервис <xliff:g id="SERVICE_NAME">%1$s</xliff:g> отключен"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Чтобы активировать выбранные специальные возможности, воспользуйтесь быстрым включением ещё раз"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Чтобы использовать сервис \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", нажмите и удерживайте обе клавиши громкости в течение трех секунд."</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Выберите функцию, которая запускается при нажатии кнопки специальных возможностей:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Чтобы изменить функцию, удерживайте кнопку специальных возможностей."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Увеличение"</string>
@@ -2045,5 +2049,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Микрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"показ поверх других окон"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Загрузка"</string>
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 6486f3a..baa0387 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1130,6 +1130,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"සමඟ විවෘත කරන්න"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s සමඟ විවෘත කරන්න"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"විවෘත කරන්න"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"මේවා මඟින් <xliff:g id="HOST">%1$s</xliff:g> සබැඳි විවෘත කිරීමට ප්‍රවේශය දෙන්න"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> මඟින් <xliff:g id="HOST">%1$s</xliff:g> සබැඳි විවෘත කිරීමට ප්‍රවේශය දෙන්න"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ප්‍රවේශය දෙන්න"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"සමඟ සංස්කරණය කරන්න"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s සමඟ සංස්කරණය කරන්න"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"සංස්කරණය"</string>
@@ -1568,6 +1571,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"ඇමතුම පිළිගන්නවාද?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"සැම විටම"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"එක් වාරයයි"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"සැකසීම්"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s කාර්යාල පැතිකඩ සඳහා සහාය ලබනොදේ."</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ටැබ්ලට්ය"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"රූපවාහිනී"</string>
@@ -1647,7 +1651,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"වර්ණ නිවැරදි කිරීම"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ප්‍රවේශ්‍යතා කෙටි මග <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාත්මක කරන ලදී"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ප්‍රවේශ්‍යතා කෙටි මග <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාවිරහිත කරන ලදී"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"වර්තමාන ප්‍රවේශ්‍යතා විශේෂංගය ආරම්භ කිරීම සඳහා ප්‍රවේශ්‍යතා කෙටි මග භාවිතා කරන්න"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> භාවිත කිරීමට හඬ පරිමා යතුරු දෙකම තත්පර තුනකට ඔබාගෙන සිටින්න"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ඔබ ප්‍රවේශ්‍යතා බොත්තම තට්ටු කරන විට භාවිතා කිරීමට අංගයක් තෝරාගන්න:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"අංග වෙනස් කිරීමට ප්‍රවේශ්‍යතා බොත්තම ස්පර්ශ කර අල්ලා ගෙන සිටින්න."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"විශාලනය"</string>
@@ -1977,5 +1981,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"කැමරාව"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"මයික්‍රෆෝනය"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ඔබගේ තිරය මත වෙනත් යෙදුම්වලට උඩින් සංදර්ශනය කරමින්"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"දිනචරියා ප්‍රකාර තතු දැනුම්දීම"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"බැටරිය සුපුරුදු ආරෝපණයට පෙර ඉවර විය හැක"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"බැටරි සුරැකුම බැටරි ආයු කාලය දීර්ඝ කිරීමට සක්‍රිය කෙරිණි"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"පූරණය කරමින්"</string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 66d6ef6..2942537 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Otvoriť v aplikácii"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Otvoriť v aplikácii %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Otvoriť"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Udeľte prístup na otváranie odkazov <xliff:g id="HOST">%1$s</xliff:g> pomocou aplikácie"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Udeľte prístup na otváranie odkazov <xliff:g id="HOST">%1$s</xliff:g> pomocou aplikácie <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Udeliť prístup"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Upraviť pomocou"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Upraviť v aplikácii %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Upraviť"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Prijať hovor?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Vždy"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Len raz"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Nastavenia"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"Spúšťač %1$s nepodporuje pracovné profily"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televízor"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Úprava farieb"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Skratka dostupnosti zapla službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Skratka dostupnosti vypla službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Znova použite skratku dostupnosti, čím sprístupníte aktuálnu funkciu dostupnosti"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Ak chcete používať službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, pridržte tri sekundy oba klávesy hlasitosti"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Klepnutím na tlačidlo dostupnosti vyberte požadovanú funkciu:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Ak chcete zmeniť funkcie, klepnite na tlačidlo dostupnosti a podržte ho"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Priblíženie"</string>
@@ -2045,5 +2049,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Fotoaparát"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofón"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"sa zobrazuje cez ďalšie aplikácie na obrazovke"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Načítava sa"</string>
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index dcaa4f5..d3f0872 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Odpiranje z aplikacijo"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Odpiranje z aplikacijo %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Odpiranje"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Omogočanje dostopa za odpiranje povezav <xliff:g id="HOST">%1$s</xliff:g> z aplikacijo"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Omogočanje dostopa za odpiranje povezav <xliff:g id="HOST">%1$s</xliff:g> z aplikacijo <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Omogoči dostop"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Urejanje z aplikacijo"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Urejanje z aplikacijo %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Urejanje"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Ali želite sprejeti klic?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Vedno"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Samo tokrat"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Nastavitve"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ne podpira delovnega profila"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablični računalnik"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televizor"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Popravljanje barv"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Bližnjica funkcij za ljudi s posebnimi potrebami je vklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Bližnjica funkcij za ljudi s posebnimi potrebami je izklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Če želite zagnati trenutno funkcijo za ljudi s posebnimi potrebami, znova uporabite bližnjico funkcij za ljudi s posebnimi potrebami"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Za uporabo storitve <xliff:g id="SERVICE_NAME">%1$s</xliff:g> pritisnite obe tipki za glasnost in ju pridržite tri sekunde"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izberite funkcijo, ki jo želite uporabljati, ko se dotaknete gumba »Dostopnost«:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Če želite spremeniti funkcije, se dotaknite gumba »Dostopnost« in ga pridržite."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Povečava"</string>
@@ -2045,5 +2049,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Fotoaparat"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"prekriva druge aplikacije na zaslonu"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Nalaganje"</string>
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 413ff8a..89afb1a 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Hap me"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Hap me %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Hap"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Jep qasje për të hapur lidhjet e <xliff:g id="HOST">%1$s</xliff:g> me"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Jep qasje për të hapur lidhjet e <xliff:g id="HOST">%1$s</xliff:g> me <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Jep qasje"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Redakto me"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Redakto me %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Redakto"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Dëshiron ta pranosh telefonatën?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Gjithmonë"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Vetëm një herë"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Cilësimet"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s nuk e mbështet profilin e punës"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televizori"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Korrigjimi i ngjyrës"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Shkurtorja e qasshmërisë e aktivizoi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Shkurtorja e qasshmërisë e çaktivizoi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Përdor përsëri \"Shkurtoren e qasshmërisë\" për të nisur funksionin aktual të qasshmërisë"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Zgjidh një funksion për ta përdorur kur troket butonin e \"Qasshmërisë\":"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Për të ndryshuar funksionet, prek dhe mbaj butonin e \"Qasshmërisë\"."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Zmadhimi"</string>
@@ -1976,5 +1980,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofoni"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"po shfaqet mbi aplikacionet e tjera në ekranin tënd"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Njoftimi i informacionit të \"Modalitetit rutinë\""</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Bateria mund të mbarojë përpara ngarkimit të zakonshëm"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"\"Kursyesi i baterisë\" u aktivizua për të zgjatur jetëgjatësinë e baterisë"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Po ngarkohet"</string>
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8f8fe0e..19f88cf 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1148,6 +1148,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Отворите помоћу"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Отворите помоћу апликације %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Отвори"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Дозволите да се линкови <xliff:g id="HOST">%1$s</xliff:g> отварају помоћу"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Дозволите да <xliff:g id="APPLICATION">%2$s</xliff:g> отвара линикове <xliff:g id="HOST">%1$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Дозволи приступ"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Измените помоћу"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Измените помоћу апликације %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Измени"</string>
@@ -1589,6 +1592,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Желите ли да прихватите позив?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Увек"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Само једном"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Подешавања"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s не подржава пословни профил"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ТВ"</string>
@@ -1669,7 +1673,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Корекција боја"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Пречица за приступачност је укључила услугу <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Пречица за приступачност је искључила услугу <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Употребите поново пречицу за приступачност да бисте покренули актуелну функцију приступачности"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Притисните и задржите оба тастера за јачину звука три секунде да бисте користили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изаберите функцију која ће се користити када додирнете дугме за приступачност:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Притисните и задржите дугме за приступачност да бисте мењали функције."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Увећање"</string>
@@ -2010,5 +2014,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Микрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"приказује се на екрану док користите друге апликације"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Обавештење о информацијама Рутинског режима"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Батерија ће се можда испразнити пре уобичајеног пуњења"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Уштеда батерије је активирана да би се продужило трајање батерије"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Учитава се"</string>
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cd5580d..fcae364 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Öppna med"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Öppna med %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Öppna"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Tillåt att länkar från <xliff:g id="HOST">%1$s</xliff:g> öppnas med"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Tillåt att länkar från <xliff:g id="HOST">%1$s</xliff:g> öppnas med <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Ge åtkomst"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Redigera med"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Redigera med %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Redigera"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vill du ta emot samtal?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Alltid"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Bara en gång"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Inställningar"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s har inte stöd för arbetsprofil"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Surfplatta"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Färgkorrigering"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiverades av Aktivera tillgänglighet snabbt"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> inaktiverades av Aktivera tillgänglighet snabbt"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Använd Aktivera tillgänglighet snabbt en gång till om du vill aktivera tillgänglighetsfunktionen i fråga"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Tryck och håll båda volymknapparna i tre sekunder för att använda <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Välj en funktion som ska användas när du trycker på tillgänglighetsknappen:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Tryck länge på tillgänglighetsknappen för att ändra funktioner."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Förstoring"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"visar över andra appar på mobilen"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Avisering om rutinläge"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Batteriet kan ta slut innan du brukar ladda det"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Batterisparläget har aktiverats för att utöka batteritiden"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Läser in"</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index bc3e4ad..be7c301 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Fungua ukitumia"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Fungua ukitumia %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Fungua"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Ipe <xliff:g id="HOST">%1$s</xliff:g> ruhusa ya kufungua viungo kwa kutumia"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Ipe <xliff:g id="HOST">%1$s</xliff:g> ruhusa ya kufungua viungo kwa kutumia <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Idhinisha ufikiaji"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Badilisha kwa"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Badilisha kwa %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Badilisha"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Kubali simu?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Kila mara"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Mara moja tu"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Mipangilio"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s haitumii wasifu wa kazini"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Kompyuta kibao"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Runinga"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Usahihishaji wa rangi"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Tumia njia ya Mkato wa Zana za walio na matatizo ya kuona au kusikia tena ili kuanzisha kipengele kilichopo cha walio na matatizo ya kuona au kusikia"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Chagua kipengele utakachotumia, ukigonga Kitufe cha zana za walio na matatizo ya kuona au kusikia."</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Ili kubadilisha vipengele, gusa na ushikile Kitufe cha zana za walio na matatizo ya kuona au kusikia."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ukuzaji"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Maikrofoni"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"inachomoza kwenye programu zingine katika skrini yako"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Arifa ya maelezo ya Hali ya Kawaida"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Huenda betri itakwisha chaji mapema"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Imewasha Kiokoa Betri ili kurefusha muda wa matumizi ya betri"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Inapakia"</string>
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 22da7b0..117f071 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"இதன்மூலம் திற"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s மூலம் திற"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"திற"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"மூலம் <xliff:g id="HOST">%1$s</xliff:g> இணைப்புகளைத் திறப்பதற்கான அணுகலை வழங்குதல்"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> மூலம் <xliff:g id="HOST">%1$s</xliff:g> இணைப்புகளைத் திறப்பதற்கான அணுகலை வழங்குதல்"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"அணுகலை வழங்கு"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"இதன் மூலம் திருத்து"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s மூலம் திருத்து"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"மாற்று"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"அழைப்பை ஏற்கவா?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"எப்போதும்"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"இப்போது மட்டும்"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"அமைப்புகள்"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s பணி சுயவிவரத்தை ஆதரிக்காது"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"டேப்லெட்"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"டிவி"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"வண்ணத் திருத்தம்"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"அணுகல்தன்மைக் குறுக்குவழியானது <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐ இயக்கியது"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"அணுகல்தன்மைக் குறுக்குவழியானது <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐ முடக்கியது"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"தற்போதுள்ள அணுகல்தன்மை அம்சத்தை மீண்டும் தொடங்க ’அணுகல்தன்மை ஷார்ட்கட்டைப்’ பயன்படுத்தவும்"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐப் பயன்படுத்த 3 விநாடிகளுக்கு இரண்டு ஒலியளவு பட்டன்களையும் அழுத்திப் பிடிக்கவும்"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"அணுகல்தன்மைப் பொத்தானைத் தட்டி, பயன்படுத்துவதற்கான அம்சத்தைத் தேர்ந்தெடுக்கவும்:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"அம்சங்களை மாற்ற, அணுகல்தன்மைப் பொத்தானைத் தொட்டுப் பிடித்திருக்கவும்."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"பெரிதாக்கல்"</string>
@@ -1976,5 +1980,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"கேமரா"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"மைக்ரோஃபோன்"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"உங்கள் திரையில் உள்ள பிற பயன்பாடுகளின் மேல் காட்டுகிறது"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"வழக்கமான பேட்டரி சேமிப்பானுக்கான விவர அறிவிப்பு"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"வழக்கமாகச் சார்ஜ் செய்வதற்கு முன்பே பேட்டரி தீர்ந்துபோகக்கூடும்"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"பேட்டரி நிலையை நீட்டிக்க பேட்டரி சேமிப்பான் இயக்கப்பட்டுள்ளது"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"ஏற்றுகிறது"</string>
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 1cc0274..654585d 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"దీనితో తెరువు"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$sతో తెరువు"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"తెరువు"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"<xliff:g id="HOST">%1$s</xliff:g> లింక్‌లను తెరవడానికి యాక్సెస్ ఇవ్వండి"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g>తో <xliff:g id="HOST">%1$s</xliff:g> లింక్‌లను తెరవడానికి యాక్సెస్ ఇవ్వండి"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"యాక్సెస్ ఇవ్వండి"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"దీనితో సవరించు"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$sతో సవరించు"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"సవరించు"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"కాల్‌ను ఆమోదించాలా?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ఎల్లప్పుడూ"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"ఒకసారి"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"సెట్టింగ్‌లు"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s కార్యాలయ ప్రొఫైల్‌కు మద్దతు ఇవ్వదు"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"టాబ్లెట్"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"టీవీ"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"రంగు సవరణ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"యాక్సెస్ సామర్థ్య షార్ట్‌కట్ ద్వారా <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"యాక్సెస్ సామర్థ్య షార్ట్‌కట్ ద్వారా <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ప్రస్తుత యాక్సెస్ సౌలభ్య ఫీచర్‌ను ఉపయోగించడానికి యాక్సెసిబిలిటీ షార్ట్‌కట్‌ను మళ్లీ ప్రారంభించండి"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ని ఉపయోగించడానికి వాల్యూమ్ కీలు రెండింటినీ 3 సెకన్లు నొక్కి ఉంచండి"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"యాక్సెస్ సామర్థ్య బటన్‌ను మీరు నొక్కినప్పుడు ఉపయోగించాల్సిన ఒక ఫీచర్‌ను ఎంచుకోండి:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ఫీచర్లను మార్చడానికి, యాక్సెస్ సామర్థ్య బటన్‌ను నొక్కి &amp; పట్టుకోండి."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"మాగ్నిఫికేషన్"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"కెమెరా"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"మైక్రోఫోన్"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"మీ స్క్రీన్‌పై ఇతర యాప్‌ల ద్వారా ప్రదర్శించబడుతోంది"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"లోడవుతోంది"</string>
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 2edf57c..ca8f529 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"เปิดด้วย"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"เปิดด้วย %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"เปิด"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"ให้สิทธิ์ในการเปิดลิงก์ของ <xliff:g id="HOST">%1$s</xliff:g> โดยใช้"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"ให้สิทธิ์ในการเปิดลิงก์ของ <xliff:g id="HOST">%1$s</xliff:g> โดยใช้ <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"ให้สิทธิ์"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"แก้ไขด้วย"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"แก้ไขด้วย %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"แก้ไข"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"รับสายหรือไม่"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ทุกครั้ง"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"เฉพาะครั้งนี้"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"การตั้งค่า"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ไม่สนับสนุนโปรไฟล์งาน"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"แท็บเล็ต"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ทีวี"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"การปรับแก้สี"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ทางลัดการเข้าถึงเปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ทางลัดการเข้าถึงปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ใช้ทางลัดการช่วยเหลือพิเศษอีกครั้งเพื่อเริ่มฟีเจอร์การช่วยเหลือพิเศษปัจจุบัน"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 3 วินาทีเพื่อใช้ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"เลือกฟีเจอร์ที่จะใช้เมื่อคุณแตะปุ่ม \"การเข้าถึงพิเศษ\":"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"หากต้องการเปลี่ยนฟีเจอร์ ให้แตะปุ่ม \"การเข้าถึงพิเศษ\" ค้างไว้"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"การขยาย"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"กล้อง"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"ไมโครโฟน"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"แสดงทับแอปอื่นๆ บนหน้าจอ"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"กำลังโหลด"</string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index f7995f7..31d906f 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Buksan gamit ang"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Buksan gamit ang %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Buksan"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Magbigay ng access para buksan ang mga link ng <xliff:g id="HOST">%1$s</xliff:g> sa"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Magbigay ng access para buksan ang mga link ng <xliff:g id="HOST">%1$s</xliff:g> sa <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Bigyan ng access"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"I-edit gamit ang"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"I-edit gamit ang %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"I-edit"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Tanggapin ang tawag?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Palagi"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Isang beses lang"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Mga Setting"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"Hindi sinusuportahan ng %1$s ang profile sa trabaho"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Pagwawasto ng Kulay"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Na-on ng Shortcut sa Accessibility ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Na-off ng Shortcut sa Accessibility ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gamiting muli ang Shortcut sa Pagiging Naa-access para simulan ang kasalukuyang feature ng pagiging naa-access"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pumili ng feature na gagamitin kapag na-tap mo ang button ng Pagiging Naa-access:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Upang baguhin ang mga feature, pindutin nang matagal ang button ng Pagiging Naa-access."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Pag-magnify"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Camera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikropono"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ipinapakita sa ibabaw ng ibang app sa iyong screen"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Naglo-load"</string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 761eba6..75b19a9 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Şununla aç:"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ile aç"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Aç"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Şununla <xliff:g id="HOST">%1$s</xliff:g> bağlantılarını açma izni verin"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> uygulamasıyla <xliff:g id="HOST">%1$s</xliff:g> bağlantılarını açma izni verin"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Erişim ver"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Şununla düzenle:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ile düzenle"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Düzenle"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Çağrı kabul edilsin mi?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Her zaman"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Yalnızca bir defa"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Ayarlar"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s, iş profilini desteklemiyor"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Renk Düzeltme"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Erişilebilirlik Kısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini açtı"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Erişilebilirlik Kısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kapattı"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Geçerli erişilebilirlik özelliğini başlatmak için Erişilebilirlik Kısayolu\'nu tekrar kullanın"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kullanmak için her iki ses tuşunu basılı tutun"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Erişilebilirlik düğmesine dokunduğunuzda kullanmak üzere bir özellik seçin:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Özellikleri değiştirmek için Erişilebilirlik düğmesine dokunup basılı tutun."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Büyütme"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ekranınızdaki diğer uygulamaların üzerinde görüntüleniyor"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Yükleniyor"</string>
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index e7e99ae..933d04a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1168,6 +1168,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Відкрити за допомогою"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Відкрити за допомогою %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Відкрити"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Дозвольте відкривати посилання на сайт <xliff:g id="HOST">%1$s</xliff:g> у додатку"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Дозвольте відкривати посилання на сайт <xliff:g id="HOST">%1$s</xliff:g> у додатку <xliff:g id="APPLICATION">%2$s</xliff:g>."</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Дозволити"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Редагувати за допомогою"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Редагувати за допомогою %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Редагувати"</string>
@@ -1612,6 +1615,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Прийняти виклик?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Завжди"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Лише цього разу"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Налаштування"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s не підтримує робочий профіль"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшетний ПК"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевізор"</string>
@@ -1693,7 +1697,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Корекція кольорів"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Ярлик спеціальних можливостей увімкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Ярлик спеціальних можливостей вимкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Щоб запустити поточну функцію спеціальних можливостей, знову скористайтеся відповідним ярликом"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Щоб скористатися службою <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, утримуйте обидві клавіші гучності впродовж трьох секунд"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Виберіть функцію для кнопки спеціальних можливостей:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Щоб змінити функцію, натисніть і втримуйте кнопку спеціальних можливостей."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Збільшення"</string>
@@ -2045,5 +2049,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Камера"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Мікрофон"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"показ поверх інших додатків на екрані"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Завантаження"</string>
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index e33f3f2..20f7621 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"اس کے ساتھ کھولیں"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"‏%1$s کے ساتھ کھولیں"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"کھولیں"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"اس کے ساتھ <xliff:g id="HOST">%1$s</xliff:g> لنکس کو کھولنے کے لیے رسائی ديں"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="APPLICATION">%2$s</xliff:g> کے ساتھ <xliff:g id="HOST">%1$s</xliff:g> لنکس کو کھولنے کے لیے رسائی ديں"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"رسائی دیں"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"اس کے ساتھ ترمیم کریں"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"‏%1$s کے ساتھ ترمیم کریں"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ترمیم کریں"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"کال قبول کریں؟"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ہمیشہ"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"بس ایک مرتبہ"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"ترتیبات"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"‏%1$s دفتری پروفائل کا تعاون نہیں کرتا ہے"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ٹیبلیٹ"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"رنگ کی تصحیح"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ایکسیسبیلٹی شارٹ کٹ نے <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آن کر دیا"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ایکسیسبیلٹی شارٹ کٹ نے <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آف کر دیا"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"موجودہ ایکسیسبیلٹی خصوصیت کو شروع کرنے کیلئے دوبارہ ایکسیسبیلٹی شارٹ کٹ استعمال کریں"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> کا استعمال کرنے کے لیے 3 سیکنڈ تک والیوم کی دونوں کلیدوں کو چھوئیں اور دبائے رکھیں"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ایکسیسبیلٹی بٹن پر تھپتھپانے وقت استعمال کرنے کیلئے ایک خصوصیت چنیں:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"خصوصیات تبدیل کرنے کیلئے، ایکسیسبیلٹی بٹن ٹچ کریں اور دبائے رکھیں۔"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"میگنیفکیشن"</string>
@@ -1976,5 +1980,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"کیمرا"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"مائیکروفون"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"آپ کی اسکرین پر دیگر ایپس پر دکھایا جا رہا ہے"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"لوڈ ہو رہا ہے"</string>
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 7742c3e..3f20dc1 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Ochish…"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s bilan ochish"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ochish"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"<xliff:g id="HOST">%1$s</xliff:g> havolalarini ushbu ilova bilan ochishga ruxsat bering:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"<xliff:g id="HOST">%1$s</xliff:g> havolalarini <xliff:g id="APPLICATION">%2$s</xliff:g> bilan ochishga ruxsat bering"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Ruxsat berish"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Tahrirlash…"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"“%1$s” yordamida tahrirlash"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Tahrirlash"</string>
@@ -1567,6 +1570,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Har doim"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Faqat hozir"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Sozlamalar"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"“%1$s” ishchi profilni qo‘llab-quvvatlamaydi"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planshet"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1646,7 +1650,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Rangni tuzatish"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmati yoqildi"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmati o‘chirib qo‘yildi"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Joriy maxsus imkoniyatlar funksiyasini boshlash uchun tezkor ishga tushirishdan qayta foydalaning"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatidan foydalanish uchun ikkala ovoz balandligi tugmalarini uzoq bosib turing"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Maxsus imkoniyatlar tugmasi bosilganda ishga tushadigan funksiyani tanlang:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Funksiyalarni o‘zgartirish uchun Maxsus imkoniyatlar tugmasini bosib turing."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Kattalashtirish"</string>
@@ -1976,5 +1980,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Kamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"ekranda boshqa ilovalar ustidan ochiladi"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Kun tartibi rejimi haqidagi bildirishnoma"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Batareya quvvati odatdagidan ertaroq tugashi mumkin"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Batareya quvvati uzoqroq ishlashi uchun Tejamkor rejim yoqildi"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Yuklanmoqda"</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 86bb532..19c3913 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Mở bằng"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Mở bằng %1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Mở"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Cấp quyền truy cập để mở đường dẫn liên kết <xliff:g id="HOST">%1$s</xliff:g> bằng"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Cấp quyền truy cập để mở đường dẫn liên kết <xliff:g id="HOST">%1$s</xliff:g> bằng <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Cấp quyền truy cập"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Chỉnh sửa bằng"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Chỉnh sửa bằng %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Chỉnh sửa"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Chấp nhận cuộc gọi?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Luôn chọn"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Chỉ một lần"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Cài đặt"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s không hỗ trợ hồ sơ công việc"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Máy tính bảng"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Sửa màu"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Đã bật phím tắt trợ năng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Đã tắt phím tắt trợ năng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Sử dụng lại Phím tắt hỗ trợ tiếp cận để bắt đầu tính năng hỗ trợ tiếp cận hiện tại"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Nhấn và giữ đồng thời cả hai phím âm lượng trong 3 giây để sử dụng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Chọn tính năng sẽ sử dụng khi bạn nhấn nút Trợ năng:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Để thay đổi các tính năng, hãy chạm và giữ nút Trợ năng."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Phóng to"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Máy ảnh"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Micrô"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"hiển thị qua các ứng dụng khác trên màn hình của bạn"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"Đang tải"</string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index dbb7834..6720132 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"打开方式"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"使用%1$s打开"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"打开"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"授权使用以下应用打开 <xliff:g id="HOST">%1$s</xliff:g> 链接:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"授权使用<xliff:g id="APPLICATION">%2$s</xliff:g>打开 <xliff:g id="HOST">%1$s</xliff:g> 链接"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"授予访问权限"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"编辑方式"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"使用%1$s编辑"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"编辑"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"要接听电话吗?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"始终"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"仅此一次"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"设置"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s不支持工作资料"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"平板电脑"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"电视"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"无障碍快捷方式已开启<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"无障碍快捷方式已关闭<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"再次使用无障碍快捷方式即可启动目前设置的无障碍功能"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"同时按住两个音量键 3 秒钟即可使用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"选择按下“无障碍”按钮时要使用的功能:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"要更改指定的功能,请触摸并按住“无障碍”按钮。"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"放大功能"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"相机"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"麦克风"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"显示在屏幕上其他应用的上层"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"正在加载"</string>
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 6248db5..701e266 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"用於開啟的應用程式"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"使用 %1$s 開啟"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"開啟"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"授予存取權以透過以下應用程式開啟 <xliff:g id="HOST">%1$s</xliff:g> 連結:"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"授予存取權以透過<xliff:g id="APPLICATION">%2$s</xliff:g>開啟 <xliff:g id="HOST">%1$s</xliff:g> 連結"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"授予存取權"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"使用以下選擇器編輯:"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"使用 %1$s 編輯"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"編輯"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"接聽電話嗎?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"一律採用"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"只此一次"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"設定"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s 不支援公司檔案"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"平板電腦"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"電視"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"無障礙功能快速鍵已啟用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"無障礙功能快速鍵已停用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"再用一次無障礙功能捷徑,就可以啟用宜家設定咗嘅無障礙功能"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"㩒住兩個音量鍵 3 秒就可以用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"請選擇輕按「無障礙功能」按鈕時使用的功能:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"如要變更功能,可按住「無障礙功能」按鈕。"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"放大"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"相機"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"麥克風"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"顯示在畫面上的其他應用程式上層"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"正在載入"</string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 74da200..24a51bd 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"選擇開啟工具"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"透過 %1$s 開啟"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"開啟"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"授權系統使用以下應用程式開啟 <xliff:g id="HOST">%1$s</xliff:g> 連結"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"授權系統使用「<xliff:g id="APPLICATION">%2$s</xliff:g>」開啟 <xliff:g id="HOST">%1$s</xliff:g> 連結"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"授予存取權"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"選擇編輯工具"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"使用 %1$s 編輯"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"編輯"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"接聽電話嗎?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"一律採用"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"僅限一次"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"設定"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s 不支援工作設定檔"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"平板電腦"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"電視"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"協助工具捷徑啟用了「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"協助工具捷徑停用了「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"再次使用協助工具捷徑即可啟動目前設定的無障礙功能"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"同時按住調低及調高音量鍵三秒即可使用「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"輕觸 [協助工具] 按鈕後,選擇你想使用的功能:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"如要變更指派的功能,請按住 [協助工具] 按鈕。"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"放大"</string>
@@ -1975,5 +1979,11 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"相機"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"麥克風"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"顯示在畫面上的其他應用程式上層"</string>
+    <!-- no translation found for dynamic_mode_notification_channel_name (2348803891571320452) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_title (508815255807182035) -->
+    <skip />
+    <!-- no translation found for dynamic_mode_notification_summary (2541166298550402690) -->
+    <skip />
     <string name="car_loading_profile" msgid="3545132581795684027">"載入中"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 93b2ae6..f819945 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1128,6 +1128,9 @@
     <string name="whichViewApplication" msgid="3272778576700572102">"Vula nge-"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Vula nge-%1$s"</string>
     <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Kuvuliwe"</string>
+    <string name="whichGiveAccessToApplication" msgid="8279395245414707442">"Nika ukufinyelela kuzixhumanisi ezivulekile ze-<xliff:g id="HOST">%1$s</xliff:g> nge-"</string>
+    <string name="whichGiveAccessToApplicationNamed" msgid="7992388824107710849">"Nika ukufinyelela kuzixhumanisi ze-<xliff:g id="HOST">%1$s</xliff:g> ezivulekile nge-<xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+    <string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Nikeza ukufinyel"</string>
     <string name="whichEditApplication" msgid="144727838241402655">"Hlela nge-"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Hlela nge-%1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Hlela"</string>
@@ -1566,6 +1569,7 @@
     <string name="SetupCallDefault" msgid="5834948469253758575">"Amukela ucingo?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Njalo"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Kanye nje"</string>
+    <string name="activity_resolver_app_settings" msgid="8965806928986509855">"Izilungiselelo"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ayisekeli iphrofayela yomsebenzi"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Ithebulethi"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"I-TV"</string>
@@ -1645,7 +1649,7 @@
     <string name="color_correction_feature_name" msgid="6779391426096954933">"Ukulungiswa kombala"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Isinqamuleli sokufinyelela sivule i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Isinqamuleli sokufinyelela sivale i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Sebenzisa isinqamuleli sokufinyelela futhi ukuze uqale isici samanje sokufinyelela"</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Cindezela uphinde ubambe bobabili okhiye bevolumu ngamasekhondi amathathu ukuze usebenzise i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Khetha isici ozosisebenzisa uma uthepha inkinobho yokufinyelela:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Ukuze ushintshe izici, thinta uphinde ubambe inkinobho yokufinyelela."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ukukhuliswa"</string>
@@ -1975,5 +1979,8 @@
     <string name="notification_appops_camera_active" msgid="5050283058419699771">"Ikhamera"</string>
     <string name="notification_appops_microphone_active" msgid="4335305527588191730">"Imakrofoni"</string>
     <string name="notification_appops_overlay_active" msgid="633813008357934729">"iboniswa ngaphezulu kwezinye izinhlelo zokusebenza kusikrini sakho"</string>
+    <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Isaziso solwazi lwe-Routine Mode"</string>
+    <string name="dynamic_mode_notification_title" msgid="508815255807182035">"Ibhethri lingaphela ngaphambi kokushaja okuvamile"</string>
+    <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Isilondolozi sebhethri siyasebenza ngaphandle kwempilo yebhethri"</string>
     <string name="car_loading_profile" msgid="3545132581795684027">"Iyalayisha"</string>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 183c2e8..fa3a549 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3660,7 +3660,12 @@
              the settings for this service. This setting cannot be changed at runtime. -->
         <attr name="settingsActivity" />
         <!-- Attribute whether the accessibility service wants to be able to retrieve the
-             active window content. This setting cannot be changed at runtime. -->
+             active window content. This setting cannot be changed at runtime.
+             <p>
+             Required to allow setting the {@link android.accessibilityservice
+             #AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+             </p>
+         -->
         <attr name="canRetrieveWindowContent" format="boolean" />
         <!-- Attribute whether the accessibility service wants to be able to request touch
              exploration mode in which touched items are spoken aloud and the UI can be
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 54f6c63..b15f72e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -274,6 +274,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the document manager -->
         <flag name="documenter" value="0x40000" />
+        <!-- Additional flag from base permission type: this permission automatically
+            granted to device configurator -->
+        <flag name="configurator" value="0x80000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
@@ -593,7 +596,7 @@
          to be set for all windows of this activity -->
     <attr name="showForAllUsers" format="boolean" />
 
-    <!-- Specifies whether an {@link android.app.Activity} should be shown on top of the the lock screen
+    <!-- Specifies whether an {@link android.app.Activity} should be shown on top of the lock screen
          whenever the lockscreen is up and the activity is resumed. Normally an activity will be
          transitioned to the stopped state if it is started while the lockscreen is up, but with
          this flag set the activity will remain in the resumed state visible on-top of the lock
@@ -2412,6 +2415,19 @@
         <attr name="showForAllUsers" />
 
         <attr name="showWhenLocked" />
+        <!-- @hide @SystemApi Specifies whether this {@link android.app.Activity} should be shown on
+             top of the lock screen whenever the lockscreen is up and this activity has another
+             activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That
+             is, this activity is only visible on the lock screen if there is another activity with
+             the {@link android.R.attr#showWhenLocked} attribute visible at the same time on the
+             lock screen. A use case for this is permission dialogs, that should only be visible on
+             the lock screen if their requesting activity is also visible.
+
+         The default value of this attribute is <code>false</code>. -->
+    <attr name="inheritShowWhenLocked" format="boolean" />
+
+
+        <attr name="inheritShowWhenLocked" />
         <attr name="turnScreenOn" />
 
         <attr name="directBootAware" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e4abf8f..cdf4f69 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -618,15 +618,6 @@
     <!-- Integer indicating wpa_supplicant scan interval when p2p is connected in milliseconds -->
     <integer translatable="false" name="config_wifi_scan_interval_p2p_connected">60000</integer>
 
-    <!-- Integer indicating the framework scan interval in milliseconds. This is used in the scenario
-         where the chipset does not support background scanning (config_wifi_background_scan_suport
-         is false) to set up a periodic wake up scan so that the device can connect to a new access
-         point on the move. A value of 0 means no periodic scans will be used in the framework. -->
-    <integer translatable="false" name="config_wifi_framework_scan_interval">300000</integer>
-
-    <!-- Integer indicating the framework no networks periodic scan interval in milliseconds. -->
-    <integer translatable="false" name="config_wifi_no_network_periodic_scan_interval">300000</integer>
-
     <!-- Integer indicating disconnect mode short scan interval in milliseconds -->
     <integer translatable="false" name="config_wifi_disconnected_short_scan_interval">15000</integer>
 
@@ -692,6 +683,11 @@
     <!-- Wifi driver supports Automatic channel selection (ACS) for softap -->
     <bool translatable="false" name="config_wifi_softap_acs_supported">false</bool>
 
+    <!-- Channel list restriction to Automatic channel selection (ACS) for softap. If the device
+         doesn't want to restrict channels this should be empty. Value is a comma separated channel
+         string and/or channel range string like '1-6,11' -->
+    <string translatable="false" name="config_wifi_softap_acs_supported_channel_list"></string>
+
     <!-- Wifi driver supports IEEE80211AC for softap -->
     <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
 
@@ -699,6 +695,9 @@
          for automotive builds only (the one that have PackageManager#FEATURE_AUTOMOTIVE) -->
     <bool translatable="false" name="config_wifi_local_only_hotspot_5ghz">false</bool>
 
+    <!-- Indicates that connected MAC randomization is supported on this device -->
+    <bool translatable="false" name="config_wifi_connected_mac_randomization_supported">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>
@@ -929,9 +928,6 @@
          in hardware. -->
     <bool name="config_setColorTransformAccelerated">false</bool>
 
-    <!-- Boolean indicating whether display white balance is supported. -->
-    <bool name="config_displayWhiteBalanceAvailable">false</bool>
-
     <!-- Control whether Night display is available. This should only be enabled on devices
          that have a HWC implementation that can apply the matrix passed to setColorTransform
          without impacting power, performance, and app compatibility (e.g. protected content). -->
@@ -987,6 +983,44 @@
         <!-- B y-intercept --> <item>-0.198650895</item>
     </string-array>
 
+    <!-- Boolean indicating whether display white balance is supported. -->
+    <bool name="config_displayWhiteBalanceAvailable">false</bool>
+
+    <!-- Minimum color temperature, in Kelvin, supported by display white balance. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureMin">4000</integer>
+
+    <!-- Maximum color temperature, in Kelvin, supported by display white balance. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureMax">8000</integer>
+
+    <!-- Default color temperature, in Kelvin, used by display white balance. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureDefault">6500</integer>
+
+    <!-- The display primaries, in CIE1931 XYZ color space, for display
+         white balance to use in its calculations. -->
+    <string-array name="config_displayWhiteBalanceDisplayPrimaries">
+        <!-- Red X -->   <item>0.412315</item>
+        <!-- Red Y -->   <item>0.212600</item>
+        <!-- Red Z -->   <item>0.019327</item>
+        <!-- Green X --> <item>0.357600</item>
+        <!-- Green Y --> <item>0.715200</item>
+        <!-- Green Z --> <item>0.119200</item>
+        <!-- Blue X -->  <item>0.180500</item>
+        <!-- Blue Y -->  <item>0.072200</item>
+        <!-- Blue Z -->  <item>0.950633</item>
+        <!-- White X --> <item>0.950456</item>
+        <!-- White Y --> <item>1.000000</item>
+        <!-- White Z --> <item>1.089058</item>
+    </string-array>
+
+    <!-- The nominal white coordinates, in CIE1931 XYZ color space, for Display White Balance to
+         use in its calculations. AWB will adapt this white point to the target ambient white
+         point. -->
+    <string-array name="config_displayWhiteBalanceDisplayNominalWhite">
+        <!-- Nominal White X --> <item>0.950456</item>
+        <!-- Nominal White Y --> <item>1.000000</item>
+        <!-- Nominal White Z --> <item>1.089058</item>
+    </string-array>
+
 
     <!-- Indicate available ColorDisplayController.COLOR_MODE_xxx. -->
     <integer-array name="config_availableColorModes">
@@ -1152,6 +1186,10 @@
          Settings.System.NOTIFICATION_VIBRATION_INTENSITY more details on the constant values and
          meanings. -->
     <integer name="config_defaultNotificationVibrationIntensity">2</integer>
+    <!-- The default intensity level for ring vibrations. See
+         Settings.System.RING_VIBRATION_INTENSITY more details on the constant values and
+         meanings. -->
+    <integer name="config_defaultRingVibrationIntensity">2</integer>
 
     <bool name="config_use_strict_phone_number_comparation">false</bool>
 
@@ -1169,7 +1207,7 @@
     <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
 
     <!-- The app which will handle routine based automatic battery saver, if empty the UI for
-             routine based battery saver will be hidden -->
+         routine based battery saver will be hidden -->
     <string name="config_batterySaverScheduleProvider"></string>
 
     <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
@@ -1404,6 +1442,13 @@
     <integer-array name="config_autoBrightnessLevels">
     </integer-array>
 
+    <!-- Timeout (in milliseconds) after which we remove the effects any user interactions might've
+         had on the brightness mapping. This timeout doesn't start until we transition to a
+         non-interactive display policy so that we don't reset while users are using their devices,
+         but also so that we don't erroneously keep the short-term model if the device is dozing
+         but the display is fully on. -->
+    <integer name="config_autoBrightnessShortTermModelTimeout">300000</integer>
+
     <!-- Array of output values for LCD backlight corresponding to the lux values
          in the config_autoBrightnessLevels array.  This array should have size one greater
          than the size of the config_autoBrightnessLevels array.
@@ -1643,6 +1688,8 @@
          config_enableFusedLocationOverlay is false. -->
     <string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
 
+    <string-array name="config_locationExtraPackageNames" translatable="false"></string-array>
+
     <!-- The package name of the default network recommendation app.
          A network recommendation provider must:
              * Be granted the SCORE_NETWORKS permission.
@@ -1844,6 +1891,8 @@
                cell broadcasting sms, and MMS. -->
     <bool name="config_sms_capable">true</bool>
 
+    <!-- TODO: STOPSHIP(b/110557011): Remove this from framework and overlays as we use
+         config_defaultRoleHolders now. -->
     <!-- Default SMS Application. This will be the default SMS application when
          the phone first boots. The user can then change the default app to one
          of their choosing.
@@ -1862,6 +1911,12 @@
          the behavior will be as though no app was named as an explicit default. -->
     <string name="default_browser" translatable="false"></string>
 
+    <!-- Default role holders. This will be an array of roles and package names of their default
+         holders, with each item in the format of "ROLE_NAME: PACKAGE_NAME_1, PACKAGE_NAME_2". -->
+    <string-array name="config_defaultRoleHolders" translatable="false">
+        <item>android.app.role.SMS: com.android.messaging</item>
+    </string-array>
+
     <!-- Enable/disable default bluetooth profiles:
         HSP_AG, ObexObjectPush, Audio, NAP -->
     <bool name="config_bluetooth_default_profiles">true</bool>
@@ -2157,6 +2212,9 @@
     <!-- Type of the double tap sensor. Empty if double tap is not supported. -->
     <string name="config_dozeDoubleTapSensorType" translatable="false"></string>
 
+    <!-- Type of the tap sensor. Empty if tap is not supported. -->
+    <string name="config_dozeTapSensorType" translatable="false"></string>
+
     <!-- Type of the long press sensor. Empty if long press is not supported. -->
     <string name="config_dozeLongPressSensorType" translatable="false"></string>
 
@@ -2754,6 +2812,9 @@
     <!-- Flag indicating which package name can access the persistent data partition -->
     <string name="config_persistentDataPackageName" translatable="false"></string>
 
+    <!-- Flag indicating which package name can access DeviceConfig table -->
+    <string name="config_deviceConfiguratorPackageName" translatable="false"></string>
+
     <!-- Flag indicating apps will skip sending hold request before merge. In this case
         IMS service implementation will do both.i.e.hold followed by merge. -->
     <bool name="skipHoldBeforeMerge">true</bool>
@@ -3388,7 +3449,7 @@
          See android.view.textclassifier.TextClassificationManager.
     -->
     <string name="config_defaultTextClassifierPackage" translatable="false"></string>
-    
+
     <!-- The package name for the default wellbeing app.
          This package must be trusted, as it has the permissions to control other applications
          on the device.
@@ -3396,6 +3457,12 @@
      -->
     <string name="config_defaultWellbeingPackage" translatable="false"></string>
 
+    <!-- The component name for the default system attention service.
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         See android.attention.AttentionManagerService.
+    -->
+    <string name="config_defaultAttentionService" translatable="false"></string>
+
     <!-- The package name for the system's content capture service.
          This service must be trusted, as it can be activated without explicit consent of the user.
          If no service with the specified name exists on the device, content capture will be
@@ -3412,6 +3479,23 @@
     -->
     <string name="config_defaultAugmentedAutofillService" translatable="false"></string>
 
+    <!-- The package name for the system's app prediction service.
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         Example: "com.android.intelligence/.AppPredictionService"
+    -->
+    <string name="config_defaultAppPredictionService" translatable="false"></string>
+
+    <!-- The package name for the system's content suggestions service.
+         Provides suggestions for text and image selection regions in snapshots of apps and should
+         be able to classify the type of entities in those selections.
+
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         If no service with the specified name exists on the device, content suggestions wil be
+         disabled.
+         Example: "com.android.contentsuggestions/.ContentSuggestionsService"
+    -->
+    <string name="config_defaultContentSuggestionsService" translatable="false"></string>
+
     <!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
     <bool name="config_useDefaultFocusHighlight">true</bool>
 
@@ -3633,7 +3717,7 @@
     <string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string>
 
     <!-- Component name for the default module metadata provider on this device -->
-    <string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string>
+    <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
 
     <!-- This is the default launcher component to use on secondary displays that support system
          decorations.
@@ -3641,4 +3725,17 @@
          set in AndroidManifest.
          {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
     <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
+
+    <!-- If device supports corner radius on windows.
+         This should be turned off on low-end devices to improve animation performance. -->
+    <bool name="config_supportsRoundedCornersOnWindows">true</bool>
+
+    <!-- If the sensor that skips media is available or not. -->
+    <bool name="config_skipSensorAvailable">false</bool>
+
+    <!-- If the sensor that silences alerts is available or not. -->
+    <bool name="config_silenceSensorAvailable">false</bool>
+
+    <!-- Enable Zram writeback feature to allow unused pages in zram be written to flash. -->
+    <bool name="config_zramWriteback">false</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 05a156b..c870683 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -194,9 +194,6 @@
     <!-- The margin for text at the end of the image view for media notifications -->
     <dimen name="notification_media_image_margin_end">72dp</dimen>
 
-    <!-- The additional margin on the sides of the ambient view. -->
-    <dimen name="notification_extra_margin_ambient">16dp</dimen>
-
     <!-- The height of the notification action list -->
     <dimen name="notification_action_list_height">60dp</dimen>
 
@@ -233,9 +230,6 @@
     <!-- The bottom padding for the notification header -->
     <dimen name="notification_header_padding_bottom">16dp</dimen>
 
-    <!-- The margin at the top of the notification header when dozing. -->
-    <dimen name="notification_header_margin_top_ambient">3dp</dimen>
-
     <!-- The margin at the bottom of the notification header. -->
     <dimen name="notification_header_margin_bottom">0dp</dimen>
 
@@ -400,11 +394,6 @@
     <dimen name="notification_title_text_size">14sp</dimen>
     <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) -->
     <dimen name="notification_subtext_size">12sp</dimen>
-    <!-- Size of notification text (see TextAppearance.StatusBar.EventContent) -->
-    <dimen name="notification_ambient_text_size">16sp</dimen>
-    <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) -->
-    <dimen name="notification_ambient_title_text_size">24sp</dimen>
-
     <!-- Top padding for notifications in the standard layout. -->
     <dimen name="notification_top_pad">10dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 799d9d8..777886a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2934,6 +2934,8 @@
         <public name="foregroundServiceType" />
         <public name="hasFragileUserData" />
         <public name="minAspectRatio" />
+        <!-- @hide @SystemApi -->
+        <public name="inheritShowWhenLocked" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
@@ -2983,6 +2985,11 @@
         <public name="system_notification_accent_color" />
     </public-group>
 
+    <public-group type="array" first-id="0x01070006">
+      <!-- @hide @SystemApi -->
+      <public name="config_defaultRoleHolders" />
+    </public-group>
+
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a7bc57a..0878562 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5238,6 +5238,15 @@
     <!-- Content description of the overlay icon in the notification. [CHAR LIMIT=NONE] -->
     <string name="notification_appops_overlay_active">displaying over other apps on your screen</string>
 
+    <!-- Dynamic mode battery saver strings -->
+    <!-- The user visible name of the notification channel for the routine mode battery saver fyi notification [CHAR_LIMIT=80]-->
+    <string name="dynamic_mode_notification_channel_name">Routine Mode info notification</string>
+    <!-- Title of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+    <string name="dynamic_mode_notification_title">Battery may run out before usual charge</string>
+    <!-- Summary of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+    <string name="dynamic_mode_notification_summary">Battery Saver activated to extend battery life</string>
+
+
     <!-- Strings for car -->
     <!-- String displayed when loading a user in the car [CHAR LIMIT=30] -->
     <string name="car_loading_profile">Loading</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 200ef2f..79afe69 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -271,9 +271,6 @@
     <style name="TextAppearance.DeviceDefault.Notification.Info" parent="TextAppearance.Material.Notification.Info">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.Notification.Info.Ambient" parent="TextAppearance.Material.Notification.Info.Ambient">
-        <item name="fontFamily">@string/config_bodyFontFamily</item>
-    </style>
     <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 5a7199d..63ac0e6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -487,10 +487,6 @@
 
     <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" />
 
-    <style name="TextAppearance.Material.Notification.Info.Ambient">
-        <item name="textSize">@dimen/notification_text_size</item>
-    </style>
-
     <style name="TextAppearance.Material.Notification.Emphasis">
         <item name="textColor">#66000000</item>
     </style>
@@ -1308,10 +1304,5 @@
         <item name="gravity">top</item>
     </style>
 
-    <style name="Notification.Header.Ambient">
-        <item name="layout_marginTop">@dimen/notification_header_margin_top_ambient</item>
-        <item name="gravity">top|center_horizontal</item>
-    </style>
-
 
 </resources>
diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
deleted file mode 100644
index 5a9d3e6..0000000
--- a/core/res/res/values/styles_permission_controller.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2018 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-    <!-- styles for the permission grant dialog. -->
-    <style name="PermissionGrantDialog">
-        <item name="background">?attr/windowBackground</item>
-        <item name="elevation">?attr/windowElevation</item>
-        <item name="layout_weight">@dimen/permissionGrantDialogWeight</item>
-        <item name="layout_width">@dimen/permissionGrantDialogWidth</item>
-    </style>
-
-    <style name="PermissionGrantTitleIcon">
-        <item name="layout_width">24dp</item>
-        <item name="layout_height">24dp</item>
-        <item name="layout_marginBottom">12dp</item>
-        <item name="tint">?attr/colorAccent</item>
-        <item name="scaleType">fitCenter</item>
-    </style>
-
-    <style name="PermissionGrantTitleMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="gravity">center</item>
-        <item name="textSize">20sp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-    </style>
-
-    <style name="PermissionGrantIndex"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingEnd">12dp</item>
-        <item name="singleLine">true</item>
-        <item name="textColor">?attr/textColorSecondary</item>
-    </style>
-
-    <style name="PermissionGrantDescription">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionGrantContent">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionGrantDetailMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="layout_marginTop">18dp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-        <item name="textSize">16sp</item>
-    </style>
-
-    <!-- styles for the permission review screen. -->
-    <style name="PermissionReviewDescription">
-        <item name="layout_marginTop">20dp</item>
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginBottom">16dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionReviewTitleIcon">
-        <item name="layout_marginTop">4dp</item>
-        <item name="layout_width">36dp</item>
-        <item name="layout_height">36dp</item>
-        <item name="scaleType">fitCenter</item>
-    </style>
-
-    <style name="PermissionReviewTitleMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingStart">22dp</item>
-        <item name="textSize">20sp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-    </style>
-
-    <style name="PermissionReviewSettings">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginEnd">8dp</item>
-    </style>
-
-    <style name="PermissionReviewButtonBar">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">16dp</item>
-        <item name="layout_marginBottom">4dp</item>
-    </style>
-</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e8cbf66..8427e72 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -325,6 +325,7 @@
   <java-symbol type="bool" name="config_forceDefaultOrientation" />
   <java-symbol type="bool" name="config_wifi_batched_scan_supported" />
   <java-symbol type="bool" name="config_wifi_softap_acs_supported" />
+  <java-symbol type="string" name="config_wifi_softap_acs_supported_channel_list" />
   <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" />
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
@@ -458,9 +459,7 @@
   <java-symbol type="integer" name="config_toastDefaultGravity" />
   <java-symbol type="integer" name="config_triplePressOnPowerBehavior" />
   <java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
-  <java-symbol type="integer" name="config_wifi_framework_scan_interval" />
   <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
-  <java-symbol type="integer" name="config_wifi_no_network_periodic_scan_interval" />
   <java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" />
   <java-symbol type="integer" name="config_windowOutsetBottom" />
   <java-symbol type="integer" name="db_connection_pool_size" />
@@ -1840,6 +1839,7 @@
   <java-symbol type="array" name="radioAttributes" />
   <java-symbol type="array" name="config_oemUsbModeOverride" />
   <java-symbol type="array" name="config_locationProviderPackageNames" />
+  <java-symbol type="array" name="config_locationExtraPackageNames" />
   <java-symbol type="array" name="config_testLocationProviders" />
   <java-symbol type="array" name="config_defaultNotificationVibePattern" />
   <java-symbol type="array" name="config_notificationFallbackVibePattern" />
@@ -1879,6 +1879,7 @@
   <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_connected_mac_randomization_supported" />
   <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" />
@@ -1964,6 +1965,7 @@
   <java-symbol type="integer" name="config_screenBrightnessDark" />
   <java-symbol type="integer" name="config_screenBrightnessDim" />
   <java-symbol type="integer" name="config_screenBrightnessDoze" />
+  <java-symbol type="integer" name="config_autoBrightnessShortTermModelTimeout" />
   <java-symbol type="integer" name="config_shutdownBatteryTemperature" />
   <java-symbol type="integer" name="config_undockedHdmiRotation" />
   <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
@@ -2138,6 +2140,7 @@
   <java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
   <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
   <java-symbol type="string" name="config_persistentDataPackageName" />
+  <java-symbol type="string" name="config_deviceConfiguratorPackageName" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
@@ -3000,6 +3003,7 @@
   <java-symbol type="array" name="config_emergency_mcc_codes" />
 
   <java-symbol type="string" name="config_dozeDoubleTapSensorType" />
+  <java-symbol type="string" name="config_dozeTapSensorType" />
   <java-symbol type="bool" name="config_dozePulsePickup" />
 
   <!-- Used for MimeIconUtils. -->
@@ -3041,6 +3045,13 @@
   <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
   <java-symbol type="array" name="config_availableColorModes" />
 
+  <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMin" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMax" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureDefault" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDisplayPrimaries" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" />
+
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
 
@@ -3065,8 +3076,6 @@
 
   <java-symbol type="dimen" name="config_appTransitionAnimationDurationScaleDefault" />
 
-  <java-symbol type="layout" name="notification_template_material_ambient" />
-
   <!-- Network Recommendation -->
   <java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" />
 
@@ -3226,8 +3235,6 @@
   <java-symbol type="string" name="time_picker_text_input_mode_description"/>
   <java-symbol type="string" name="time_picker_radial_mode_description"/>
 
-  <java-symbol type="layout" name="notification_template_ambient_header" />
-
   <!-- resolver activity -->
   <java-symbol type="drawable" name="resolver_icon_placeholder" />
 
@@ -3274,6 +3281,9 @@
   <java-symbol type="string" name="config_defaultWellbeingPackage" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
   <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
+  <java-symbol type="string" name="config_defaultAppPredictionService" />
+  <java-symbol type="string" name="config_defaultContentSuggestionsService" />
+  <java-symbol type="string" name="config_defaultAttentionService" />
 
   <java-symbol type="string" name="notification_channel_foreground_service" />
   <java-symbol type="string" name="foreground_service_app_in_background" />
@@ -3488,6 +3498,7 @@
 
   <java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
   <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
+  <java-symbol type="integer" name="config_defaultRingVibrationIntensity" />
 
   <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
 
@@ -3514,9 +3525,20 @@
   <java-symbol type="dimen" name="rounded_corner_radius" />
   <java-symbol type="dimen" name="rounded_corner_radius_top" />
   <java-symbol type="dimen" name="rounded_corner_radius_bottom" />
+  <java-symbol type="bool" name="config_supportsRoundedCornersOnWindows" />
 
   <java-symbol type="string" name="config_defaultModuleMetadataProvider" />
 
   <!-- For Secondary Launcher -->
   <java-symbol type="string" name="config_secondaryHomeComponent" />
+  
+  <java-symbol type="string" name="dynamic_mode_notification_channel_name" />
+  <java-symbol type="string" name="dynamic_mode_notification_title" />
+  <java-symbol type="string" name="dynamic_mode_notification_summary" />
+  <java-symbol type="drawable" name="ic_battery" />
+
+  <java-symbol type="bool" name="config_skipSensorAvailable" />
+  <java-symbol type="bool" name="config_silenceSensorAvailable" />
+
+  <java-symbol type="bool" name="config_zramWriteback" />
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0f4ca66..75a727b 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1454,12 +1454,17 @@
         <item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item>
 
         <!-- action bar -->
+        <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
         <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
         <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
 
         <!-- Color palette -->
+        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorPrimary">@color/primary_device_default_settings_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
+        <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorEdgeEffect">@android:color/black</item>
 
         <!-- Add white nav bar with divider that matches material -->
@@ -1467,9 +1472,16 @@
         <item name="navigationBarColor">@android:color/white</item>
         <item name="windowLightNavigationBar">true</item>
 
+        <!-- Dialog attributes -->
+        <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
+        <!-- Progress bar attributes -->
+        <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
         <item name="listDivider">@color/list_divider_color_light</item>
     </style>
 
@@ -1699,8 +1711,4 @@
         <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info</item>
     </style>
 
-    <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient">
-        <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info.Ambient</item>
-    </style>
-
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index ccaf041..6b7698e 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1341,14 +1341,6 @@
         <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item>
     </style>
 
-    <!-- Theme for inflating ambient notification -->
-    <style name="Theme.Material.Notification.Ambient">
-        <item name="notificationHeaderStyle">@style/Notification.Header.Ambient</item>
-        <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item>
-        <item name="notificationHeaderAppNameVisibility">gone</item>
-        <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item>
-    </style>
-
     <!-- Default theme for Settings and activities launched from Settings. -->
     <style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar">
         <item name="homeAsUpIndicator">@drawable/ic_ab_back_material_settings</item>
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
deleted file mode 100644
index 205c4eb..0000000
--- a/core/res/res/values/themes_permission_controller.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2018 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-    <!-- themes for the permission grant dialog. -->
-    <style name="Theme.DeviceDefault.PermissionGrantApp"
-           parent="@style/Theme.DeviceDefault.Light.Panel">
-        <item name="windowIsFloating">false</item>
-        <item name="windowTranslucentStatus">true</item>
-        <item name="backgroundDimEnabled">true</item>
-        <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
-    </style>
-
-    <style name="Theme.DeviceDefault.PermissionGrant"
-           parent="@style/Theme.DeviceDefault.Light.Dialog">
-        <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-    </style>
-
-    <!-- themes for the permission review dialog. -->
-    <style name="Theme.DeviceDefault.PermissionReviewApp"
-           parent="@style/Theme.DeviceDefault.Settings">
-        <item name="windowActionBar">false</item>
-        <item name="windowNoTitle">true</item>
-        <item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
-    </style>
-</resources>
diff --git a/core/tests/BroadcastRadioTests/Android.mk b/core/tests/BroadcastRadioTests/Android.mk
index 24f0cf0..6b0484e 100644
--- a/core/tests/BroadcastRadioTests/Android.mk
+++ b/core/tests/BroadcastRadioTests/Android.mk
@@ -25,7 +25,7 @@
 # LOCAL_SDK_VERSION := current
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test testng
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util androidx.test.rules testng
 
 LOCAL_JAVA_LIBRARIES := android.test.base
 
diff --git a/core/tests/BroadcastRadioTests/AndroidManifest.xml b/core/tests/BroadcastRadioTests/AndroidManifest.xml
index d9b5522..ce12cc9 100644
--- a/core/tests/BroadcastRadioTests/AndroidManifest.xml
+++ b/core/tests/BroadcastRadioTests/AndroidManifest.xml
@@ -24,7 +24,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.hardware.radio.tests"
         android:label="Tests for Broadcast Radio APIs" >
     </instrumentation>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
index fdaba08..d2bd1e1 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
@@ -15,22 +15,29 @@
  */
 package android.hardware.radio.tests.functional;
 
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.testng.Assert.assertThrows;
+
 import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioTuner;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -41,19 +48,10 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import static org.junit.Assert.*;
-import static org.junit.Assume.*;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.testng.Assert.assertThrows;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * A test for broadcast radio API.
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 041fb7e..0fc3bd2 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,14 +36,15 @@
     frameworks-core-util-lib \
     mockwebserver \
     guava \
-    android-support-test \
+    androidx.test.runner \
+    androidx.test.rules \
     mockito-target-minus-junit4 \
     espresso-core \
     ub-uiautomator \
     platform-test-annotations \
-    compatibility-device-util \
     truth-prebuilt \
-    print-test-util-lib
+    print-test-util-lib \
+    testng # TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 46d4a47..86818c6 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -56,6 +56,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.READ_DREAM_STATE" />
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
     <uses-permission android:name="android.permission.READ_LOGS"/>
@@ -65,6 +66,7 @@
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
@@ -1425,7 +1427,7 @@
 
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.frameworks.coretests"
             android:label="Frameworks Core Tests" />
     <key-sets>
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 68ef34b..b40aa87 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -25,6 +25,7 @@
     <option name="test-tag" value="FrameworksCoreTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.coretests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/core/tests/coretests/README b/core/tests/coretests/README
index ea282a0..34beb45 100644
--- a/core/tests/coretests/README
+++ b/core/tests/coretests/README
@@ -30,9 +30,9 @@
 
   adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
   adb shell am instrument -w \
-    com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+    com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
 
-To run a tests within a specific package, add the following argument AFTER -w:
+To run a tests within a specific package, add -e AFTER -w and before the runner class:
 
     -e package android.content.pm
 
diff --git a/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml b/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml
new file mode 100644
index 0000000..8d630b0
--- /dev/null
+++ b/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This drawable is purposely in the color directory to test a backwards compatible fallback. -->
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/icon" android:drawable="@string/app_name" />
+</layer-list>
\ No newline at end of file
diff --git a/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml b/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml
new file mode 100644
index 0000000..d140475
--- /dev/null
+++ b/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This drawable is purposely in the color directory to test a backwards compatible fallback. -->
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background" android:drawable="@drawable/test32x24" />
+    <item android:id="@android:id/icon" android:drawable="@drawable/test16x12" />
+</layer-list>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
index e26bdf5..be1d44c 100644
--- a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
@@ -13,17 +13,17 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+
 package android.animation;
 
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 
+import androidx.test.filters.LargeTest;
+
+import com.android.frameworks.coretests.R;
+
 import java.util.HashSet;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import com.android.frameworks.coretests.R;
 
 @LargeTest
 public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity>  {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivity.java b/core/tests/coretests/src/android/animation/AnimatorSetActivity.java
index 501ea48..af265af 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivity.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivity.java
@@ -1,10 +1,26 @@
-package android.animation;
+/*
+ * 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.
+ */
 
-import com.android.frameworks.coretests.R;
+package android.animation;
 
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class AnimatorSetActivity extends Activity {
     @Override
     public void onCreate(Bundle savedBundleInstance) {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
index 922bc59..55837ba 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
@@ -1,12 +1,29 @@
+/*
+ * 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 android.animation;
 
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.frameworks.coretests.R;
+
 import java.util.ArrayList;
 
 public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
index 7eb32ee..4e90d1a 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
@@ -13,12 +13,14 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+
 package android.animation;
 
 import android.os.Handler;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.Button;
+
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.TimeUnit;
diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java
index b1f88db..b3ec92c 100644
--- a/core/tests/coretests/src/android/animation/AutoCancelTest.java
+++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.util.HashMap;
 import java.util.concurrent.TimeUnit;
diff --git a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
index 0e1e6ac..2b9866d 100644
--- a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
+++ b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
@@ -13,14 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.animation;
 
-import com.android.frameworks.coretests.R;
+package android.animation;
 
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 public class BasicAnimatorActivity extends Activity {
     public Button mAnimatingButton;
     @Override
diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java
index 28cfe3d..ba7413a 100644
--- a/core/tests/coretests/src/android/animation/EventsTest.java
+++ b/core/tests/coretests/src/android/animation/EventsTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
diff --git a/core/tests/coretests/src/android/animation/FutureWaiter.java b/core/tests/coretests/src/android/animation/FutureWaiter.java
index 0c65e20..0c09a4a 100644
--- a/core/tests/coretests/src/android/animation/FutureWaiter.java
+++ b/core/tests/coretests/src/android/animation/FutureWaiter.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import com.google.common.util.concurrent.AbstractFuture;
diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
index 606a939..53f9472 100644
--- a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
@@ -13,9 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.widget.Button;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
index a9961e1..e755b89 100644
--- a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
@@ -14,16 +14,16 @@
 * limitations under the License.
 */
 
-
 package android.animation;
 
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
 import android.util.StateSet;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.atomic.AtomicInteger;
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
index c25d050..f6d71b8 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 /**
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index 4facf77..dee0a3e 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -13,8 +13,11 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+
 package android.animation;
 
+import static android.test.MoreAsserts.assertNotEqual;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -24,23 +27,20 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.Choreographer;
 import android.view.animation.LinearInterpolator;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 
-import static android.test.MoreAsserts.assertNotEqual;
-
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class ValueAnimatorTests {
diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
index 30ec182..997af00 100644
--- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
@@ -13,15 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ViewPropertyAnimator;
 import android.widget.Button;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.TimeUnit;
diff --git a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
index 19a390a..8dc5ad6 100644
--- a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
@@ -16,16 +16,17 @@
 
 package android.app;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.app.ApplicationErrorReport.CrashInfo;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.app.ApplicationErrorReport.CrashInfo;
+
+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 ApplicationErrorReportTest {
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 063bef7..4b0ed65 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -16,13 +16,17 @@
 
 package android.app;
 
+import static android.os.storage.VolumeInfo.STATE_MOUNTED;
+import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
+
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
@@ -32,9 +36,6 @@
 import java.util.Arrays;
 import java.util.List;
 
-import static android.os.storage.VolumeInfo.STATE_MOUNTED;
-import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
-
 @LargeTest
 public class ApplicationPackageManagerTest extends TestCase {
     private static final String sInternalVolPath = "/data";
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index c1d4be0..33e0402 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -22,7 +22,8 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import com.google.mockwebserver.MockResponse;
 
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 39d9a8e..adfe76f 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -23,10 +23,11 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.StatFs;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
diff --git a/core/tests/coretests/src/android/app/InstrumentationTest.java b/core/tests/coretests/src/android/app/InstrumentationTest.java
index 9b59da4..93b5aec 100644
--- a/core/tests/coretests/src/android/app/InstrumentationTest.java
+++ b/core/tests/coretests/src/android/app/InstrumentationTest.java
@@ -17,9 +17,10 @@
 package android.app;
 
 import android.os.Bundle;
-import android.support.test.filters.LargeTest;
 import android.test.InstrumentationTestCase;
 
+import androidx.test.filters.LargeTest;
+
 @LargeTest
 public class InstrumentationTest extends InstrumentationTestCase {
 
diff --git a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
index c83e798..e343383 100644
--- a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
+++ b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package android.app;
 
 import static junit.framework.TestCase.assertNotNull;
@@ -27,11 +26,12 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Parcelable;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e89a4d3..c17aa92 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -33,11 +33,12 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.widget.RemoteViews;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/SearchManagerTest.java b/core/tests/coretests/src/android/app/SearchManagerTest.java
index 08b7f60..14370c8 100644
--- a/core/tests/coretests/src/android/app/SearchManagerTest.java
+++ b/core/tests/coretests/src/android/app/SearchManagerTest.java
@@ -17,17 +17,13 @@
 package android.app;
 
 import android.app.activity.LocalActivity;
-
-import android.app.Activity;
-import android.app.ISearchManager;
-import android.app.SearchManager;
-import android.app.SearchableInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.ServiceManager;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * To launch this test from the command line:
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 61d73bc..bbd442d 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -21,8 +21,9 @@
 import android.content.pm.ConfigurationInfo;
 import android.content.res.Configuration;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
 
 import java.util.Iterator;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 1750dac..9cb3489 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -19,28 +19,36 @@
 import static android.content.Intent.ACTION_EDIT;
 import static android.content.Intent.ACTION_VIEW;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.StopActivityItem;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.IBinder;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.MergedConfiguration;
+import android.view.Display;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+
 /**
  * Test for verifying {@link android.app.ActivityThread} class.
  * Build/Install/Run:
@@ -50,8 +58,12 @@
 @MediumTest
 public class ActivityThreadTest {
 
-    private final ActivityTestRule mActivityTestRule =
-            new ActivityTestRule(TestActivity.class, true /* initialTouchMode */,
+    // The first sequence number to try with. Use a large number to avoid conflicts with the first a
+    // few sequence numbers the framework used to launch the test activity.
+    private static final int BASE_SEQ = 10000;
+
+    private final ActivityTestRule<TestActivity> mActivityTestRule =
+            new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
                     false /* launchActivity */);
 
     @Test
@@ -129,6 +141,179 @@
         });
     }
 
+    @Test
+    public void testHandleActivityConfigurationChanged() {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final int numOfConfig = activity.mNumOfConfigChanges;
+            applyConfigurationChange(activity, BASE_SEQ);
+            assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges);
+        });
+    }
+
+    @Test
+    public void testHandleActivityConfigurationChanged_DropStaleConfigurations() {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // Set the sequence number to BASE_SEQ.
+            applyConfigurationChange(activity, BASE_SEQ);
+
+            final int orientation = activity.mConfig.orientation;
+            final int numOfConfig = activity.mNumOfConfigChanges;
+
+            // Try to apply an old configuration change.
+            applyConfigurationChange(activity, BASE_SEQ - 1);
+            assertEquals(numOfConfig, activity.mNumOfConfigChanges);
+            assertEquals(orientation, activity.mConfig.orientation);
+        });
+    }
+
+    @Test
+    public void testHandleActivityConfigurationChanged_ApplyNewConfigurations() {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // Set the sequence number to BASE_SEQ and record the final sequence number it used.
+            final int seq = applyConfigurationChange(activity, BASE_SEQ);
+
+            final int orientation = activity.mConfig.orientation;
+            final int numOfConfig = activity.mNumOfConfigChanges;
+
+            // Try to apply an new configuration change.
+            applyConfigurationChange(activity, seq + 1);
+            assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges);
+            assertNotEquals(orientation, activity.mConfig.orientation);
+        });
+    }
+
+    @Test
+    public void testHandleActivityConfigurationChanged_PickNewerPendingConfiguration() {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // Set the sequence number to BASE_SEQ and record the final sequence number it used.
+            final int seq = applyConfigurationChange(activity, BASE_SEQ);
+
+            final int orientation = activity.mConfig.orientation;
+            final int numOfConfig = activity.mNumOfConfigChanges;
+
+            final ActivityThread activityThread = activity.getActivityThread();
+
+            final Configuration pendingConfig = new Configuration();
+            pendingConfig.orientation = orientation == Configuration.ORIENTATION_LANDSCAPE
+                    ? Configuration.ORIENTATION_PORTRAIT
+                    : Configuration.ORIENTATION_LANDSCAPE;
+            pendingConfig.seq = seq + 2;
+            activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
+                    pendingConfig);
+
+            final Configuration newConfig = new Configuration();
+            newConfig.orientation = orientation;
+            newConfig.seq = seq + 1;
+
+            activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+                    newConfig, Display.INVALID_DISPLAY);
+            assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges);
+            assertEquals(pendingConfig.orientation, activity.mConfig.orientation);
+        });
+    }
+
+    @Test
+    public void testHandleActivityConfigurationChanged_OnlyAppliesNewestConfiguration()
+            throws Exception {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+        final ActivityThread activityThread = activity.getActivityThread();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final Configuration config = new Configuration();
+            config.seq = BASE_SEQ;
+            config.orientation = Configuration.ORIENTATION_PORTRAIT;
+
+            activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+                    config, Display.INVALID_DISPLAY);
+        });
+
+        final int numOfConfig = activity.mNumOfConfigChanges;
+        final IApplicationThread appThread = activityThread.getApplicationThread();
+
+        activity.mConfigLatch = new CountDownLatch(1);
+        activity.mTestLatch = new CountDownLatch(1);
+
+        Configuration config = new Configuration();
+        config.seq = BASE_SEQ + 1;
+        config.smallestScreenWidthDp = 100;
+        appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+        // Wait until the main thread is performing the configuration change for the configuration
+        // with sequence number BASE_SEQ + 1 before proceeding. This is to mimic the situation where
+        // the activity takes very long time to process configuration changes.
+        activity.mTestLatch.await();
+
+        config = new Configuration();
+        config.seq = BASE_SEQ + 2;
+        config.smallestScreenWidthDp = 200;
+        appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+        config = new Configuration();
+        config.seq = BASE_SEQ + 3;
+        config.smallestScreenWidthDp = 300;
+        appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+        config = new Configuration();
+        config.seq = BASE_SEQ + 4;
+        config.smallestScreenWidthDp = 400;
+        appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+        activity.mConfigLatch.countDown();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        activity.mConfigLatch = null;
+        activity.mTestLatch = null;
+
+        // Only two more configuration changes: one with seq BASE_SEQ + 1; another with seq
+        // BASE_SEQ + 4. Configurations scheduled in between should be dropped.
+        assertEquals(numOfConfig + 2, activity.mNumOfConfigChanges);
+        assertEquals(400, activity.mConfig.smallestScreenWidthDp);
+    }
+
+    /**
+     * Calls {@link ActivityThread#handleActivityConfigurationChanged(IBinder, Configuration, int)}
+     * to try to push activity configuration to the activity for the given sequence number.
+     * <p>
+     * It uses orientation to push the configuration and it tries a different orientation if the
+     * first attempt doesn't make through, to rule out the possibility that the previous
+     * configuration already has the same orientation.
+     *
+     * @param activity the test target activity
+     * @param seq the specified sequence number
+     * @return the sequence number this method tried with the last time, so that the caller can use
+     * the next sequence number for next configuration update.
+     */
+    private int applyConfigurationChange(TestActivity activity, int seq) {
+        final ActivityThread activityThread = activity.getActivityThread();
+
+        final int numOfConfig = activity.mNumOfConfigChanges;
+        Configuration config = new Configuration();
+        config.orientation = Configuration.ORIENTATION_PORTRAIT;
+        config.seq = seq;
+        activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config,
+                Display.INVALID_DISPLAY);
+
+        if (activity.mNumOfConfigChanges > numOfConfig) {
+            return config.seq;
+        }
+
+        config = new Configuration();
+        config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        config.seq = seq + 1;
+        activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config,
+                Display.INVALID_DISPLAY);
+
+        return config.seq;
+    }
+
     private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
         final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
                 null, 0, new MergedConfiguration(), false /* preserveWindow */);
@@ -162,6 +347,16 @@
         return transaction;
     }
 
+    private static ClientTransaction newActivityConfigTransaction(Activity activity,
+            Configuration config) {
+        final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(config);
+
+        final ClientTransaction transaction = newTransaction(activity);
+        transaction.addCallback(item);
+
+        return transaction;
+    }
+
     private static ClientTransaction newTransaction(Activity activity) {
         final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
         return ClientTransaction.obtain(appThread, activity.getActivityToken());
@@ -169,5 +364,37 @@
 
     // Test activity
     public static class TestActivity extends Activity {
+        int mNumOfConfigChanges;
+        final Configuration mConfig = new Configuration();
+
+        /**
+         * A latch used to notify tests that we're about to wait for configuration latch. This
+         * is used to notify test code that preExecute phase for activity configuration change
+         * transaction has passed.
+         */
+        volatile CountDownLatch mTestLatch;
+        /**
+         * If not {@code null} {@link #onConfigurationChanged(Configuration)} won't return until the
+         * latch reaches 0.
+         */
+        volatile CountDownLatch mConfigLatch;
+
+        @Override
+        public void onConfigurationChanged(Configuration config) {
+            super.onConfigurationChanged(config);
+            mConfig.setTo(config);
+            ++mNumOfConfigChanges;
+
+            if (mConfigLatch != null) {
+                if (mTestLatch != null) {
+                    mTestLatch.countDown();
+                }
+                try {
+                    mConfigLatch.await();
+                } catch (InterruptedException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 13e70eb..0f81896 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -27,11 +27,10 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
-import android.test.FlakyTest;
 import android.util.Log;
 
-import java.util.Arrays;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class BroadcastTest extends ActivityTestsBase {
@@ -231,7 +230,7 @@
     };
 
     // Mark flaky until http://b/issue?id=1191607 is resolved.
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testRegistered() throws Exception {
         runLaunchpad(LaunchpadActivity.BROADCAST_REGISTERED);
     }
@@ -248,12 +247,12 @@
         runLaunchpad(LaunchpadActivity.BROADCAST_ABORT);
     }
 
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testAll() throws Exception {
         runLaunchpad(LaunchpadActivity.BROADCAST_ALL);
     }
 
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testMulti() throws Exception {
         runLaunchpad(LaunchpadActivity.BROADCAST_MULTI);
     }
@@ -348,7 +347,7 @@
     }
 
     // Marking flaky until http://b/issue?id=1191337 is resolved
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testReceiveSticky() throws Exception {
         Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
         intent.putExtra("test", LaunchpadActivity.DATA_1);
@@ -358,7 +357,7 @@
     }
 
     // Marking flaky until http://b/issue?id=1191337 is resolved
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testReceive2Sticky() throws Exception {
         Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
         intent.putExtra("test", LaunchpadActivity.DATA_1);
diff --git a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
index 8c1d79b..19ddb52 100644
--- a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
+++ b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
@@ -21,7 +21,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class IntentSenderTest extends BroadcastTest {
diff --git a/core/tests/coretests/src/android/app/activity/LaunchTest.java b/core/tests/coretests/src/android/app/activity/LaunchTest.java
index 5b86dce..6846ea7 100644
--- a/core/tests/coretests/src/android/app/activity/LaunchTest.java
+++ b/core/tests/coretests/src/android/app/activity/LaunchTest.java
@@ -17,8 +17,9 @@
 package android.app.activity;
 
 import android.content.ComponentName;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
 
 @Suppress  // Flaky.
 public class LaunchTest extends ActivityTestsBase {
diff --git a/core/tests/coretests/src/android/app/activity/LifecycleTest.java b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
index ed01fac..5aa0380 100644
--- a/core/tests/coretests/src/android/app/activity/LifecycleTest.java
+++ b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
@@ -18,8 +18,9 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class LifecycleTest extends ActivityTestsBase {
     private Intent mTopIntent;
diff --git a/core/tests/coretests/src/android/app/activity/MetaDataTest.java b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
index 5b9c0e9..cf27878 100644
--- a/core/tests/coretests/src/android/app/activity/MetaDataTest.java
+++ b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
@@ -27,8 +27,11 @@
 import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
index 9f402a5..8184627 100644
--- a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
+++ b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
@@ -1,19 +1,18 @@
-/* //device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java
-**
-** Copyright 2006, 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.
-*/
+/*
+ * Copyright (C) 2006 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.app.activity;
 
@@ -21,7 +20,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Process;
-import android.util.Log;
+
+//device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java
 
 public class RemoteSubActivityScreen extends SubActivityScreen {
     Handler mHandler = new Handler();
diff --git a/core/tests/coretests/src/android/app/activity/ServiceTest.java b/core/tests/coretests/src/android/app/activity/ServiceTest.java
index d3ae415..9d2aebd 100644
--- a/core/tests/coretests/src/android/app/activity/ServiceTest.java
+++ b/core/tests/coretests/src/android/app/activity/ServiceTest.java
@@ -22,13 +22,12 @@
 import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
+import android.os.RemoteException;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 // These test binders purport to support an interface whose canonical
 // interface name is ServiceTest.SERVICE_LOCAL
diff --git a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
index 41b9547..8e17295 100644
--- a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
+++ b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
@@ -19,7 +19,8 @@
 import android.app.AlarmManager;
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import java.util.TimeZone;
 
diff --git a/core/tests/coretests/src/android/app/activity/SubActivityTest.java b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
index 35dde8a..53f89fe 100644
--- a/core/tests/coretests/src/android/app/activity/SubActivityTest.java
+++ b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
@@ -16,9 +16,10 @@
 
 package android.app.activity;
 
-import android.test.suitebuilder.annotation.Suppress;
 import android.content.ComponentName;
 
+import androidx.test.filters.Suppress;
+
 @Suppress
 public class SubActivityTest extends ActivityTestsBase {
 
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index 9b5b725..8d42c74 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -27,8 +27,9 @@
 
 import android.app.admin.PasswordMetrics.PasswordComplexityBucket;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index 689e683..1f4e5df 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.app.assist;
 
 import static android.view.View.AUTOFILL_TYPE_TEXT;
@@ -22,13 +23,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.assist.AssistStructure.ViewNode;
-import android.content.ComponentName;
 import android.content.Context;
 import android.os.Parcel;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.InputFilter;
 import android.util.Log;
 import android.view.autofill.AutofillId;
@@ -37,6 +34,10 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
index f4b6bed..defec43 100644
--- a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
+++ b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.app.assist;
 
 import android.app.Activity;
diff --git a/core/tests/coretests/src/android/app/backup/BackupDataTest.java b/core/tests/coretests/src/android/app/backup/BackupDataTest.java
index 5b8e481..18ff54f 100644
--- a/core/tests/coretests/src/android/app/backup/BackupDataTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupDataTest.java
@@ -16,31 +16,22 @@
 
 package android.app.backup;
 
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
-import android.test.InstrumentationTestCase;
 import android.util.Base64;
-import android.util.Log;
-import org.json.JSONObject;
+
+import androidx.test.filters.LargeTest;
 
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.lang.Exception;
-import java.nio.ByteBuffer;
 
 @LargeTest
 public class BackupDataTest extends AndroidTestCase {
diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
index 5db416b..08edb4e 100644
--- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java
+++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
@@ -18,11 +18,12 @@
 
 import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
 import android.content.Context;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import androidx.test.filters.LargeTest;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlPullParserFactory;
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index b1f8552..52b2658 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -23,8 +23,9 @@
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index fb0f534..ad28d13 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -35,8 +35,9 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index a788a93..f730a24 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -45,10 +45,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 2801f324..8604b0c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -50,8 +50,9 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.app.IVoiceInteractor;
 
diff --git a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
index e20645c..0efc0ab 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
index b69054c..b519bf8 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index dd46240..bb535b6 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -16,14 +16,13 @@
 
 package android.app.timezone;
 
-import static junit.framework.Assert.fail;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
index 4004086..df9ddea 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
@@ -25,7 +25,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
diff --git a/core/tests/coretests/src/android/app/usage/EventListTest.java b/core/tests/coretests/src/android/app/usage/EventListTest.java
index 9dc0d43..685fcae 100644
--- a/core/tests/coretests/src/android/app/usage/EventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/EventListTest.java
@@ -20,10 +20,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index 28aaf1e..1633e1a 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -21,6 +21,7 @@
 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
 import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.END_OF_DAY;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
@@ -33,8 +34,9 @@
 
 import android.app.usage.UsageEvents.Event;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -528,6 +530,11 @@
     }
 
     @Test
+    public void testEvent_DEVICE_SHUTDOWN() {
+        testClosingEvent(DEVICE_SHUTDOWN);
+    }
+
+    @Test
     public void testEvent_FLUSH_TO_DISK() {
         testClosingEvent(FLUSH_TO_DISK);
     }
@@ -535,8 +542,9 @@
     private void testClosingEvent(int eventType) {
         // When these three closing events are received, all open activities/services need to be
         // closed and usage stats are updated.
-        if (eventType != FLUSH_TO_DISK) {
-            fail("Closing eventType must be one of FLUSH_TO_DISK");
+        if (eventType != DEVICE_SHUTDOWN
+                && eventType != FLUSH_TO_DISK) {
+            fail("Closing eventType must be one of DEVICE_SHUTDOWN, FLUSH_TO_DISK");
         }
 
         left.mPackageName = "com.test";
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index 978ea7a..c307e64 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -16,7 +16,6 @@
 
 package android.content;
 
-
 import static org.junit.Assert.fail;
 
 import android.app.ActivityManager;
@@ -30,9 +29,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/content/AssetTest.java b/core/tests/coretests/src/android/content/AssetTest.java
index b66574c..8e55e8a 100644
--- a/core/tests/coretests/src/android/content/AssetTest.java
+++ b/core/tests/coretests/src/android/content/AssetTest.java
@@ -18,7 +18,8 @@
 
 import android.content.res.AssetManager;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java
index 3d246b4..d8c9baa 100644
--- a/core/tests/coretests/src/android/content/BrickDeniedTest.java
+++ b/core/tests/coretests/src/android/content/BrickDeniedTest.java
@@ -17,7 +17,8 @@
 package android.content;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 /** Test to make sure brick intents <b>don't</b> work without permission. */
 public class BrickDeniedTest extends AndroidTestCase {
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 8deccb7..1509ff9 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -18,9 +18,9 @@
 
 import static org.junit.Assert.fail;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/ContentProviderOperationTest.java b/core/tests/coretests/src/android/content/ContentProviderOperationTest.java
index aea124b..b142761 100644
--- a/core/tests/coretests/src/android/content/ContentProviderOperationTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderOperationTest.java
@@ -16,22 +16,23 @@
 
 package android.content;
 
-import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
+
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
-import java.util.Set;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 
 @SmallTest
 public class ContentProviderOperationTest extends TestCase {
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index 2142f27..8895f9b 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -23,7 +23,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/ContentQueryMapTest.java b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
index f47bfdb..7106234 100644
--- a/core/tests/coretests/src/android/content/ContentQueryMapTest.java
+++ b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
@@ -16,16 +16,14 @@
 
 package android.content;
 
-import android.content.ContentQueryMap;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.database.Cursor;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 import java.util.Observable;
 import java.util.Observer;
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 9940bf7..f14f289 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -31,10 +31,11 @@
 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;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/ContentValuesTest.java b/core/tests/coretests/src/android/content/ContentValuesTest.java
index 7b39939..0ab79e7 100644
--- a/core/tests/coretests/src/android/content/ContentValuesTest.java
+++ b/core/tests/coretests/src/android/content/ContentValuesTest.java
@@ -17,8 +17,8 @@
 package android.content;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 
+import androidx.test.filters.SmallTest;
 
 /*
   runtest -c android.content.ContentValuesTest frameworks-core
@@ -29,7 +29,7 @@
     adb shell pm uninstall -k com.android.frameworks.coretests && \
     adb install out/target/product/bullhead/testcases/FrameworksCoreTests/FrameworksCoreTests.apk && \
     adb shell am instrument -w -e package android.content \
-      com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+      com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
 */
 public class ContentValuesTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index c8a3098..2f442c3 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -19,11 +19,12 @@
 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 androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
index 4362ec3..22b2314 100644
--- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
@@ -19,7 +19,8 @@
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 /**
  * To run the tests, use
@@ -32,7 +33,7 @@
  * Install: adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class android.content.ManagedUserContentResolverTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 @LargeTest
 public class ManagedUserContentResolverTest extends AbstractCrossUserContentResolverTest {
diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
index bbe7c10..7cd4862 100644
--- a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
+++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
@@ -18,9 +18,10 @@
 
 import android.net.Uri;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 
 import java.io.InputStream;
 import java.util.Arrays;
diff --git a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
index 10d74f7..fd5de32 100644
--- a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
+++ b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
+
 package android.content;
 
 import android.os.Bundle;
 import android.os.Parcelable;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
index f8b13f0..dbe0278 100644
--- a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
@@ -18,7 +18,8 @@
 
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 /**
  * To run the tests, use
@@ -31,7 +32,7 @@
  * Install: adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class android.content.SecondaryUserContentResolverTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 @LargeTest
 public class SecondaryUserContentResolverTest extends AbstractCrossUserContentResolverTest {
diff --git a/core/tests/coretests/src/android/content/UriMatcherTest.java b/core/tests/coretests/src/android/content/UriMatcherTest.java
index f3b9e76..6cef46b 100644
--- a/core/tests/coretests/src/android/content/UriMatcherTest.java
+++ b/core/tests/coretests/src/android/content/UriMatcherTest.java
@@ -17,14 +17,14 @@
 package android.content;
 
 import android.net.Uri;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 
-
 public class UriMatcherTest extends TestCase {
 
     static final int ROOT = 0;
diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
index 2acb08d..9b360db 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
 
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
index dce22ce..0ed76dc 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
 
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
index 866de93..7f817d6 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 
 import android.content.pm.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/ComponentTest.java b/core/tests/coretests/src/android/content/pm/ComponentTest.java
index cc75641..f31f0b5 100644
--- a/core/tests/coretests/src/android/content/pm/ComponentTest.java
+++ b/core/tests/coretests/src/android/content/pm/ComponentTest.java
@@ -21,7 +21,14 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.PackageManager.GET_DISABLED_COMPONENTS;
 
-import android.test.suitebuilder.annotation.Suppress;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.enabled_app.DisabledActivity;
 import com.android.frameworks.coretests.enabled_app.DisabledProvider;
 import com.android.frameworks.coretests.enabled_app.DisabledReceiver;
@@ -31,12 +38,6 @@
 import com.android.frameworks.coretests.enabled_app.EnabledReceiver;
 import com.android.frameworks.coretests.enabled_app.EnabledService;
 
-import android.content.ComponentName;
-import android.content.Intent;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import java.util.List;
 
 /**
diff --git a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
index 1f762fd..1c703ab 100644
--- a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
+++ b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
@@ -17,7 +17,8 @@
 package android.content.pm;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
diff --git a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
index 659f9ea..1ddd753 100644
--- a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
+++ b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
@@ -16,9 +16,12 @@
 
 package android.content.pm;
 
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
+import libcore.io.Streams;
+
 import java.io.ByteArrayInputStream;
 import java.util.Arrays;
 
@@ -26,8 +29,6 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
-import libcore.io.Streams;
-
 @LargeTest
 public class MacAuthenticatedInputStreamTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java b/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
index 91697c0..05db8ee 100644
--- a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
+++ b/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import org.junit.Assume;
diff --git a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
index dcd2707..834a0bb 100644
--- a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
 import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
index c64d520..3d7aab0 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
@@ -24,7 +24,8 @@
 
 import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Assume;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/pm/PackageBuilder.java b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
index 4ceed83..c5db962 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBuilder.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static org.junit.Assert.assertEquals;
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 8ac9451d..0ab5367 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -54,11 +54,12 @@
 import android.system.Os;
 import android.system.StructStat;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.R;
 import com.android.internal.content.PackageHelper;
 
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
index 00be822..e852f98 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
@@ -22,8 +22,9 @@
 import android.content.pm.PackageParserCacheHelper.WriteHelper;
 import android.os.Bundle;
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 267267e..be1b1ce 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -29,9 +29,10 @@
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.SystemProperties;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
@@ -51,6 +52,12 @@
     private static final String PRE_RELEASE = "B";
     private static final String NEWER_PRE_RELEASE = "C";
 
+    // Codenames with a fingerprint attached to them. These may only be present in the apps
+    // declared min SDK and not as platform codenames.
+    private static final String OLDER_PRE_RELEASE_WITH_FINGERPRINT = "A.fingerprint";
+    private static final String PRE_RELEASE_WITH_FINGERPRINT = "B.fingerprint";
+    private static final String NEWER_PRE_RELEASE_WITH_FINGERPRINT = "C.fingerprint";
+
     private static final String[] CODENAMES_RELEASED = { /* empty */ };
     private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE };
 
@@ -68,7 +75,7 @@
                 isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
                 outError);
 
-        assertEquals(result, expectedMinSdk);
+        assertEquals("Error msg: " + outError[0], expectedMinSdk, result);
 
         if (expectedMinSdk == -1) {
             assertNotNull(outError[0]);
@@ -98,6 +105,7 @@
         // APP: Pre-release API 10
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
 
         // Do allow same pre-release minSdkVersion on pre-release platform,
         // but overwrite the specified version with CUR_DEVELOPMENT.
@@ -105,11 +113,15 @@
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT);
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT);
+
 
         // Don't allow newer pre-release minSdkVersion on pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
     }
 
     @Test
@@ -133,16 +145,20 @@
         // APP: Pre-release API 10
         // DEV: Released API 20
         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
 
         // Don't allow same pre-release minSdkVersion on released platform.
         // APP: Pre-release API 20
         // DEV: Released API 20
         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1);
+
 
         // Don't allow newer pre-release minSdkVersion on released platform.
         // APP: Pre-release API 30
         // DEV: Released API 20
         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
     }
 
     private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
@@ -189,6 +205,9 @@
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1,
+                false /* forceCurrentDev */);
+
 
         // Do allow same pre-release targetSdkVersion on pre-release platform,
         // but overwrite the specified version with CUR_DEVELOPMENT.
@@ -196,18 +215,26 @@
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
+
 
         // Don't allow newer pre-release targetSdkVersion on pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1,
+                false /* forceCurrentDev */);
+
 
         // Force newer pre-release targetSdkVersion to current pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
     }
 
     @Test
@@ -235,18 +262,25 @@
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
 
         // Don't allow same pre-release targetSdkVersion on released platform.
         // APP: Pre-release API 20
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
+
 
         // Don't allow newer pre-release targetSdkVersion on released platform.
         // APP: Pre-release API 30
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
     }
 
     /**
diff --git a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
index d5d3d7a..71a0e5e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import java.util.function.Supplier;
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index 952bb55..8874525 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -1,8 +1,25 @@
+/*
+ * 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.
+ */
+
 package android.content.pm;
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index d3d1f22a..365e97d 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -21,11 +21,12 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 
+import androidx.test.filters.LargeTest;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
index 3dba440..216b0c8 100644
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 
 import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
index 15b27d7..fc60980 100644
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 
 import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java
index a3fa1a9..f0b4af6 100644
--- a/core/tests/coretests/src/android/content/pm/SignatureTest.java
+++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java
@@ -16,7 +16,7 @@
 
 package android.content.pm;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
index 68942cb..f6527da 100644
--- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
@@ -16,12 +16,12 @@
 
 package android.content.pm;
 
-import android.content.pm.VerificationParams;
 import android.net.Uri;
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
 /**
  * Tests the android.content.pm.VerificationParams class
  *
diff --git a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
index 88d7a59..e7cd02d 100644
--- a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
@@ -17,7 +17,8 @@
 package android.content.pm;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import java.util.Random;
 
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
index e248a77..1ca879c 100644
--- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -29,12 +29,21 @@
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
+import libcore.testing.io.TestIoUtils;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -45,14 +54,6 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
-import libcore.testing.io.TestIoUtils;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DexMetadataHelperTest {
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index 47554a6..47b14bb 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -17,9 +17,10 @@
 package android.content.res;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.TypedValue;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.lang.reflect.InvocationTargetException;
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 72b9197..2fc3e36 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -13,19 +13,18 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package android.content.res;
 
-import org.junit.runner.RunWith;
-import org.junit.Test;
-import org.junit.runners.JUnit4;
-
-import android.content.res.Configuration;
-import android.support.test.filters.SmallTest;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
-import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * Build/install/run: bit FrameworksCoreTests:android.content.res.ConfigurationTest
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 42ff2e9..7ab9d7f 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.res;
 
 import static android.content.res.FontResourcesParser.FamilyResourceEntry;
@@ -26,9 +27,10 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
new file mode 100644
index 0000000..c4df88b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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.res;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.drawable.ColorStateListDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ResourcesDrawableTest {
+
+    @Test
+    public void testLoadColorAsDrawable() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Resources resources = context.getResources();
+        Drawable drawable = resources.getDrawable(R.color.color1);
+        assertTrue(drawable instanceof ColorStateListDrawable);
+    }
+
+    @Test
+    public void testLoadColorAsDrawableFailureThrowsOriginalException() throws Throwable {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Resources resources = context.getResources();
+
+        Exception exception = null;
+
+        try {
+            resources.getDrawable(R.color.drawable_in_color_dir_invalid);
+        } catch (Exception e) {
+            exception = e;
+        }
+
+        assertNotNull(
+                "Loading drawable_in_color_dir_invalid should throw an exception",
+                exception
+        );
+
+        assertEquals(
+                "Can't find ColorStateList from drawable resource ID #0x"
+                        + Integer.toHexString(R.color.drawable_in_color_dir_invalid),
+                exception.getCause().getCause().getMessage()
+        );
+    }
+
+    @Test
+    public void testLoadNormalDrawableInColorDir() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Resources resources = context.getResources();
+        Drawable drawable = resources.getDrawable(R.color.drawable_in_color_dir_valid);
+        assertTrue(drawable instanceof LayerDrawable);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index e85666e..aa1a534 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -13,14 +13,15 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package android.content.res;
 
 import android.os.FileUtils;
 import android.os.LocaleList;
-import android.support.test.filters.SmallTest;
 import android.test.AndroidTestCase;
 import android.util.DisplayMetrics;
-import android.view.Display;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index b2ff927..a2dab99 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -13,18 +13,20 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package android.content.res;
 
 import android.annotation.NonNull;
 import android.app.ResourcesManager;
 import android.os.Binder;
 import android.os.LocaleList;
-import android.support.test.filters.SmallTest;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 public class ResourcesManagerTest extends TestCase {
diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java
index 075f5b7..123da3e 100644
--- a/core/tests/coretests/src/android/database/CursorWindowTest.java
+++ b/core/tests/coretests/src/android/database/CursorWindowTest.java
@@ -16,14 +16,14 @@
 
 package android.database;
 
-import android.test.suitebuilder.annotation.SmallTest;
-import android.database.CursorWindow;
 import android.test.PerformanceTestCase;
 
-import java.util.Arrays;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
+import java.util.Arrays;
+
 public class CursorWindowTest extends TestCase implements PerformanceTestCase {
     public boolean isPerformanceOnly() {
         return false;
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
index 3507223..eb4fd70 100644
--- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -18,10 +18,6 @@
 
 import android.content.ContentValues;
 import android.content.Context;
-import android.database.Cursor;
-import android.database.CursorIndexOutOfBoundsException;
-import android.database.DataSetObserver;
-import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteCursor;
 import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
@@ -29,11 +25,12 @@
 import android.os.Looper;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index 9d75c78..49fb75b 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -25,16 +25,17 @@
 import android.database.sqlite.SQLiteDebug;
 import android.database.sqlite.SQLiteException;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
index b3282941..ee7936f 100644
--- a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
@@ -17,17 +17,17 @@
 package android.database;
 
 import android.database.sqlite.SQLiteDatabase;
-import android.database.Cursor;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
 import android.test.MoreAsserts;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
 
 import java.util.ArrayList;
 import java.util.Locale;
 
-import junit.framework.TestCase;
-
 public class DatabaseLocaleTest extends TestCase {
 
     private SQLiteDatabase mDatabase;
diff --git a/core/tests/coretests/src/android/database/DatabaseLockTest.java b/core/tests/coretests/src/android/database/DatabaseLockTest.java
index 8d3cf5a..e8936c9 100644
--- a/core/tests/coretests/src/android/database/DatabaseLockTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseLockTest.java
@@ -17,11 +17,13 @@
 package android.database;
 
 import android.database.sqlite.SQLiteDatabase;
-import android.test.suitebuilder.annotation.Suppress;
+import android.test.AndroidTestCase;
 import android.util.Log;
+
+import androidx.test.filters.Suppress;
+
 import java.io.File;
 import java.util.concurrent.atomic.AtomicInteger;
-import android.test.AndroidTestCase;
 
 /* 
  * This is a series of unit tests for database locks.
diff --git a/core/tests/coretests/src/android/database/DatabaseStatementTest.java b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
index 895d715..4b34650 100644
--- a/core/tests/coretests/src/android/database/DatabaseStatementTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
@@ -17,14 +17,14 @@
 package android.database;
 
 import android.content.Context;
-import android.database.Cursor;
 import android.database.sqlite.SQLiteConstraintException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDoneException;
 import android.database.sqlite.SQLiteStatement;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 import java.io.File;
 
diff --git a/core/tests/coretests/src/android/database/DatabaseStressTest.java b/core/tests/coretests/src/android/database/DatabaseStressTest.java
index 30e46e7..bfea1fc 100644
--- a/core/tests/coretests/src/android/database/DatabaseStressTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseStressTest.java
@@ -17,11 +17,11 @@
 package android.database;
 
 import android.content.Context;
-import android.database.sqlite.*;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.AndroidTestCase;
 import android.util.Log;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
+import androidx.test.filters.Suppress;
 
 import java.io.File;
 
diff --git a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
index 7c206d7..be156c8 100644
--- a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
@@ -20,7 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/database/RedactingCursorTest.java b/core/tests/coretests/src/android/database/RedactingCursorTest.java
index 93998f3..e2d2bae 100644
--- a/core/tests/coretests/src/android/database/RedactingCursorTest.java
+++ b/core/tests/coretests/src/android/database/RedactingCursorTest.java
@@ -22,8 +22,9 @@
 
 import android.content.Context;
 import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
index 9ed3f11b..730a3cb 100644
--- a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
+++ b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
@@ -26,11 +26,12 @@
 import android.database.sqlite.SQLiteDatabaseConfiguration;
 import android.database.sqlite.SQLiteDebug;
 import android.database.sqlite.SQLiteOpenHelper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/run_newdb_perf_test.sh b/core/tests/coretests/src/android/database/run_newdb_perf_test.sh
index c5b2c97..95f1f83 100755
--- a/core/tests/coretests/src/android/database/run_newdb_perf_test.sh
+++ b/core/tests/coretests/src/android/database/run_newdb_perf_test.sh
@@ -23,7 +23,7 @@
 
 for (( i=0; i<$RUN_N; i++ ))
 do
-    adb  shell am instrument -e class 'android.database.NewDatabasePerformanceTestSuite' -w 'com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner'
+    adb  shell am instrument -e class 'android.database.NewDatabasePerformanceTestSuite' -w 'com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner'
 done
 
 adb logcat -d > /tmp/testlogcat.txt
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
index 551a58e..5dbcb3c 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
@@ -22,9 +22,10 @@
 
 import android.content.Context;
 import android.database.DatabaseUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
index ed14a53..f1d27d4 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
@@ -21,11 +21,12 @@
 
 import android.content.Context;
 import android.os.HandlerThread;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
index c52cf6e..78d3c41 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
@@ -23,7 +23,8 @@
 import android.database.CursorWindow;
 import android.platform.test.annotations.Presubmit;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import java.io.File;
 import java.util.Arrays;
diff --git a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
index e9e2a4d..564460e 100644
--- a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
@@ -17,14 +17,14 @@
 package android.graphics;
 
 import android.os.ParcelFileDescriptor;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
 
-
 public class BitmapFactoryTest extends TestCase {
 
     // tests that we can decode bitmaps from MemoryFiles
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index 3666ddd..e79d2ae 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -16,11 +16,10 @@
 
 package android.graphics;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
-
 public class BitmapTest extends TestCase {
 
     @SmallTest
diff --git a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java b/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java
index 6e38fb6..8e9b38c 100644
--- a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java
+++ b/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/ColorStateListTest.java b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
index 374d142..1d34f93 100644
--- a/core/tests/coretests/src/android/graphics/ColorStateListTest.java
+++ b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
@@ -19,7 +19,8 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
index 76267b2..1771671 100644
--- a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
+++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
@@ -22,11 +22,12 @@
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontFileUtil;
 import android.graphics.fonts.FontVariationAxis;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java b/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java
index 164c1aa..3cfeb25 100644
--- a/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java
+++ b/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java
@@ -20,7 +20,8 @@
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.Suppress;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index b5ed01f..bf56df1 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -18,9 +18,9 @@
 
 import static org.junit.Assert.assertNotEquals;
 
-import android.graphics.Paint;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.util.Arrays;
 import java.util.HashSet;
diff --git a/core/tests/coretests/src/android/graphics/PathOffsetTest.java b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
index 950f873..6cc42f6 100644
--- a/core/tests/coretests/src/android/graphics/PathOffsetTest.java
+++ b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
@@ -16,13 +16,13 @@
 
 package android.graphics;
 
+import static org.junit.Assert.assertTrue;
 
 import android.graphics.Bitmap.Config;
 import android.graphics.Path.Direction;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 
-import static org.junit.Assert.assertTrue;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
index 78e4959..c6d6d1f 100644
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ b/core/tests/coretests/src/android/graphics/PathTest.java
@@ -16,11 +16,10 @@
 
 package android.graphics;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
-
 public class PathTest extends TestCase {
 
     @SmallTest
diff --git a/core/tests/coretests/src/android/graphics/RectTest.java b/core/tests/coretests/src/android/graphics/RectTest.java
index d31d7d5..2918f44 100644
--- a/core/tests/coretests/src/android/graphics/RectTest.java
+++ b/core/tests/coretests/src/android/graphics/RectTest.java
@@ -23,8 +23,9 @@
 import static org.junit.Assert.assertNull;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
index 909a8d9..e1ca7df 100644
--- a/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 6fdb71f..c66bac6 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -27,12 +27,13 @@
 import android.graphics.fonts.FontCustomizationParser;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.SystemFonts;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.FontConfig;
 import android.util.ArrayMap;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index b0c7976..2d16f82 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -22,13 +22,12 @@
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
index 781e343..3dc9987 100644
--- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.graphics.drawable;
 
 import android.content.res.Resources;
@@ -11,11 +27,12 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.PathParser;
 
+import androidx.test.filters.LargeTest;
+
 import org.junit.Test;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
index 655efb5..d0a6ff9 100644
--- a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
@@ -24,8 +24,9 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Xfermode;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index 64fadc0..2bdcc28 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -25,9 +25,10 @@
 import android.os.HandlerThread;
 import android.os.Parcel;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.io.ByteArrayOutputStream;
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
index f90ae34..0a25bd7 100644
--- a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -23,8 +23,9 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
index dcc51e1..823fca5 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -22,10 +22,11 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Pair;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
index f30b1a2..daf6139 100644
--- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
@@ -22,15 +22,12 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.drawable.ColorDrawable;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -40,6 +37,8 @@
 import android.view.WindowManager;
 import android.widget.ImageView;
 
+import androidx.test.filters.LargeTest;
+
 import java.nio.ByteBuffer;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
diff --git a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
new file mode 100644
index 0000000..16be0b0
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
@@ -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.
+ */
+package android.hardware.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiUtils} class. */
+public class HdmiUtilsTest {
+
+    @Test
+    public void pathToPort_isMe() {
+        int targetPhysicalAddress = 0x1000;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS);
+    }
+
+    @Test
+    public void pathToPort_isDirectlyBelow() {
+        int targetPhysicalAddress = 0x1100;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+            targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_isBelow() {
+        int targetPhysicalAddress = 0x1110;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+            targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_neitherMeNorBelow() {
+        int targetPhysicalAddress = 0x3000;
+        int myPhysicalAddress = 0x2000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2200;
+        myPhysicalAddress = 0x3300;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2213;
+        myPhysicalAddress = 0x2212;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2340;
+        myPhysicalAddress = 0x2310;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+    }
+}
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
index 3be776d..aabfc28 100644
--- a/core/tests/coretests/src/android/metrics/LogMakerTest.java
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -13,9 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.metrics;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
index 784a12f..96dac64 100644
--- a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
+++ b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.metrics;
 
 import android.metrics.MetricsReader.Event;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
index 1286b13..9172237 100644
--- a/core/tests/coretests/src/android/net/LocalSocketTest.java
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -16,12 +16,9 @@
 
 package android.net;
 
-import android.net.Credentials;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
 import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
index fff23a0..0f1c71d 100644
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
 package android.net;
 
 import static org.junit.Assert.assertEquals;
@@ -7,7 +23,8 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiSsid;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java b/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java
index c6758ce..29e212f 100644
--- a/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.net;
 
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index ff9816a..3e45a79 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.net;
 
 import static junit.framework.Assert.assertFalse;
@@ -9,7 +25,8 @@
 
 import android.Manifest.permission;
 import android.content.Context;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
index 5cbf02a..bc12e72 100644
--- a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
+++ b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
index 11d066b..eec09e6 100644
--- a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
+++ b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
@@ -19,10 +19,10 @@
 import com.android.org.conscrypt.ClientSessionContext;
 import com.android.org.conscrypt.SSLClientSessionCache;
 
-import org.mockito.Mockito;
-
 import junit.framework.TestCase;
 
+import org.mockito.Mockito;
+
 import java.security.KeyManagementException;
 import java.security.SecureRandom;
 
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index 109f32e..d984d86 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -16,11 +16,17 @@
 
 package android.net;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.os.Bundle;
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index d72738c..87edb6e 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -20,9 +20,10 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import libcore.util.HexEncoding;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index ea0347d..a33de7b 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -18,7 +18,8 @@
 
 import android.content.ContentUris;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/net/WebAddressTest.java b/core/tests/coretests/src/android/net/WebAddressTest.java
index 6fcb97e..70a6253 100644
--- a/core/tests/coretests/src/android/net/WebAddressTest.java
+++ b/core/tests/coretests/src/android/net/WebAddressTest.java
@@ -16,8 +16,8 @@
 
 package android.net;
 
-import android.net.WebAddress;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 public class WebAddressTest extends TestCase {
diff --git a/core/tests/coretests/src/android/net/http/SslCertificateTest.java b/core/tests/coretests/src/android/net/http/SslCertificateTest.java
index 6a30c6c..1beb1de 100644
--- a/core/tests/coretests/src/android/net/http/SslCertificateTest.java
+++ b/core/tests/coretests/src/android/net/http/SslCertificateTest.java
@@ -17,12 +17,13 @@
 
 package android.net.http;
 
-import android.net.http.SslCertificate;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
+
+import junit.framework.TestCase;
+
 import java.io.ByteArrayInputStream;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import junit.framework.TestCase;
 
 public class SslCertificateTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index bf11d56..4c51415 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -16,11 +16,10 @@
 
 package android.os;
 
-import android.os.IInterface;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import com.google.android.collect.Lists;
+
 import junit.framework.TestCase;
 
 import java.util.List;
diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
index 6cdb35ab..ce6ad87 100644
--- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package android.os;
 
 import static org.junit.Assert.assertEquals;
@@ -25,12 +24,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
 import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService;
 import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService;
@@ -63,7 +63,7 @@
  * Install: adb install -r \
  * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class android.os.BinderProxyCountingTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index 4c36b5c..aceda2d 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -19,7 +19,8 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 public class BinderProxyTest extends AndroidTestCase {
     private static class CountingListener implements Binder.ProxyTransactListener {
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
index 534c5cd..a354195 100644
--- a/core/tests/coretests/src/android/os/BinderTest.java
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -16,7 +16,7 @@
 
 package android.os;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
@@ -43,4 +43,13 @@
         Binder.restoreCallingWorkSource(token);
         assertEquals(UID, Binder.getCallingWorkSourceUid());
     }
+
+    @SmallTest
+    public void testGetCallingUidOrThrow() throws Exception {
+        try {
+            Binder.getCallingUidOrThrow();
+            throw new AssertionError("IllegalStateException expected");
+        } catch (IllegalStateException expected) {
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityService.java b/core/tests/coretests/src/android/os/BinderThreadPriorityService.java
index 47a4483..ed42058 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityService.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityService.java
@@ -18,7 +18,6 @@
 
 import android.app.Service;
 import android.content.Intent;
-import android.text.TextUtils;
 import android.util.Log;
 
 /**
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
index 56e977c..48c9df6 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
@@ -21,7 +21,6 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index 5664df6..e16a3db 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -24,9 +24,10 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index fabcf3d..5a3724f 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.hardware.display.DisplayManager;
-import android.os.Bundle;
 import android.provider.Settings;
 import android.view.View;
 import android.view.View.OnClickListener;
diff --git a/core/tests/coretests/src/android/os/BroadcasterTest.java b/core/tests/coretests/src/android/os/BroadcasterTest.java
index 551ea8d..b4c47af9 100644
--- a/core/tests/coretests/src/android/os/BroadcasterTest.java
+++ b/core/tests/coretests/src/android/os/BroadcasterTest.java
@@ -16,11 +16,8 @@
 
 package android.os;
 
-import android.os.Broadcaster;
-import android.os.Handler;
-import android.os.Message;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.MediumTest;
+
 import junit.framework.TestCase;
 
 public class BroadcasterTest extends TestCase {
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 3758627..decc768 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -16,9 +16,8 @@
 
 package android.os;
 
-import android.os.Build;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 9fcf96d..e4dc993 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
index 5189df5..d98ceaf 100644
--- a/core/tests/coretests/src/android/os/EnvironmentTest.java
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -25,8 +25,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java
index 93e27af..ece7645 100644
--- a/core/tests/coretests/src/android/os/FileObserverTest.java
+++ b/core/tests/coretests/src/android/os/FileObserverTest.java
@@ -16,13 +16,14 @@
 
 package android.os;
 
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.util.Iterator;
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 514ea0c..5bce227c 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -51,8 +51,9 @@
 import android.content.Context;
 import android.os.FileUtils.MemoryPipe;
 import android.provider.DocumentsContract.Document;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import libcore.io.Streams;
 
diff --git a/core/tests/coretests/src/android/os/HandlerTester.java b/core/tests/coretests/src/android/os/HandlerTester.java
index a216a0b..fa442f4 100644
--- a/core/tests/coretests/src/android/os/HandlerTester.java
+++ b/core/tests/coretests/src/android/os/HandlerTester.java
@@ -16,10 +16,6 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
 public abstract class HandlerTester extends Thread {
     public abstract void go();
     public abstract void handleMessage(Message msg);
diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java
index 9772aa4..93cfc40 100644
--- a/core/tests/coretests/src/android/os/HandlerThreadTest.java
+++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java
@@ -16,13 +16,9 @@
 
 package android.os;
 
+import androidx.test.filters.MediumTest;
+
 import junit.framework.TestCase;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.Process;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 
 public class HandlerThreadTest extends TestCase {
     private static final int TEST_WHAT = 1;
diff --git a/core/tests/coretests/src/android/os/IdleHandlerTest.java b/core/tests/coretests/src/android/os/IdleHandlerTest.java
index 6c0a862..d8886c9 100644
--- a/core/tests/coretests/src/android/os/IdleHandlerTest.java
+++ b/core/tests/coretests/src/android/os/IdleHandlerTest.java
@@ -16,11 +16,10 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
 import android.os.MessageQueue.IdleHandler;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
+
 import junit.framework.TestCase;
 
 public class IdleHandlerTest extends TestCase {
diff --git a/core/tests/coretests/src/android/os/LocaleListTest.java b/core/tests/coretests/src/android/os/LocaleListTest.java
index 17ef773..1f00a7a 100644
--- a/core/tests/coretests/src/android/os/LocaleListTest.java
+++ b/core/tests/coretests/src/android/os/LocaleListTest.java
@@ -13,14 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.os;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Locale;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
+import java.util.Locale;
+
 public class LocaleListTest extends TestCase {
     @SmallTest
     public void testConstructor() throws Exception {
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index 20b298d..05c2995 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -17,8 +17,9 @@
 package android.os;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 1cd1020..2c5588e 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -16,11 +16,9 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import junit.framework.TestCase;
 
 @Suppress  // Failing.
diff --git a/core/tests/coretests/src/android/os/MessengerService.java b/core/tests/coretests/src/android/os/MessengerService.java
index f15e134..db33471 100644
--- a/core/tests/coretests/src/android/os/MessengerService.java
+++ b/core/tests/coretests/src/android/os/MessengerService.java
@@ -18,11 +18,6 @@
 
 import android.app.Service;
 import android.content.Intent;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
 
 public class MessengerService extends Service {
     private final Handler mHandler = new Handler() {
diff --git a/core/tests/coretests/src/android/os/MessengerTest.java b/core/tests/coretests/src/android/os/MessengerTest.java
index 473ffe2..9143ff1 100644
--- a/core/tests/coretests/src/android/os/MessengerTest.java
+++ b/core/tests/coretests/src/android/os/MessengerTest.java
@@ -20,13 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.RemoteException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 public class MessengerTest extends AndroidTestCase {
     private Messenger mServiceMessenger;
diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java
index 2b84126..08fb945 100644
--- a/core/tests/coretests/src/android/os/OsTests.java
+++ b/core/tests/coretests/src/android/os/OsTests.java
@@ -16,12 +16,8 @@
 
 package android.os;
 
-import com.google.android.collect.Lists;
 import junit.framework.TestSuite;
 
-import java.util.Enumeration;
-import java.util.List;
-
 public class OsTests {
     public static TestSuite suite() {
         TestSuite suite = new TestSuite(OsTests.class.getName());
diff --git a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
index 516dc0a..a6b296d 100644
--- a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
+++ b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
@@ -20,10 +20,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArrayMap;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/os/PatternMatcherTest.java b/core/tests/coretests/src/android/os/PatternMatcherTest.java
index 9645ccc..82350cd 100644
--- a/core/tests/coretests/src/android/os/PatternMatcherTest.java
+++ b/core/tests/coretests/src/android/os/PatternMatcherTest.java
@@ -1,9 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.os;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
-import org.junit.runner.RunWith;
+
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
index 7533c84..38ad90f 100644
--- a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
@@ -17,15 +17,16 @@
 package android.os;
 
 import android.os.PerformanceCollector.PerformanceResultsWriter;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Random;
 
-import junit.framework.TestCase;
-
 public class PerformanceCollectorTest extends TestCase {
 
     private PerformanceCollector mPerfCollector;
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index a828f44..1b587dd 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -21,10 +21,11 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/PowerManagerVrTest.java b/core/tests/coretests/src/android/os/PowerManagerVrTest.java
index e01e5fa..5d2c65b 100644
--- a/core/tests/coretests/src/android/os/PowerManagerVrTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerVrTest.java
@@ -19,16 +19,14 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.provider.Settings;
-import android.service.dreams.IDreamManager;
 import android.service.dreams.DreamService;
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
+import android.service.dreams.IDreamManager;
 import android.test.ActivityInstrumentationTestCase2;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
 /**
  * Tests dream aspects of PowerManager.
  */
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index 1f5b7c8..b749e71 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -17,14 +17,10 @@
 
 package android.os;
 
-import android.os.Process;
-import android.os.UserHandle;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.MediumTest;
 
 import junit.framework.TestCase;
 
-
 public class ProcessTest extends TestCase {
 
     @MediumTest
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index 9e15231..d5163e1 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -24,10 +24,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.system.Os;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
index 9e44554..8085993 100644
--- a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
+++ b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.os;
 
 import android.app.ActivityManager;
@@ -20,10 +21,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.os.Process;
 import android.provider.Settings;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 /**
  * Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's
diff --git a/core/tests/coretests/src/android/os/TestHandlerThread.java b/core/tests/coretests/src/android/os/TestHandlerThread.java
index 7e84af3..8c0330b 100644
--- a/core/tests/coretests/src/android/os/TestHandlerThread.java
+++ b/core/tests/coretests/src/android/os/TestHandlerThread.java
@@ -16,11 +16,6 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue.IdleHandler;
-
 abstract class TestHandlerThread {
     private boolean mDone = false;
     private boolean mSuccess = false;
diff --git a/core/tests/coretests/src/android/os/TestVrActivity.java b/core/tests/coretests/src/android/os/TestVrActivity.java
index 33ff164..75df7c2 100644
--- a/core/tests/coretests/src/android/os/TestVrActivity.java
+++ b/core/tests/coretests/src/android/os/TestVrActivity.java
@@ -17,7 +17,6 @@
 package android.os;
 
 import android.app.Activity;
-import android.os.Bundle;
 import android.service.vr.VrListenerService;
 
 import java.util.concurrent.CountDownLatch;
diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java
index 1541553..5cad549 100644
--- a/core/tests/coretests/src/android/os/TraceTest.java
+++ b/core/tests/coretests/src/android/os/TraceTest.java
@@ -16,13 +16,13 @@
 
 package android.os;
 
-import android.os.Debug;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
 /**
  * This class is used to test the native tracing support.  Run this test
  * while tracing on the emulator and then run traceview to view the trace.
diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java
index af559fd..4a1cdbf 100644
--- a/core/tests/coretests/src/android/os/UserHandleTest.java
+++ b/core/tests/coretests/src/android/os/UserHandleTest.java
@@ -25,7 +25,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java
index 44510c2..af3660a 100644
--- a/core/tests/coretests/src/android/os/VintfObjectTest.java
+++ b/core/tests/coretests/src/android/os/VintfObjectTest.java
@@ -16,7 +16,6 @@
 
 package android.os;
 
-import junit.framework.Assert;
 import junit.framework.TestCase;
 
 public class VintfObjectTest extends TestCase {
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index 3ec297c..62f2ac2 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -19,7 +19,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ProxyFileDescriptorCallback;
 import android.system.ErrnoException;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/preference/ListPreferenceTest.java b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
index 72f62f1..51dbb64 100644
--- a/core/tests/coretests/src/android/preference/ListPreferenceTest.java
+++ b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
@@ -16,10 +16,10 @@
 
 package android.preference;
 
-import android.preference.ListPreference;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
 @LargeTest
 public class ListPreferenceTest extends AndroidTestCase {
     public void testListPreferenceSummaryFromEntries() {
diff --git a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
index 654474c..0deb77e 100644
--- a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
+++ b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
@@ -16,20 +16,20 @@
 
 package android.preference;
 
-
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 5d12f7e..94d85e6 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -41,9 +41,10 @@
 import android.print.test.services.PrinterDiscoverySessionCallbacks;
 import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.recommendation.IRecommendationsChangeListener;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 800b864..17e9654 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -28,9 +28,10 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
index 1465d0a9..02a9adf 100644
--- a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
@@ -20,9 +20,10 @@
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.provider.DocumentsContract.Path;
-import android.support.test.filters.SmallTest;
 import android.test.ProviderTestCase2;
 
+import androidx.test.filters.SmallTest;
+
 import java.util.Arrays;
 
 /**
diff --git a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
index 2955869..7e02be8 100644
--- a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
@@ -25,25 +25,25 @@
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
 import android.graphics.Typeface;
 import android.os.Handler;
-import android.provider.FontsContract.Columns;
 import android.provider.FontsContract.FontFamilyResult;
-import android.provider.FontsContract.FontInfo;
-import android.provider.FontsContract;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import java.util.ArrayList;
-import java.util.List;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class FontsContractE2ETest {
diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java
index d42d79e..c5d6f7f 100644
--- a/core/tests/coretests/src/android/provider/FontsContractTest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractTest.java
@@ -13,31 +13,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.provider;
 
+import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND;
+import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE;
+import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
+import static android.provider.FontsContract.Columns.RESULT_CODE_OK;
+
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import static android.provider.FontsContract.Columns.RESULT_CODE_OK;
-import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND;
-import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE;
-import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
-
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ProviderInfo;
 import android.content.pm.Signature;
 import android.database.MatrixCursor;
 import android.graphics.fonts.FontVariationAxis;
 import android.provider.FontsContract.FontInfo;
-import android.support.test.filters.SmallTest;
 import android.test.ProviderTestCase2;
 import android.util.Base64;
 
+import androidx.test.filters.SmallTest;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/provider/MockFontProvider.java b/core/tests/coretests/src/android/provider/MockFontProvider.java
index ad5b130..e527dec 100644
--- a/core/tests/coretests/src/android/provider/MockFontProvider.java
+++ b/core/tests/coretests/src/android/provider/MockFontProvider.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.provider;
 
 import static android.provider.FontsContract.Columns;
@@ -21,16 +22,13 @@
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.database.Cursor;
 import android.database.MatrixCursor;
-import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
-import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
-import android.util.ArraySet;
-import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -46,8 +44,6 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-import com.android.internal.annotations.GuardedBy;
-
 public class MockFontProvider extends ContentProvider {
     final static String AUTHORITY = "android.provider.fonts.font";
 
diff --git a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
index 7458de5..f84355f 100644
--- a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
@@ -21,9 +21,9 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.test.ProviderTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 /**
  * ProviderTestCase that performs unit tests of SearchRecentSuggestionsProvider.
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ac57d20..0fb1c7c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -27,8 +27,9 @@
 import static java.lang.reflect.Modifier.isStatic;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -114,6 +115,7 @@
                     Settings.Global.ANOMALY_CONFIG_VERSION,
                     Settings.Global.APN_DB_UPDATE_CONTENT_URL,
                     Settings.Global.APN_DB_UPDATE_METADATA_URL,
+                    Settings.Global.APPLY_RAMPING_RINGER,
                     Settings.Global.APP_BINDING_CONSTANTS,
                     Settings.Global.APP_IDLE_CONSTANTS,
                     Settings.Global.APP_OPS_CONSTANTS,
@@ -197,6 +199,7 @@
                     Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
                     Settings.Global.DATA_STALL_EVALUATION_TYPE,
                     Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+                    Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK,
                     Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
                     Settings.Global.DEBUG_APP,
                     Settings.Global.DEBUG_VIEW_ATTRIBUTES,
@@ -268,6 +271,7 @@
                     Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS,
                     Settings.Global.GNSS_SATELLITE_BLACKLIST,
                     Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
+                    Settings.Global.HDMI_CEC_SWITCH_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                     Settings.Global.HDMI_CONTROL_ENABLED,
@@ -309,6 +313,7 @@
                     Settings.Global.MDC_INITIAL_MAX_RETRY,
                     Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                     Settings.Global.MHL_POWER_CHARGE_ENABLED,
+                    Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS,
                     Settings.Global.MOBILE_DATA, // Candidate for backup?
                     Settings.Global.MOBILE_DATA_ALWAYS_ON,
                     Settings.Global.MODE_RINGER,
@@ -475,8 +480,10 @@
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+                    Settings.Global.GUP_DEV_ALL_APPS,
                     Settings.Global.GUP_DEV_OPT_IN_APPS,
-                    Settings.Global.GUP_BLACK_LIST,
+                    Settings.Global.GUP_DEV_OPT_OUT_APPS,
+                    Settings.Global.GUP_BLACKLIST,
                     Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
@@ -547,7 +554,14 @@
                     Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
                     Settings.Global.BACKUP_MULTI_USER_ENABLED,
                     Settings.Global.ISOLATED_STORAGE_LOCAL,
-                    Settings.Global.ISOLATED_STORAGE_REMOTE);
+                    Settings.Global.ISOLATED_STORAGE_REMOTE,
+                    Settings.Global.APPOP_HISTORY_PARAMETERS,
+                    Settings.Global.APPOP_HISTORY_MODE,
+                    Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER,
+                    Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS,
+                    Settings.Global.ENABLE_RADIO_BUG_DETECTION,
+                    Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD,
+                    Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD);
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
@@ -656,6 +670,7 @@
                  Settings.Secure.SMS_DEFAULT_APPLICATION,
                  Settings.Secure.SPELL_CHECKER_ENABLED,  // Intentionally removed in Q
                  Settings.Secure.TRUST_AGENTS_INITIALIZED,
+                 Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
                  Settings.Secure.TV_INPUT_CUSTOM_LABELS,
                  Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
                  Settings.Secure.TV_USER_SETUP_COMPLETE,
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index cb6f0e6..a5f5f67 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -34,9 +34,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
 
 import java.util.HashMap;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
index b0d29bd..08f9de6 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
@@ -22,8 +22,9 @@
 
 import android.platform.test.annotations.Presubmit;
 import android.provider.SettingsValidators.Validator;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/provider/SmsProviderTest.java b/core/tests/coretests/src/android/provider/SmsProviderTest.java
index af4d1a6..67ac8ea 100644
--- a/core/tests/coretests/src/android/provider/SmsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SmsProviderTest.java
@@ -22,8 +22,9 @@
 import android.net.Uri;
 import android.provider.Telephony.Sms;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
 
 import java.util.GregorianCalendar;
 
diff --git a/core/tests/coretests/src/android/provider/TestFontsProvider.java b/core/tests/coretests/src/android/provider/TestFontsProvider.java
index 46906df..c3457fa 100644
--- a/core/tests/coretests/src/android/provider/TestFontsProvider.java
+++ b/core/tests/coretests/src/android/provider/TestFontsProvider.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.provider;
 
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
@@ -23,7 +24,6 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.MatrixCursor;
-import android.graphics.Typeface;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
index 0c9c4c1..ce0bf30 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
index 61ab152..37fe22f 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.collect.Lists;
 
@@ -44,6 +45,7 @@
     private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
     private static final String KEY_ALIAS = "steph";
     private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 };
+    private static final byte[] KEY_METADATA = new byte[] { 5, 3, 11, 13 };
     private static final CertPath CERT_PATH = TestData.getThmCertPath();
 
     @Test
@@ -99,6 +101,7 @@
         WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
         assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
         assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
+        assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata());
     }
 
     @Test
@@ -165,6 +168,7 @@
         WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
         assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
         assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
+        assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata());
     }
 
     private static KeyChainSnapshot createKeyChainSnapshot() throws Exception {
@@ -196,6 +200,7 @@
         return new WrappedApplicationKey.Builder()
                 .setAlias(KEY_ALIAS)
                 .setEncryptedKeyMaterial(KEY_MATERIAL)
+                .setMetadata(KEY_METADATA)
                 .build();
     }
 
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
index b6af9bb..2b37b52 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
index dd8cd8d..3b8f715 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
index a5a3ca9..2b15d73 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
@@ -20,8 +20,8 @@
 
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
index 15afbdd..aec54e1 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -34,6 +35,7 @@
 
     private static final String ALIAS = "karlin";
     private static final byte[] KEY_MATERIAL = new byte[] { 0, 1, 2, 3, 4 };
+    private static final byte[] METADATA = new byte[] {3, 2, 1, 0};
 
     private Parcel mParcel;
 
@@ -58,8 +60,18 @@
     }
 
     @Test
+    public void build_setsMetadata_nonNull() {
+        assertArrayEquals(METADATA, buildTestKeyWithMetadata(METADATA).getMetadata());
+    }
+
+    @Test
+    public void build_setsMetadata_null() {
+        assertArrayEquals(null, buildTestKeyWithMetadata(null).getMetadata());
+    }
+
+    @Test
     public void writeToParcel_writesAliasToParcel() {
-        buildTestKey().writeToParcel(mParcel, /*flags=*/ 0);
+        buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0);
 
         mParcel.setDataPosition(0);
         WrappedApplicationKey readFromParcel =
@@ -69,7 +81,7 @@
 
     @Test
     public void writeToParcel_writesKeyMaterial() {
-        buildTestKey().writeToParcel(mParcel, /*flags=*/ 0);
+        buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0);
 
         mParcel.setDataPosition(0);
         WrappedApplicationKey readFromParcel =
@@ -77,10 +89,48 @@
         assertArrayEquals(KEY_MATERIAL, readFromParcel.getEncryptedKeyMaterial());
     }
 
+    @Test
+    public void writeToParcel_writesMetadata_nonNull() {
+        buildTestKeyWithMetadata(METADATA).writeToParcel(mParcel, /*flags=*/ 0);
+
+        mParcel.setDataPosition(0);
+        WrappedApplicationKey readFromParcel =
+                WrappedApplicationKey.CREATOR.createFromParcel(mParcel);
+        assertArrayEquals(METADATA, readFromParcel.getMetadata());
+    }
+
+    @Test
+    public void writeToParcel_writesMetadata_null() {
+        buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0);
+
+        mParcel.setDataPosition(0);
+        WrappedApplicationKey readFromParcel =
+                WrappedApplicationKey.CREATOR.createFromParcel(mParcel);
+        assertArrayEquals(null, readFromParcel.getMetadata());
+    }
+
+    @Test
+    public void writeToParcel_writesMetadata_absent() {
+        buildTestKey().writeToParcel(mParcel, /*flags=*/ 0);
+
+        mParcel.setDataPosition(0);
+        WrappedApplicationKey readFromParcel =
+                WrappedApplicationKey.CREATOR.createFromParcel(mParcel);
+        assertArrayEquals(null, readFromParcel.getMetadata());
+    }
+
     private WrappedApplicationKey buildTestKey() {
         return new WrappedApplicationKey.Builder()
                 .setAlias(ALIAS)
                 .setEncryptedKeyMaterial(KEY_MATERIAL)
                 .build();
     }
+
+    private WrappedApplicationKey buildTestKeyWithMetadata(byte[] metadata) {
+        return new WrappedApplicationKey.Builder()
+                .setAlias(ALIAS)
+                .setEncryptedKeyMaterial(KEY_MATERIAL)
+                .setMetadata(metadata)
+                .build();
+    }
 }
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
index 7f0eb43..17486d5 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
@@ -21,8 +21,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
index e69d1e7..d00d052 100644
--- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.service.euicc;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -23,10 +24,11 @@
 
 import android.os.Parcel;
 import android.service.carrier.CarrierIdentifier;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.UiccAccessRule;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
new file mode 100644
index 0000000..4aa1000
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.service.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.metrics.LogMaker;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StatusBarNotificationTest {
+
+    private final Context mMockContext = mock(Context.class);
+    @Mock
+    private PackageManager mPm;
+
+    private static final String PKG = "com.example.o";
+    private static final int UID = 9583;
+    private static final int ID = 1;
+    private static final String TAG = "tag1";
+    private static final String CHANNEL_ID = "channel";
+    private static final String CHANNEL_ID_LONG =
+            "give_a_developer_a_string_argument_and_who_knows_what_they_will_pass_in_there";
+    private static final String GROUP_ID_1 = "group1";
+    private static final String GROUP_ID_2 = "group2";
+    private static final String GROUP_ID_LONG =
+            "0|com.foo.bar|g:content://com.foo.bar.ui/account%3A-0000000/account/";
+    private static final android.os.UserHandle USER =
+            UserHandle.of(ActivityManager.getCurrentUser());
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockContext.getResources()).thenReturn(
+                InstrumentationRegistry.getContext().getResources());
+        when(mMockContext.getPackageManager()).thenReturn(mPm);
+        when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+    }
+
+    @Test
+    public void testLogMaker() {
+        final LogMaker logMaker = getNotification(PKG, GROUP_ID_1, CHANNEL_ID).getLogMaker();
+
+        assertEquals(CHANNEL_ID,
+                (String) logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID));
+        assertEquals(PKG, logMaker.getPackageName());
+        assertEquals(ID, logMaker.getTaggedData(MetricsEvent.NOTIFICATION_ID));
+        assertEquals(TAG, logMaker.getTaggedData(MetricsEvent.NOTIFICATION_TAG));
+        assertEquals(GROUP_ID_1,
+                logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+    }
+
+    @Test
+    public void testLogMakerNoChannel() {
+        final LogMaker logMaker = getNotification(PKG, GROUP_ID_1, null).getLogMaker();
+
+        assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID));
+    }
+
+    @Test
+    public void testLogMakerLongChannel() {
+        final LogMaker logMaker = getNotification(PKG, null, CHANNEL_ID_LONG).getLogMaker();
+        final String loggedId = (String) logMaker
+                .getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID);
+        assertEquals(StatusBarNotification.MAX_LOG_TAG_LENGTH, loggedId.length());
+        assertEquals(CHANNEL_ID_LONG.substring(0, 10), loggedId.substring(0, 10));
+    }
+
+    @Test
+    public void testLogMakerNoGroup() {
+        final LogMaker logMaker = getNotification(PKG, null, CHANNEL_ID).getLogMaker();
+
+        assertNull(
+                logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+    }
+
+    @Test
+    public void testLogMakerLongGroup() {
+        final LogMaker logMaker = getNotification(PKG, GROUP_ID_LONG, CHANNEL_ID)
+                .getLogMaker();
+
+        final String loggedId = (String)
+                logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID);
+        assertEquals(StatusBarNotification.MAX_LOG_TAG_LENGTH, loggedId.length());
+        assertEquals(GROUP_ID_LONG.substring(0, 10), loggedId.substring(0, 10));
+    }
+
+    @Test
+    public void testLogMakerOverrideGroup() {
+        StatusBarNotification sbn = getNotification(PKG, GROUP_ID_1, CHANNEL_ID);
+        assertEquals(GROUP_ID_1,
+                sbn.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+
+        sbn.setOverrideGroupKey(GROUP_ID_2);
+        assertEquals(GROUP_ID_2,
+                sbn.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+
+        sbn.setOverrideGroupKey(null);
+        assertEquals(GROUP_ID_1,
+                sbn.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+    }
+
+    private StatusBarNotification getNotification(String pkg, String group, String channelId) {
+        final Notification.Builder builder = new Notification.Builder(mMockContext, channelId)
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        if (group != null) {
+            builder.setGroup(group);
+        }
+
+        Notification n = builder.build();
+        return new StatusBarNotification(
+                pkg, pkg, ID, TAG, UID, UID, n, USER, null, UID);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java b/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java
index ab541a1..9c8e868 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java
@@ -16,16 +16,12 @@
 
 package android.service.settings.suggestions;
 
-import android.support.annotation.VisibleForTesting;
-
 import java.util.ArrayList;
 import java.util.List;
 
 public class MockSuggestionService extends SuggestionService {
 
-    @VisibleForTesting
     static boolean sOnSuggestionLaunchedCalled;
-    @VisibleForTesting
     static boolean sOnSuggestionDismissedCalled;
 
     public static void reset() {
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
index dca8c09..64edda5 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
@@ -21,10 +21,11 @@
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.ServiceTestRule;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ServiceTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
index b0ec55d..8c47fcb 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
@@ -24,9 +24,10 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
index 0c7e730..1c5986a 100644
--- a/core/tests/coretests/src/android/text/AndroidCharacterTest.java
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -19,7 +19,8 @@
 import static org.junit.Assert.assertArrayEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/text/BidiFormatterTest.java b/core/tests/coretests/src/android/text/BidiFormatterTest.java
index 1b936c7..312fb68 100644
--- a/core/tests/coretests/src/android/text/BidiFormatterTest.java
+++ b/core/tests/coretests/src/android/text/BidiFormatterTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
index c69f4f5..cca1ad3 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
@@ -22,8 +22,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
index ea954f6..699243b 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
@@ -27,11 +27,12 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.ReplacementSpan;
 import android.util.ArraySet;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/EmojiTest.java b/core/tests/coretests/src/android/text/EmojiTest.java
index 313f1b6..1948994 100644
--- a/core/tests/coretests/src/android/text/EmojiTest.java
+++ b/core/tests/coretests/src/android/text/EmojiTest.java
@@ -21,8 +21,9 @@
 
 import android.icu.lang.UCharacterDirection;
 import android.icu.text.Bidi;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index 5592aac..cc51ec3 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -24,9 +24,10 @@
 import android.graphics.fonts.FontCustomizationParser;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.SystemFonts;
-import android.support.test.InstrumentationRegistry;
 import android.util.ArrayMap;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
diff --git a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
index 1208d7c..96e7fb9 100644
--- a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
+++ b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
@@ -22,12 +22,13 @@
 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 androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 97937a4..990161a 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -31,11 +31,12 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Alignment;
 import android.text.style.StrikethroughSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index a0dca2c..57bb434 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -23,9 +23,10 @@
 
 import android.content.Context;
 import android.graphics.Typeface;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
index d9dc6fc..ba15b92 100644
--- a/core/tests/coretests/src/android/text/PackedIntVectorTest.java
+++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
index d0f2d46..91b8c6a 100644
--- a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
@@ -16,18 +16,18 @@
 
 package android.text;
 
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.BulletSpan;
 import android.text.style.QuoteSpan;
 import android.text.style.SubscriptSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
index 6c05601..9149f7b 100644
--- a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
@@ -21,11 +21,12 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.QuoteSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java
index f368428..d248a1f 100644
--- a/core/tests/coretests/src/android/text/SpannableTest.java
+++ b/core/tests/coretests/src/android/text/SpannableTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.MoreAsserts;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
index 380e315..ca43733 100644
--- a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
@@ -21,11 +21,12 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.QuoteSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java
index 5737571..3ab0755 100644
--- a/core/tests/coretests/src/android/text/SpannedTest.java
+++ b/core/tests/coretests/src/android/text/SpannedTest.java
@@ -21,13 +21,14 @@
 import android.graphics.Typeface;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.CharacterStyle;
 import android.text.style.StyleSpan;
 import android.text.style.TextAppearanceSpan;
 import android.text.style.TypefaceSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
index bf0d427..32370b3e 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
index 5cf5426..4221ac2 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
@@ -19,11 +19,12 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Directions;
 import android.text.StaticLayoutTest.LayoutBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 2521712..b1c896e 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -25,13 +25,14 @@
 import android.graphics.Paint.FontMetricsInt;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Alignment;
 import android.text.method.EditorState;
 import android.text.style.LocaleSpan;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
index f6888e3..0d42326 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Alignment;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java
index 24020ce..15fbc9e 100644
--- a/core/tests/coretests/src/android/text/TextLayoutTest.java
+++ b/core/tests/coretests/src/android/text/TextLayoutTest.java
@@ -17,8 +17,9 @@
 package android.text;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 61f976a..90ce305 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -25,14 +25,15 @@
 import android.graphics.Paint;
 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.filters.Suppress;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.TabStops;
 import android.text.style.ReplacementSpan;
 import android.text.style.TabStopSpan;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -254,6 +255,18 @@
     }
 
     @Test
+    public void testMeasure_wordSpacing() {
+        final TextPaint paint = new TextPaint();
+        paint.setTypeface(TYPEFACE);
+        paint.setTextSize(10.0f);  // make 1em = 10px
+        paint.setWordSpacing(10.0f);
+
+        TextLine tl = getTextLine("I I", paint);
+        assertMeasurements(tl, 3, false,
+                new float[]{0.0f, 10.0f, 120.0f, 130.0f});
+    }
+
+    @Test
     public void testHandleRun_ellipsizedReplacementSpan_isSkipped() {
         final Spannable text = new SpannableStringBuilder("This is a... text");
 
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 72290bf..be6ef19 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -26,15 +26,16 @@
 
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.MoreAsserts;
 import android.text.style.StyleSpan;
 import android.text.util.Rfc822Token;
 import android.text.util.Rfc822Tokenizer;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.google.android.collect.Lists;
 
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/VariationParserTest.java b/core/tests/coretests/src/android/text/VariationParserTest.java
index fbd2412..0afe811 100644
--- a/core/tests/coretests/src/android/text/VariationParserTest.java
+++ b/core/tests/coretests/src/android/text/VariationParserTest.java
@@ -21,8 +21,9 @@
 
 import android.graphics.fonts.FontVariationAxis;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 9000ed0..5a0a84d 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index 872b71a..381c051 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -22,8 +22,9 @@
 import android.content.res.Resources;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 82e4bff..068d047 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -25,11 +25,12 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 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.format.Formatter.BytesResult;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
index d563f2e..ac00411 100644
--- a/core/tests/coretests/src/android/text/format/TimeTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -21,12 +21,13 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.filters.Suppress;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 import android.util.TimeFormatException;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index df4609f..ddae652 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -17,15 +17,16 @@
 package android.text.method;
 
 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.InputType;
 import android.util.KeyUtils;
 import android.view.KeyEvent;
 import android.widget.EditText;
 import android.widget.TextView.BufferType;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/method/EditorState.java b/core/tests/coretests/src/android/text/method/EditorState.java
index 12bb8c8..4eff7a4 100644
--- a/core/tests/coretests/src/android/text/method/EditorState.java
+++ b/core/tests/coretests/src/android/text/method/EditorState.java
@@ -21,8 +21,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.text.Editable;
 import android.text.Spannable;
 import android.text.SpannableString;
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 45a5010..45b63e0 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -17,15 +17,16 @@
 package android.text.method;
 
 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.InputType;
 import android.util.KeyUtils;
 import android.view.KeyEvent;
 import android.widget.EditText;
 import android.widget.TextView.BufferType;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
index 6845863..cc345f5 100644
--- a/core/tests/coretests/src/android/text/method/WordIteratorTest.java
+++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
@@ -22,8 +22,9 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
index 3d7e5ba..a0d2f85 100644
--- a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
+++ b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
@@ -16,17 +16,17 @@
 
 package android.text.style;
 
-
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.SpannableString;
 import android.text.Spanned;
 import android.text.StaticLayout;
 import android.text.TextPaint;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index be3a0be..107ecd7 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -23,9 +23,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.LocaleList;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.method.LinkMovementMethod;
@@ -33,6 +30,10 @@
 import android.util.Patterns;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/transition/AutoTransitionTest.java b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
index 834fb7a..deae967 100644
--- a/core/tests/coretests/src/android/transition/AutoTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
@@ -16,16 +16,16 @@
 
 package android.transition;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @RunWith(AndroidJUnit4.class)
 public class AutoTransitionTest {
     @Test
diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
index 22365ba..2077d94 100644
--- a/core/tests/coretests/src/android/transition/FadeTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
@@ -22,13 +22,14 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.transition.Transition.TransitionListener;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.CountDownLatch;
diff --git a/core/tests/coretests/src/android/transition/SlideTransitionTest.java b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
index 8b9ec74..046e49d 100644
--- a/core/tests/coretests/src/android/transition/SlideTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
@@ -19,18 +19,18 @@
 import android.animation.AnimatorSetActivity;
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-
 public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
 
     Activity mActivity;
diff --git a/core/tests/coretests/src/android/transition/TransitionTest.java b/core/tests/coretests/src/android/transition/TransitionTest.java
index 7e629f9..f59c406 100644
--- a/core/tests/coretests/src/android/transition/TransitionTest.java
+++ b/core/tests/coretests/src/android/transition/TransitionTest.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorSetActivity;
 import android.app.Activity;
 import android.graphics.Rect;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 import android.transition.Transition.EpicenterCallback;
 import android.util.ArrayMap;
@@ -27,6 +26,8 @@
 import android.view.animation.AccelerateInterpolator;
 import android.widget.TextView;
 
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.lang.reflect.Field;
diff --git a/core/tests/coretests/src/android/util/ArrayMapTest.java b/core/tests/coretests/src/android/util/ArrayMapTest.java
index f0cc7f7..b212cf6 100644
--- a/core/tests/coretests/src/android/util/ArrayMapTest.java
+++ b/core/tests/coretests/src/android/util/ArrayMapTest.java
@@ -14,10 +14,10 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
-import android.util.ArrayMap;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
+
 import org.junit.Test;
 
 import java.util.ConcurrentModificationException;
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index af608c3..15c51af 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/DataUnitTest.java b/core/tests/coretests/src/android/util/DataUnitTest.java
index 4eae8b4..ec296b7 100644
--- a/core/tests/coretests/src/android/util/DataUnitTest.java
+++ b/core/tests/coretests/src/android/util/DataUnitTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
index 4c5ad76..572e9b0 100644
--- a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
+++ b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
@@ -16,10 +16,11 @@
 
 package android.util;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.util.Calendar;
-import android.test.suitebuilder.annotation.SmallTest;
 
 /**
  * Unit tests for {@link DayOfMonthCursor}.
diff --git a/core/tests/coretests/src/android/util/InternalSelectionView.java b/core/tests/coretests/src/android/util/InternalSelectionView.java
index a0fb0f1..4a1baef 100644
--- a/core/tests/coretests/src/android/util/InternalSelectionView.java
+++ b/core/tests/coretests/src/android/util/InternalSelectionView.java
@@ -16,19 +16,16 @@
 
 package android.util;
 
-import com.android.frameworks.coretests.R;
-
-import android.view.View;
-import android.view.KeyEvent;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Paint;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.graphics.Color;
-import android.util.AttributeSet;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.view.KeyEvent;
+import android.view.View;
 
-
+import com.android.frameworks.coretests.R;
 
 /**
  * A view that has a known number of selectable rows, and maintains a notion of which
diff --git a/core/tests/coretests/src/android/util/KeyUtils.java b/core/tests/coretests/src/android/util/KeyUtils.java
index 593f727..612643e 100644
--- a/core/tests/coretests/src/android/util/KeyUtils.java
+++ b/core/tests/coretests/src/android/util/KeyUtils.java
@@ -17,16 +17,10 @@
 package android.util;
 
 import android.app.Instrumentation;
-import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.InstrumentationTestCase;
-import android.view.Gravity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
 
 /**
  * Reusable methods for generating key events.
diff --git a/core/tests/coretests/src/android/util/KeyValueListParserTest.java b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
index 038f0b7..f65c4c7 100644
--- a/core/tests/coretests/src/android/util/KeyValueListParserTest.java
+++ b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/ListUtil.java b/core/tests/coretests/src/android/util/ListUtil.java
index 2a7cb96..3748cf8 100644
--- a/core/tests/coretests/src/android/util/ListUtil.java
+++ b/core/tests/coretests/src/android/util/ListUtil.java
@@ -20,13 +20,11 @@
 import android.view.KeyEvent;
 import android.widget.ListView;
 
-
 /**
  * Various useful stuff for instrumentation testing listview.
  */
 public class ListUtil {
 
-
     private final ListView mListView;
     private final Instrumentation mInstrumentation;
 
diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/coretests/src/android/util/LocalLogTest.java
index 5144c85..6cdcb5e 100644
--- a/core/tests/coretests/src/android/util/LocalLogTest.java
+++ b/core/tests/coretests/src/android/util/LocalLogTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/LogNullabilityTest.java b/core/tests/coretests/src/android/util/LogNullabilityTest.java
index 3f4776d..370885d 100644
--- a/core/tests/coretests/src/android/util/LogNullabilityTest.java
+++ b/core/tests/coretests/src/android/util/LogNullabilityTest.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java
index 30c81b0..d783c12 100644
--- a/core/tests/coretests/src/android/util/LogTest.java
+++ b/core/tests/coretests/src/android/util/LogTest.java
@@ -16,13 +16,12 @@
 
 package android.util;
 
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
 import android.os.SystemProperties;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
+
+import androidx.test.filters.Suppress;
+
+import junit.framework.TestCase;
 
 //This is an empty TestCase.
 @Suppress
diff --git a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
index 3f03db9..a1d48e6 100644
--- a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/LruCacheTest.java b/core/tests/coretests/src/android/util/LruCacheTest.java
index 5a97158..1928bfd 100644
--- a/core/tests/coretests/src/android/util/LruCacheTest.java
+++ b/core/tests/coretests/src/android/util/LruCacheTest.java
@@ -16,12 +16,13 @@
 
 package android.util;
 
+import junit.framework.TestCase;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import junit.framework.TestCase;
 
 public final class LruCacheTest extends TestCase {
     private int expectedCreateCount;
diff --git a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
index 55da665..30d5f77 100644
--- a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
+++ b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
@@ -16,12 +16,12 @@
 
 package android.util;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Calendar;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
+import java.util.Calendar;
+
 /**
  * Unit tests for {@link MonthDisplayHelper}.
  */
diff --git a/core/tests/coretests/src/android/util/OrientationUtil.java b/core/tests/coretests/src/android/util/OrientationUtil.java
index ecdca5d..0361194 100644
--- a/core/tests/coretests/src/android/util/OrientationUtil.java
+++ b/core/tests/coretests/src/android/util/OrientationUtil.java
@@ -19,7 +19,6 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.pm.ActivityInfo;
-
 import android.test.ActivityInstrumentationTestCase2;
 
 import com.android.internal.util.Preconditions;
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 360b875..6cea2f3 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.util;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import junit.framework.TestCase;
-
 public class PatternsTest extends TestCase {
 
     // Tests for Patterns.TOP_LEVEL_DOMAIN
diff --git a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
index 39d492d..caa1208 100644
--- a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
+++ b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/SparseLongArrayTest.java b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
index 5a5e893..df2d752 100644
--- a/core/tests/coretests/src/android/util/SparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
@@ -19,9 +19,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.support.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import android.annotation.NonNull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/StateSetTest.java b/core/tests/coretests/src/android/util/StateSetTest.java
index e481ce0..b5d6270 100644
--- a/core/tests/coretests/src/android/util/StateSetTest.java
+++ b/core/tests/coretests/src/android/util/StateSetTest.java
@@ -16,8 +16,9 @@
 
 package android.util;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 
 /**
  * Tests for {@link StateSet}
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 03b4abd..6e3ab79 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
index 7bb4ab8..77d0552 100644
--- a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
+++ b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Trace;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -28,7 +29,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 /**
  * Tests for {@link TimingsTraceLog}.
  * <p>Usage: bit FrameworksCoreTests:android.util.TimingsTraceLogTest
diff --git a/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java b/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java
index ca12a15..fc08235 100644
--- a/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java
+++ b/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java
@@ -16,12 +16,12 @@
 
 package android.util;
 
-import junit.framework.Assert;
-
 import android.test.InstrumentationTestCase;
 import android.test.TouchUtils;
 import android.view.View;
 
+import junit.framework.Assert;
+
 /**
  * When entering touch mode via touch, the tests can be flaky.  These asserts
  * are more flexible (allowing up to MAX_ATTEMPTS touches to enter touch mode via touch or
diff --git a/core/tests/coretests/src/android/view/BigCache.java b/core/tests/coretests/src/android/view/BigCache.java
index 2182176..6a1bcaa 100644
--- a/core/tests/coretests/src/android/view/BigCache.java
+++ b/core/tests/coretests/src/android/view/BigCache.java
@@ -16,16 +16,12 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
-import android.view.ViewGroup;
-import android.view.View;
-import android.view.Display;
-import android.view.ViewConfiguration;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * This activity contains two Views, one as big as the screen, one much larger. The large one
diff --git a/core/tests/coretests/src/android/view/BigCacheTest.java b/core/tests/coretests/src/android/view/BigCacheTest.java
index 8c2c865..ed4b996 100644
--- a/core/tests/coretests/src/android/view/BigCacheTest.java
+++ b/core/tests/coretests/src/android/view/BigCacheTest.java
@@ -16,14 +16,12 @@
 
 package android.view;
 
-import android.view.BigCache;
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewConfiguration;
 import android.graphics.Bitmap;
+import android.test.ActivityInstrumentationTestCase;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Builds the drawing cache of two Views, one smaller than the maximum cache size,
diff --git a/core/tests/coretests/src/android/view/BitmapDrawable.java b/core/tests/coretests/src/android/view/BitmapDrawable.java
index f7bad84..42c176f 100644
--- a/core/tests/coretests/src/android/view/BitmapDrawable.java
+++ b/core/tests/coretests/src/android/view/BitmapDrawable.java
@@ -16,19 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AbsoluteLayout;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 public class BitmapDrawable extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/view/CreateViewTest.java b/core/tests/coretests/src/android/view/CreateViewTest.java
index 16656f6..520f83f 100644
--- a/core/tests/coretests/src/android/view/CreateViewTest.java
+++ b/core/tests/coretests/src/android/view/CreateViewTest.java
@@ -16,16 +16,17 @@
 
 package android.view;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 import android.content.Context;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.test.filters.SmallTest;
+
 public class CreateViewTest extends AndroidTestCase implements PerformanceTestCase {
 
     public boolean isPerformanceOnly() {
diff --git a/core/tests/coretests/src/android/view/Disabled.java b/core/tests/coretests/src/android/view/Disabled.java
index d3c7470..ce90039 100644
--- a/core/tests/coretests/src/android/view/Disabled.java
+++ b/core/tests/coretests/src/android/view/Disabled.java
@@ -16,13 +16,12 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
-import android.widget.Button;
-import android.view.View;
-import android.view.View.OnClickListener;
 import android.app.Activity;
+import android.os.Bundle;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise View's disabled state.
diff --git a/core/tests/coretests/src/android/view/DisabledLongpressTest.java b/core/tests/coretests/src/android/view/DisabledLongpressTest.java
index faa0865..a9fec01 100644
--- a/core/tests/coretests/src/android/view/DisabledLongpressTest.java
+++ b/core/tests/coretests/src/android/view/DisabledLongpressTest.java
@@ -18,11 +18,12 @@
 
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.KeyUtils;
 import android.view.View.OnLongClickListener;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/DisabledTest.java b/core/tests/coretests/src/android/view/DisabledTest.java
index 291a11c..8922e5f 100644
--- a/core/tests/coretests/src/android/view/DisabledTest.java
+++ b/core/tests/coretests/src/android/view/DisabledTest.java
@@ -18,12 +18,11 @@
 
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.KeyEvent;
-import android.view.View;
 import android.widget.Button;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 8e4f2cd..dd50af877 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -33,17 +33,17 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.DisplayCutout.ParcelableWrapper;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 import java.util.Collections;
 
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
diff --git a/core/tests/coretests/src/android/view/DrawableBgMinSize.java b/core/tests/coretests/src/android/view/DrawableBgMinSize.java
index 58bfb8a..ba9ba00 100644
--- a/core/tests/coretests/src/android/view/DrawableBgMinSize.java
+++ b/core/tests/coretests/src/android/view/DrawableBgMinSize.java
@@ -16,12 +16,9 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.AbsoluteLayout;
 import android.widget.Button;
@@ -30,6 +27,8 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Views should obey their background {@link Drawable}'s minimum size
  * requirements ({@link Drawable#getMinimumHeight()} and
diff --git a/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java b/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java
index e705c87..1735e8c 100644
--- a/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java
+++ b/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java
@@ -16,14 +16,9 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-import android.view.DrawableBgMinSize;
-import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import android.graphics.drawable.Drawable;
 import android.test.ActivityInstrumentationTestCase;
-import android.view.View;
+import android.test.TouchUtils;
 import android.widget.AbsoluteLayout;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -31,6 +26,10 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link DrawableBgMinSize} exercises Views to obey their background drawable's
  * minimum sizes.
diff --git a/core/tests/coretests/src/android/view/FocusFinderTest.java b/core/tests/coretests/src/android/view/FocusFinderTest.java
index 2732a04..b35c64a 100644
--- a/core/tests/coretests/src/android/view/FocusFinderTest.java
+++ b/core/tests/coretests/src/android/view/FocusFinderTest.java
@@ -18,7 +18,8 @@
 
 import android.graphics.Rect;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 public class FocusFinderTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/view/GlobalFocusChange.java b/core/tests/coretests/src/android/view/GlobalFocusChange.java
index 041c0de..ee92b64 100644
--- a/core/tests/coretests/src/android/view/GlobalFocusChange.java
+++ b/core/tests/coretests/src/android/view/GlobalFocusChange.java
@@ -16,12 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.ViewTreeObserver;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class GlobalFocusChange extends Activity implements ViewTreeObserver.OnGlobalFocusChangeListener {
     public View mOldFocus;
diff --git a/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java b/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java
index dab7b90..960654a 100644
--- a/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java
+++ b/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java
@@ -17,13 +17,13 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.FlakyTest;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.Suppress;
-import android.view.View;
-import android.view.KeyEvent;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.R;
 
 @Suppress // Flaky
@@ -50,7 +50,7 @@
         super.tearDown();
     }
 
-    @FlakyTest(tolerance = 4)
+    @FlakyTest
     @LargeTest
     public void testFocusChange() throws Exception {
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
@@ -62,7 +62,7 @@
         assertSame(mRight, mActivity.mNewFocus);        
     }
 
-    @FlakyTest(tolerance = 4)
+    @FlakyTest
     @MediumTest
     public void testEnterTouchMode() throws Exception {
         assertTrue(mLeft.isFocused());
@@ -73,7 +73,7 @@
         assertSame(null, mActivity.mNewFocus);        
     }
 
-    @FlakyTest(tolerance = 4)
+    @FlakyTest
     @MediumTest
     public void testLeaveTouchMode() throws Exception {
         assertTrue(mLeft.isFocused());
diff --git a/core/tests/coretests/src/android/view/HandlerActionQueueTest.java b/core/tests/coretests/src/android/view/HandlerActionQueueTest.java
index fd8f23a..bedb75b 100644
--- a/core/tests/coretests/src/android/view/HandlerActionQueueTest.java
+++ b/core/tests/coretests/src/android/view/HandlerActionQueueTest.java
@@ -17,7 +17,8 @@
 package android.view;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 public class HandlerActionQueueTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/view/Include.java b/core/tests/coretests/src/android/view/Include.java
index e90c484..4b6aefb 100644
--- a/core/tests/coretests/src/android/view/Include.java
+++ b/core/tests/coretests/src/android/view/Include.java
@@ -16,10 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise <include /> tag in XML files.
diff --git a/core/tests/coretests/src/android/view/IncludeTest.java b/core/tests/coretests/src/android/view/IncludeTest.java
index cdcfa3c..e490559 100644
--- a/core/tests/coretests/src/android/view/IncludeTest.java
+++ b/core/tests/coretests/src/android/view/IncludeTest.java
@@ -16,13 +16,11 @@
 
 package android.view;
 
-import android.view.Include;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 public class IncludeTest extends ActivityInstrumentationTestCase<Include> {
     public IncludeTest() {
diff --git a/core/tests/coretests/src/android/view/InflateTest.java b/core/tests/coretests/src/android/view/InflateTest.java
index cb4f8e2..939d6ea 100644
--- a/core/tests/coretests/src/android/view/InflateTest.java
+++ b/core/tests/coretests/src/android/view/InflateTest.java
@@ -20,13 +20,11 @@
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import com.android.frameworks.coretests.R;
 
-import java.util.Map;
+import androidx.test.filters.SmallTest;
+
+import com.android.frameworks.coretests.R;
 
 public class InflateTest extends AndroidTestCase implements PerformanceTestCase {
     private LayoutInflater mInflater;
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index d520f15..7f00ad9 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -18,7 +18,10 @@
 
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.graphics.Insets;
@@ -26,12 +29,13 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 import android.view.SurfaceControl.Transaction;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -83,7 +87,7 @@
         consumers.put(TYPE_NAVIGATION_BAR, navConsumer);
         mController = new InsetsAnimationControlImpl(consumers,
                 new Rect(0, 0, 500, 500), state, mMockListener, WindowInsets.Type.systemBars(),
-                () -> mMockTransactionApplier);
+                () -> mMockTransactionApplier, mock(InsetsController.class));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index d3d274a..d447451 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -17,18 +17,20 @@
 package android.view;
 
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
+
 import static org.mockito.Mockito.mock;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 
 @Presubmit
 @FlakyTest(detail = "Promote once confirmed non-flaky")
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 6d0f984..82cd213 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.reset;
@@ -24,10 +25,11 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.SurfaceControl.Transaction;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index ed472d2..98ab3e7 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -17,13 +17,15 @@
 package android.view;
 
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+
 import static junit.framework.Assert.assertEquals;
 
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9807f26..9857b7a 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -24,17 +24,22 @@
 import static android.view.InsetsState.TYPE_SIDE_BAR_2;
 import static android.view.InsetsState.TYPE_SIDE_BAR_3;
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertNotEquals;
 
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseIntArray;
+import android.view.WindowInsets.Type;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -56,9 +61,12 @@
         SparseIntArray typeSideMap = new SparseIntArray();
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
                 DisplayCutout.NO_CUTOUT, typeSideMap);
-        assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+        assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
+        assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
         assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
         assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
+        assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+        assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime()));
     }
 
     @Test
@@ -70,7 +78,11 @@
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
                 DisplayCutout.NO_CUTOUT, null);
         assertEquals(100, insets.getStableInsetBottom());
-        assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+        assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all()));
+        assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
+        assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
+        assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars()));
+        assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime()));
     }
 
     @Test
@@ -81,7 +93,9 @@
         mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
                 DisplayCutout.NO_CUTOUT, null);
-        assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+        assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+        assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+        assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index b9d95e5..88d3bdb 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -20,8 +20,8 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/ListContextMenu.java b/core/tests/coretests/src/android/view/ListContextMenu.java
index 1b4ece6..e333b96 100644
--- a/core/tests/coretests/src/android/view/ListContextMenu.java
+++ b/core/tests/coretests/src/android/view/ListContextMenu.java
@@ -16,24 +16,17 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.ContextMenu;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises context menus in lists
  */
diff --git a/core/tests/coretests/src/android/view/Longpress.java b/core/tests/coretests/src/android/view/Longpress.java
index e8e6f13..86fa9e5 100644
--- a/core/tests/coretests/src/android/view/Longpress.java
+++ b/core/tests/coretests/src/android/view/Longpress.java
@@ -16,11 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class Longpress extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/core/tests/coretests/src/android/view/LongpressTest.java b/core/tests/coretests/src/android/view/LongpressTest.java
index d3d7589..4dfecd0 100644
--- a/core/tests/coretests/src/android/view/LongpressTest.java
+++ b/core/tests/coretests/src/android/view/LongpressTest.java
@@ -18,11 +18,12 @@
 
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.KeyUtils;
 import android.view.View.OnLongClickListener;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/MenuTest.java b/core/tests/coretests/src/android/view/MenuTest.java
index 116b38a..794769cc 100644
--- a/core/tests/coretests/src/android/view/MenuTest.java
+++ b/core/tests/coretests/src/android/view/MenuTest.java
@@ -17,7 +17,8 @@
 package android.view;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.frameworks.coretests.R;
 import com.android.internal.view.menu.MenuBuilder;
diff --git a/core/tests/coretests/src/android/view/Merge.java b/core/tests/coretests/src/android/view/Merge.java
index bdacd81..8020e4b 100644
--- a/core/tests/coretests/src/android/view/Merge.java
+++ b/core/tests/coretests/src/android/view/Merge.java
@@ -16,13 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
 import android.widget.LinearLayout;
-import android.view.ViewGroup;
-import android.view.LayoutInflater;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise <merge /> tag in XML files.
diff --git a/core/tests/coretests/src/android/view/MergeTest.java b/core/tests/coretests/src/android/view/MergeTest.java
index acfee7e..eccc9cb 100644
--- a/core/tests/coretests/src/android/view/MergeTest.java
+++ b/core/tests/coretests/src/android/view/MergeTest.java
@@ -16,11 +16,9 @@
 
 package android.view;
 
-import android.view.Merge;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.ViewGroup;
+
+import androidx.test.filters.MediumTest;
 
 public class MergeTest extends ActivityInstrumentationTestCase<Merge> {
     public MergeTest() {
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 023526f..cadf37e 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -22,11 +22,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/MutateDrawable.java b/core/tests/coretests/src/android/view/MutateDrawable.java
index 39b5789..1ec525d 100644
--- a/core/tests/coretests/src/android/view/MutateDrawable.java
+++ b/core/tests/coretests/src/android/view/MutateDrawable.java
@@ -18,8 +18,9 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
 import android.widget.Button;
+import android.widget.LinearLayout;
+
 import com.android.frameworks.coretests.R;
 
 public class MutateDrawable extends Activity {
diff --git a/core/tests/coretests/src/android/view/MutateDrawableTest.java b/core/tests/coretests/src/android/view/MutateDrawableTest.java
index 74e011d..d63ffb1 100644
--- a/core/tests/coretests/src/android/view/MutateDrawableTest.java
+++ b/core/tests/coretests/src/android/view/MutateDrawableTest.java
@@ -17,9 +17,8 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.MutateDrawable;
+
+import androidx.test.filters.MediumTest;
 
 public class MutateDrawableTest extends ActivityInstrumentationTestCase2<MutateDrawable> {
     private View mFirstButton;
diff --git a/core/tests/coretests/src/android/view/PinchZoomAction.java b/core/tests/coretests/src/android/view/PinchZoomAction.java
index 97fe980..bec9b55 100644
--- a/core/tests/coretests/src/android/view/PinchZoomAction.java
+++ b/core/tests/coretests/src/android/view/PinchZoomAction.java
@@ -30,8 +30,6 @@
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.action.Swiper;
 import android.support.test.espresso.util.HumanReadables;
-import android.view.MotionEvent;
-import android.view.View;
 
 import org.hamcrest.Matcher;
 
diff --git a/core/tests/coretests/src/android/view/PopupWindowVisibility.java b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
index 85ce04f..e6430d3 100644
--- a/core/tests/coretests/src/android/view/PopupWindowVisibility.java
+++ b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
diff --git a/core/tests/coretests/src/android/view/PreDrawListener.java b/core/tests/coretests/src/android/view/PreDrawListener.java
index 60bbee4..77bd36c 100644
--- a/core/tests/coretests/src/android/view/PreDrawListener.java
+++ b/core/tests/coretests/src/android/view/PreDrawListener.java
@@ -20,15 +20,12 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewTreeObserver;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.LinearLayout;
 
 import com.android.frameworks.coretests.R;
 
-
 /**
  * Tests views with popupWindows becoming invisible
  */
diff --git a/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java b/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java
index b52d98c..786c22b 100644
--- a/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java
+++ b/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java
@@ -21,10 +21,11 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/RunQueue.java b/core/tests/coretests/src/android/view/RunQueue.java
index 85dd32e..7544433 100644
--- a/core/tests/coretests/src/android/view/RunQueue.java
+++ b/core/tests/coretests/src/android/view/RunQueue.java
@@ -19,8 +19,7 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.TextView;
-import android.view.View;
-import android.view.ViewTreeObserver;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/RunQueueTest.java b/core/tests/coretests/src/android/view/RunQueueTest.java
index d69860b..f42f590 100644
--- a/core/tests/coretests/src/android/view/RunQueueTest.java
+++ b/core/tests/coretests/src/android/view/RunQueueTest.java
@@ -17,7 +17,8 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 public class RunQueueTest extends ActivityInstrumentationTestCase<RunQueue> {
     public RunQueueTest() {
diff --git a/core/tests/coretests/src/android/view/ScaleGesture.java b/core/tests/coretests/src/android/view/ScaleGesture.java
index a954a4a..235b224 100644
--- a/core/tests/coretests/src/android/view/ScaleGesture.java
+++ b/core/tests/coretests/src/android/view/ScaleGesture.java
@@ -16,16 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
 import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
-import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 public class ScaleGesture extends Activity {
     private ScaleGestureDetector mScaleGestureDetector;
diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
index fba8eae..1990135 100644
--- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
+++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
@@ -16,26 +16,22 @@
 
 package android.view;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
 import android.content.Context;
-import android.support.test.filters.LargeTest;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.DisplayMetrics;
-import android.view.PinchZoomAction;
-import android.view.ScaleGesture;
-import android.view.WindowManager;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.Espresso.onView;
 
 @LargeTest
 public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> {
diff --git a/core/tests/coretests/src/android/view/SetTagsTest.java b/core/tests/coretests/src/android/view/SetTagsTest.java
index 373dce6..1699713 100644
--- a/core/tests/coretests/src/android/view/SetTagsTest.java
+++ b/core/tests/coretests/src/android/view/SetTagsTest.java
@@ -16,12 +16,13 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import android.test.ActivityInstrumentationTestCase2;
 import android.widget.Button;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises {@link android.view.View}'s tags property.
  */
diff --git a/core/tests/coretests/src/android/view/StubbedView.java b/core/tests/coretests/src/android/view/StubbedView.java
index 612095c..c96b9b7 100644
--- a/core/tests/coretests/src/android/view/StubbedView.java
+++ b/core/tests/coretests/src/android/view/StubbedView.java
@@ -16,11 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
-import android.view.View;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise <ViewStub /> tag in XML files.
diff --git a/core/tests/coretests/src/android/view/VelocityTest.java b/core/tests/coretests/src/android/view/VelocityTest.java
index 7f32208..c116f4d 100644
--- a/core/tests/coretests/src/android/view/VelocityTest.java
+++ b/core/tests/coretests/src/android/view/VelocityTest.java
@@ -16,16 +16,17 @@
 
 package android.view;
 
-import android.test.suitebuilder.annotation.Suppress;
-import junit.framework.Assert;
-
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import junit.framework.Assert;
+
 /**
  * Exercises {@link android.view.VelocityTracker} to compute correct velocity.<br>
  * To launch this test, use :<br>
diff --git a/core/tests/coretests/src/android/view/ViewAttachTest.java b/core/tests/coretests/src/android/view/ViewAttachTest.java
index aa8f8d8..1a8dd99 100644
--- a/core/tests/coretests/src/android/view/ViewAttachTest.java
+++ b/core/tests/coretests/src/android/view/ViewAttachTest.java
@@ -16,16 +16,13 @@
 
 package android.view;
 
-import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
 import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/view/ViewAttachTestActivity.java b/core/tests/coretests/src/android/view/ViewAttachTestActivity.java
index 59e25ae..bcbc813 100644
--- a/core/tests/coretests/src/android/view/ViewAttachTestActivity.java
+++ b/core/tests/coretests/src/android/view/ViewAttachTestActivity.java
@@ -16,11 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class ViewAttachTestActivity extends Activity {
     public static final String TAG = "OnAttachedTest";
     @Override
diff --git a/core/tests/coretests/src/android/view/ViewAttachView.java b/core/tests/coretests/src/android/view/ViewAttachView.java
index 5af2d8f..2f3ff8f 100644
--- a/core/tests/coretests/src/android/view/ViewAttachView.java
+++ b/core/tests/coretests/src/android/view/ViewAttachView.java
@@ -22,7 +22,6 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.View;
 
 /**
  * A View that will throw a RuntimeException if onAttachedToWindow and
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 4405934..218047c 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -21,14 +21,15 @@
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseIntArray;
 import android.view.ViewDebug.CanvasProvider;
 import android.view.ViewDebug.HardwareCanvasProvider;
 import android.view.ViewDebug.SoftwareCanvasProvider;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
index 20e3eb5..2f51c24 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.Bundle;
+
 import com.android.frameworks.coretests.R;
 
 public class ViewCaptureTestActivity extends Activity {
diff --git a/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java
index b4ef0e7..c24cd35 100644
--- a/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java
@@ -18,7 +18,8 @@
 
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 public class ViewGroupAttributesTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/view/ViewGroupChildren.java b/core/tests/coretests/src/android/view/ViewGroupChildren.java
index f39720b..d5d9879 100644
--- a/core/tests/coretests/src/android/view/ViewGroupChildren.java
+++ b/core/tests/coretests/src/android/view/ViewGroupChildren.java
@@ -16,12 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
-import android.widget.Button;
-import android.view.View;
 import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise ViewGroup's ability to add and remove children.
diff --git a/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java b/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java
index d1665ef..07cb2a1 100644
--- a/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java
@@ -16,18 +16,16 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-import android.view.ViewGroupChildren;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
-import android.test.UiThreadTest;
-import android.view.View;
-import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises {@link android.view.ViewGroup}'s ability to add/remove children.
  */
diff --git a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
index 93ad41f0..54524b2 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
@@ -22,12 +22,13 @@
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.widget.FrameLayout;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/ViewInvalidateTest.java b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
index 115504b..c25a2deb 100644
--- a/core/tests/coretests/src/android/view/ViewInvalidateTest.java
+++ b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
@@ -21,24 +21,30 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
+import android.view.ViewTreeObserver.OnDrawListener;
 import android.widget.FrameLayout;
 
-import com.android.compatibility.common.util.WidgetTestUtils;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Test of invalidates, drawing, and the flags that support them
  */
@@ -281,4 +287,42 @@
                 View.PFLAG_DRAWN);
         assertTrue(getViewRoot(mParent).mIsAnimating);
     }
+
+    /** Copied from cts/common/device-side/util. */
+    static class WidgetTestUtils {
+        public static void runOnMainAndDrawSync(@NonNull final ActivityTestRule activityTestRule,
+                @NonNull final View view, @Nullable final Runnable runner) throws Throwable {
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            activityTestRule.runOnUiThread(() -> {
+                final OnDrawListener listener = new OnDrawListener() {
+                    @Override
+                    public void onDraw() {
+                        // posting so that the sync happens after the draw that's about to happen
+                        view.post(() -> {
+                            activityTestRule.getActivity().getWindow().getDecorView()
+                                    .getViewTreeObserver().removeOnDrawListener(this);
+                            latch.countDown();
+                        });
+                    }
+                };
+
+                activityTestRule.getActivity().getWindow().getDecorView()
+                        .getViewTreeObserver().addOnDrawListener(listener);
+
+                if (runner != null) {
+                    runner.run();
+                }
+                view.invalidate();
+            });
+
+            try {
+                Assert.assertTrue("Expected draw pass occurred within 5 seconds",
+                        latch.await(5, TimeUnit.SECONDS));
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index ca6d6cf..9a57847 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -23,9 +23,10 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/ViewStubTest.java b/core/tests/coretests/src/android/view/ViewStubTest.java
index ebd52a6..504a4ec 100644
--- a/core/tests/coretests/src/android/view/ViewStubTest.java
+++ b/core/tests/coretests/src/android/view/ViewStubTest.java
@@ -16,14 +16,12 @@
 
 package android.view;
 
-import android.view.StubbedView;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.UiThreadTest;
-import android.view.View;
-import android.view.ViewStub;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 public class ViewStubTest extends ActivityInstrumentationTestCase<StubbedView> {
     public ViewStubTest() {
diff --git a/core/tests/coretests/src/android/view/ViewTransientStateTest.java b/core/tests/coretests/src/android/view/ViewTransientStateTest.java
index 36ea01d..3f73b07 100644
--- a/core/tests/coretests/src/android/view/ViewTransientStateTest.java
+++ b/core/tests/coretests/src/android/view/ViewTransientStateTest.java
@@ -18,13 +18,12 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
 
 import com.android.frameworks.coretests.R;
 
-import static org.junit.Assert.assertFalse;
-
 /**
  * Exercise set View's transient state
  */
diff --git a/core/tests/coretests/src/android/view/Visibility.java b/core/tests/coretests/src/android/view/Visibility.java
index 031568c..8e3de4e 100644
--- a/core/tests/coretests/src/android/view/Visibility.java
+++ b/core/tests/coretests/src/android/view/Visibility.java
@@ -16,12 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
+import android.app.Activity;
 import android.os.Bundle;
 import android.widget.Button;
-import android.view.View;
-import android.app.Activity;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise View's ability to change their visibility: GONE, INVISIBLE and
diff --git a/core/tests/coretests/src/android/view/VisibilityCallback.java b/core/tests/coretests/src/android/view/VisibilityCallback.java
index f98a0a8..c965973 100644
--- a/core/tests/coretests/src/android/view/VisibilityCallback.java
+++ b/core/tests/coretests/src/android/view/VisibilityCallback.java
@@ -16,16 +16,15 @@
 
 package android.view;
 
+import android.app.Activity;
 import android.content.Context;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.widget.TextView;
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.widget.Button;
-import android.view.View;
-import android.app.Activity;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise View's ability to change their visibility: GONE, INVISIBLE and
diff --git a/core/tests/coretests/src/android/view/VisibilityCallbackTest.java b/core/tests/coretests/src/android/view/VisibilityCallbackTest.java
index ec956d2..d7c7b4c 100644
--- a/core/tests/coretests/src/android/view/VisibilityCallbackTest.java
+++ b/core/tests/coretests/src/android/view/VisibilityCallbackTest.java
@@ -17,12 +17,12 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/VisibilityTest.java b/core/tests/coretests/src/android/view/VisibilityTest.java
index 29c1c8a..83a7702 100644
--- a/core/tests/coretests/src/android/view/VisibilityTest.java
+++ b/core/tests/coretests/src/android/view/VisibilityTest.java
@@ -20,11 +20,12 @@
 import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.Button;
 import android.widget.TextView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 1c2df2c..15a96a1 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -16,12 +16,19 @@
 
 package android.view;
 
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.sideBars;
+
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowInsets.Builder;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,13 +40,13 @@
 
     @Test
     public void systemWindowInsets_afterConsuming_isConsumed() {
-        assertTrue(new WindowInsets(new Rect(1, 2, 3, 4), null, null, false, false, null)
+        assertTrue(new WindowInsets(new Rect(1, 2, 3, 4), null, false, false, null)
                 .consumeSystemWindowInsets().isConsumed());
     }
 
     @Test
     public void multiNullConstructor_isConsumed() {
-        assertTrue(new WindowInsets(null, null, null, false, false, null).isConsumed());
+        assertTrue(new WindowInsets((Rect) null, null, false, false, null).isConsumed());
     }
 
     @Test
@@ -47,4 +54,12 @@
         assertTrue(new WindowInsets((Rect) null).isConsumed());
     }
 
+    @Test
+    public void typeMap() {
+        Builder b = new WindowInsets.Builder();
+        b.setInsets(sideBars(), Insets.of(0, 0, 0, 100));
+        b.setInsets(ime(), Insets.of(0, 0, 0, 300));
+        WindowInsets insets = b.build();
+        assertEquals(300, insets.getSystemWindowInsets().bottom);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ZeroSized.java b/core/tests/coretests/src/android/view/ZeroSized.java
index f2a6b3e..9e2dfcb 100644
--- a/core/tests/coretests/src/android/view/ZeroSized.java
+++ b/core/tests/coretests/src/android/view/ZeroSized.java
@@ -16,10 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * This activity contains Views with various widths and heights. The goal is to exercise the
diff --git a/core/tests/coretests/src/android/view/ZeroSizedTest.java b/core/tests/coretests/src/android/view/ZeroSizedTest.java
index 193fc98..946bf68 100644
--- a/core/tests/coretests/src/android/view/ZeroSizedTest.java
+++ b/core/tests/coretests/src/android/view/ZeroSizedTest.java
@@ -16,13 +16,12 @@
 
 package android.view;
 
-import android.view.ZeroSized;
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
 import android.graphics.Bitmap;
+import android.test.ActivityInstrumentationTestCase;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Builds the drawing cache of Views of various dimension. The assumption is that
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 7f675ff..a88968b 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -28,10 +28,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 3e03414..46c96c9 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -21,7 +21,8 @@
 import static junit.framework.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index 318d122..ab24f89 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -22,10 +22,12 @@
 
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import libcore.util.EmptyArray;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 506e544..0ed690c 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -25,11 +25,12 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.CollectionUtils;
 
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
index 4814c61..11f4e3c 100644
--- a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
@@ -14,7 +14,7 @@
 
 package android.view.accessibility;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
new file mode 100644
index 0000000..33bc593
--- /dev/null
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 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.autofill;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AutofillIdTest {
+
+    @Test
+    public void testNonVirtual() {
+        final AutofillId id = new AutofillId(42);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isFalse();
+        assertThat(id.getVirtualChildId()).isEqualTo(View.NO_ID);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isFalse();
+        assertThat(clone.getVirtualChildId()).isEqualTo(View.NO_ID);
+    }
+
+    @Test
+    public void testVirtual() {
+        final AutofillId id = new AutofillId(42, 108);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+    }
+
+    @Test
+    public void testVirtual_parentObjectConstructor() {
+        assertThrows(NullPointerException.class, () -> new AutofillId(null, 108));
+
+        final AutofillId id = new AutofillId(new AutofillId(42), 108);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+    }
+
+    @Test
+    public void testVirtual_withSession() {
+        final AutofillId id = new AutofillId(new AutofillId(42), 108, 666);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+        assertThat(id.getSessionId()).isEqualTo(666);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+        assertThat(clone.getSessionId()).isEqualTo(666);
+    }
+
+    @Test
+    public void testEqualsHashCode() {
+        final AutofillId realId = new AutofillId(42);
+        final AutofillId realIdSame = new AutofillId(42);
+        assertThat(realId).isEqualTo(realIdSame);
+        assertThat(realIdSame).isEqualTo(realId);
+        assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode());
+
+        final AutofillId realIdDifferent = new AutofillId(108);
+        assertThat(realId).isNotEqualTo(realIdDifferent);
+        assertThat(realIdDifferent).isNotEqualTo(realId);
+
+        final AutofillId virtualId = new AutofillId(42, 1);
+        final AutofillId virtualIdSame = new AutofillId(42, 1);
+        assertThat(virtualId).isEqualTo(virtualIdSame);
+        assertThat(virtualIdSame).isEqualTo(virtualId);
+        assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode());
+        assertThat(virtualId).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualId);
+
+        final AutofillId virtualIdDifferentChild = new AutofillId(42, 2);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualIdDifferentChild);
+
+        final AutofillId virtualIdDifferentParent = new AutofillId(108, 1);
+        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent);
+        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
+
+        final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+        assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
+        assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
+
+        final AutofillId sameVirtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+        assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
+        assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
+        assertThat(sameVirtualIdDifferentSession.hashCode())
+                .isEqualTo(virtualIdDifferentSession.hashCode());
+    }
+
+    private AutofillId cloneThroughParcel(AutofillId id) {
+        Parcel parcel = Parcel.obtain();
+
+        try {
+            // Write to parcel
+            parcel.setDataPosition(0); // Sanity / paranoid check
+            id.writeToParcel(parcel, 0);
+
+            // Read from parcel
+            parcel.setDataPosition(0);
+            AutofillId clone = AutofillId.CREATOR.createFromParcel(parcel);
+            assertThat(clone).isNotNull();
+            return clone;
+        } finally {
+            parcel.recycle();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
new file mode 100644
index 0000000..ff97aa1
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2019 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.contentcapture;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * Unit test for {@link ContentCaptureSessionTest}.
+ *
+ * <p>To run it:
+ * {@code atest FrameworksCoreTests:android.view.contentcapture.ContentCaptureSessionTest}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ContentCaptureSessionTest {
+
+    private ContentCaptureSession mSession1 = new MyContentCaptureSession("111");
+
+    private ContentCaptureSession mSession2 = new MyContentCaptureSession("2222");
+
+    @Mock
+    private View mMockView;
+
+    @Test
+    public void testNewAutofillId_invalid() {
+        assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42));
+        assertThrows(IllegalArgumentException.class,
+                () -> mSession1.newAutofillId(new AutofillId(42, 42), 42));
+    }
+
+    @Test
+    public void testNewAutofillId_valid() {
+        final AutofillId parentId = new AutofillId(42);
+        final AutofillId childId = mSession1.newAutofillId(parentId, 108);
+        assertThat(childId.getViewId()).isEqualTo(42);
+        assertThat(childId.getVirtualChildId()).isEqualTo(108);
+        assertThat(childId.getSessionId()).isEqualTo(mSession1.getIdAsInt());
+    }
+
+    @Test
+    public void testNewAutofillId_differentSessions() {
+        assertThat(mSession1.getIdAsInt()).isNotSameAs(mSession2.getIdAsInt()); //sanity check
+        final AutofillId parentId = new AutofillId(42);
+        final AutofillId childId1 = mSession1.newAutofillId(parentId, 108);
+        final AutofillId childId2 = mSession2.newAutofillId(parentId, 108);
+        assertThat(childId1).isNotEqualTo(childId2);
+        assertThat(childId2).isNotEqualTo(childId1);
+    }
+
+    @Test
+    public void testNotifyXXX_null() {
+        assertThrows(NullPointerException.class, () -> mSession1.notifyViewAppeared(null));
+        assertThrows(NullPointerException.class, () -> mSession1.notifyViewDisappeared(null));
+        assertThrows(NullPointerException.class,
+                () -> mSession1.notifyViewTextChanged(null, "whatever", 0));
+    }
+
+    @Test
+    public void testNewViewStructure() {
+        assertThat(mMockView.getAutofillId()).isNotNull(); // sanity check
+        final ViewStructure structure = mSession1.newViewStructure(mMockView);
+        assertThat(structure).isNotNull();
+        assertThat(structure.getAutofillId()).isEqualTo(mMockView.getAutofillId());
+    }
+
+    @Test
+    public void testNewVirtualViewStructure() {
+        final AutofillId parentId = new AutofillId(42);
+        final ViewStructure structure = mSession1.newVirtualViewStructure(parentId, 108);
+        assertThat(structure).isNotNull();
+        final AutofillId childId = mSession1.newAutofillId(parentId, 108);
+        assertThat(structure.getAutofillId()).isEqualTo(childId);
+    }
+
+    @Test
+    public void testNotifyViewsDisappeared_invalid() {
+        // Null parent
+        assertThrows(NullPointerException.class,
+                () -> mSession1.notifyViewsDisappeared(null, new int[] {42}));
+        // Null child
+        assertThrows(IllegalArgumentException.class,
+                () -> mSession1.notifyViewsDisappeared(new AutofillId(42), null));
+        // Empty child
+        assertThrows(IllegalArgumentException.class,
+                () -> mSession1.notifyViewsDisappeared(new AutofillId(42), new int[] {}));
+        // Virtual parent
+        assertThrows(IllegalArgumentException.class,
+                () -> mSession1.notifyViewsDisappeared(new AutofillId(42, 108), new int[] {666}));
+    }
+
+    // Cannot use @Spy because we need to pass the session id on constructor
+    private class MyContentCaptureSession extends ContentCaptureSession {
+
+        private MyContentCaptureSession(String id) {
+            super(id);
+        }
+
+        @Override
+        MainContentCaptureSession getMainCaptureSession() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        ContentCaptureSession newChild(ContentCaptureContext context) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void flush(int reason) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void onDestroy() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewAppeared(ViewStructureImpl node) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewDisappeared(AutofillId id) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
new file mode 100644
index 0000000..099f41d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2019 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.contentcapture;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.os.Bundle;
+import android.os.LocaleList;
+import android.os.Parcel;
+import android.view.View;
+import android.view.ViewStructure.HtmlInfo;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import android.widget.FrameLayout;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Locale;
+
+/**
+ * Unit test for {@link ViewNode}.
+ *
+ * <p>To run it: {@code atest FrameworksCoreTests:android.view.contentcapture.ViewNodeTest}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ViewNodeTest {
+
+    private final Context mContext = InstrumentationRegistry.getTargetContext();
+
+    @Mock
+    private HtmlInfo mHtmlInfoMock;
+
+    @Test
+    public void testAutofillIdMethods_orphanView() {
+        View view = new View(mContext);
+        AutofillId initialId = new AutofillId(42);
+        view.setAutofillId(initialId);
+
+        ViewStructureImpl structure = new ViewStructureImpl(view);
+        ViewNode node = structure.getNode();
+
+        assertThat(node.getAutofillId()).isEqualTo(initialId);
+        assertThat(node.getParentAutofillId()).isNull();
+
+        AutofillId newId = new AutofillId(108);
+        structure.setAutofillId(newId);
+        assertThat(node.getAutofillId()).isEqualTo(newId);
+        assertThat(node.getParentAutofillId()).isNull();
+
+        structure.setAutofillId(new AutofillId(66), 6);
+        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6));
+        assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66));
+    }
+
+    @Test
+    public void testAutofillIdMethods_parentedView() {
+        FrameLayout parent = new FrameLayout(mContext);
+        AutofillId initialParentId = new AutofillId(48);
+        parent.setAutofillId(initialParentId);
+
+        View child = new View(mContext);
+        AutofillId initialChildId = new AutofillId(42);
+        child.setAutofillId(initialChildId);
+
+        parent.addView(child);
+
+        ViewStructureImpl structure = new ViewStructureImpl(child);
+        ViewNode node = structure.getNode();
+
+        assertThat(node.getAutofillId()).isEqualTo(initialChildId);
+        assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
+
+        AutofillId newChildId = new AutofillId(108);
+        structure.setAutofillId(newChildId);
+        assertThat(node.getAutofillId()).isEqualTo(newChildId);
+        assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
+
+        AutofillId newParentId = new AutofillId(15162342);
+        parent.setAutofillId(newParentId);
+        assertThat(node.getAutofillId()).isEqualTo(newChildId);
+        assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
+
+        structure.setAutofillId(new AutofillId(66), 6);
+        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6));
+        assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66));
+    }
+
+    @Test
+    public void testAutofillIdMethods_explicitIdsConstructor() {
+        AutofillId initialParentId = new AutofillId(42);
+        ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108, 666);
+        ViewNode node = structure.getNode();
+
+        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108, 666));
+        assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
+
+        AutofillId newChildId = new AutofillId(108);
+        structure.setAutofillId(newChildId);
+        assertThat(node.getAutofillId()).isEqualTo(newChildId);
+        assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
+
+        structure.setAutofillId(new AutofillId(66), 6);
+        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6));
+        assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66));
+    }
+
+    @Test
+    public void testInvalidSetters() {
+        View view = new View(mContext);
+        AutofillId initialId = new AutofillId(42);
+        view.setAutofillId(initialId);
+
+        ViewStructureImpl structure = new ViewStructureImpl(view);
+        ViewNode node = structure.getNode();
+        assertThat(node.getAutofillId()).isEqualTo(initialId); // sanity check
+
+        assertThrows(NullPointerException.class, () -> structure.setAutofillId(null));
+        assertThat(node.getAutofillId()).isEqualTo(initialId); // invariant
+
+        assertThrows(NullPointerException.class, () -> structure.setAutofillId(null, 666));
+        assertThat(node.getAutofillId()).isEqualTo(initialId); // invariant
+
+        assertThrows(NullPointerException.class, () -> structure.setTextIdEntry(null));
+        assertThat(node.getTextIdEntry()).isNull();
+    }
+
+    @Test
+    public void testUnsupportedProperties() {
+        View view = new View(mContext);
+
+        ViewStructureImpl structure = new ViewStructureImpl(view);
+        ViewNode node = structure.getNode();
+
+        structure.setChildCount(1);
+        assertThat(node.getChildCount()).isEqualTo(0);
+
+        structure.addChildCount(1);
+        assertThat(node.getChildCount()).isEqualTo(0);
+
+        assertThat(structure.newChild(0)).isNull();
+        assertThat(node.getChildCount()).isEqualTo(0);
+
+        assertThat(structure.asyncNewChild(0)).isNull();
+        assertThat(node.getChildCount()).isEqualTo(0);
+
+        structure.asyncCommit();
+        assertThat(node.getChildCount()).isEqualTo(0);
+
+        structure.setWebDomain("Y U NO SET?");
+        assertThat(node.getWebDomain()).isNull();
+
+        assertThat(structure.newHtmlInfoBuilder("WHATEVER")).isNull();
+
+        structure.setHtmlInfo(mHtmlInfoMock);
+        assertThat(node.getHtmlInfo()).isNull();
+
+        structure.setDataIsSensitive(true);
+
+        assertThat(structure.getTempRect()).isNull();
+    }
+
+    @Test
+    public void testValidProperties_directly() {
+        ViewStructureImpl structure = newSimpleStructure();
+        assertSimpleStructure(structure);
+        assertSimpleNode(structure.getNode());
+    }
+
+    @Test
+    public void testValidProperties_throughParcel() {
+        ViewStructureImpl structure = newSimpleStructure();
+        final ViewNode node = structure.getNode();
+        assertSimpleNode(node); // sanity check
+
+        final ViewNode clone = cloneThroughParcel(node);
+        assertSimpleNode(clone);
+    }
+
+    @Test
+    public void testComplexText_directly() {
+        ViewStructureImpl structure = newStructureWithComplexText();
+        assertStructureWithComplexText(structure);
+        assertNodeWithComplexText(structure.getNode());
+    }
+
+    @Test
+    public void testComplexText_throughParcel() {
+        ViewStructureImpl structure = newStructureWithComplexText();
+        final ViewNode node = structure.getNode();
+        assertNodeWithComplexText(node); // sanity check
+
+        ViewNode clone = cloneThroughParcel(node);
+        assertNodeWithComplexText(clone);
+    }
+
+    @Test
+    public void testVisibility() {
+        // Visibility is a special case becase it use flag masks, so we want to make sure it works
+        // fine
+        View view = new View(mContext);
+        ViewStructureImpl structure = new ViewStructureImpl(view);
+        ViewNode node = structure.getNode();
+
+        structure.setVisibility(View.VISIBLE);
+        assertThat(node.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE);
+
+        structure.setVisibility(View.GONE);
+        assertThat(node.getVisibility()).isEqualTo(View.GONE);
+        assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.GONE);
+
+        structure.setVisibility(View.VISIBLE);
+        assertThat(node.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE);
+
+        structure.setVisibility(View.INVISIBLE);
+        assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE);
+        assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.INVISIBLE);
+
+        structure.setVisibility(View.INVISIBLE | View.GONE);
+        assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE | View.GONE);
+        assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.INVISIBLE | View.GONE);
+
+
+        final int invalidValue = Math.max(Math.max(View.VISIBLE, View.INVISIBLE), View.GONE) * 2;
+        structure.setVisibility(View.VISIBLE);
+        structure.setVisibility(invalidValue); // should be ignored
+        assertThat(node.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE);
+
+        structure.setVisibility(View.GONE | invalidValue);
+        assertThat(node.getVisibility()).isEqualTo(View.GONE);
+        assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.GONE);
+    }
+
+    /**
+     * Creates a {@link ViewStructureImpl} that can be asserted through
+     * {@link #assertSimpleNode(ViewNode)}.
+     */
+    private ViewStructureImpl newSimpleStructure() {
+        View view = new View(mContext);
+        view.setAutofillId(new AutofillId(42));
+
+        ViewStructureImpl structure = new ViewStructureImpl(view);
+
+        // Basic properties
+        structure.setText("Text is set!");
+        structure.setClassName("Classy!");
+        structure.setContentDescription("Described I am!");
+        structure.setVisibility(View.INVISIBLE);
+
+        // Autofill properties
+        structure.setAutofillType(View.AUTOFILL_TYPE_TEXT);
+        structure.setAutofillHints(new String[] { "Auto", "Man" });
+        structure.setAutofillOptions(new String[] { "Maybe" });
+        structure.setAutofillValue(AutofillValue.forText("Malkovich"));
+
+        // Graphic properties
+        structure.setElevation(6.66f);
+        structure.setAlpha(66.6f);
+        structure.setTransformation(Matrix.IDENTITY_MATRIX);
+
+        // Extra text properties
+        structure.setMinTextEms(6);
+        structure.setMaxTextLength(66);
+        structure.setMaxTextEms(666);
+        structure.setInputType(42);
+        structure.setTextIdEntry("TEXT, Y U NO ENTRY?");
+        structure.setLocaleList(new LocaleList(Locale.US, Locale.ENGLISH));
+
+        // Resource id
+        structure.setId(16, "package.name", "type.name", "entry.name");
+
+        // Dimensions
+        structure.setDimens(4, 8, 15, 16, 23, 42);
+
+        // Boolean properties
+        structure.setAssistBlocked(true);
+        structure.setEnabled(true);
+        structure.setClickable(true);
+        structure.setLongClickable(true);
+        structure.setContextClickable(true);
+        structure.setFocusable(true);
+        structure.setFocused(true);
+        structure.setAccessibilityFocused(true);
+        structure.setChecked(true);
+        structure.setActivated(true);
+        structure.setOpaque(true);
+
+        // Bundle
+        assertThat(structure.hasExtras()).isFalse();
+        final Bundle bundle = structure.getExtras();
+        assertThat(bundle).isNotNull();
+        bundle.putString("Marlon", "Bundle");
+        assertThat(structure.hasExtras()).isTrue();
+        return structure;
+    }
+
+    /**
+     * Asserts the properties of a {@link ViewNode} that was created by
+     * {@link #newSimpleStructure()}.
+     */
+    private void assertSimpleNode(ViewNode node) {
+
+        // Basic properties
+        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(42));
+        assertThat(node.getParentAutofillId()).isNull();
+        assertThat(node.getText()).isEqualTo("Text is set!");
+        assertThat(node.getClassName()).isEqualTo("Classy!");
+        assertThat(node.getContentDescription().toString()).isEqualTo("Described I am!");
+        assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE);
+
+        // Autofill properties
+        assertThat(node.getAutofillType()).isEqualTo(View.AUTOFILL_TYPE_TEXT);
+        assertThat(node.getAutofillHints()).asList().containsExactly("Auto", "Man").inOrder();
+        assertThat(node.getAutofillOptions()).asList().containsExactly("Maybe").inOrder();
+        assertThat(node.getAutofillValue().getTextValue()).isEqualTo("Malkovich");
+
+        // Graphic properties
+        assertThat(node.getElevation()).isWithin(1.0e-10f).of(6.66f);
+        assertThat(node.getAlpha()).isWithin(1.0e-10f).of(66.6f);
+        assertThat(node.getTransformation()).isEqualTo(Matrix.IDENTITY_MATRIX);
+
+        // Extra text properties
+        assertThat(node.getMinTextEms()).isEqualTo(6);
+        assertThat(node.getMaxTextLength()).isEqualTo(66);
+        assertThat(node.getMaxTextEms()).isEqualTo(666);
+        assertThat(node.getInputType()).isEqualTo(42);
+        assertThat(node.getTextIdEntry()).isEqualTo("TEXT, Y U NO ENTRY?");
+        assertThat(node.getLocaleList()).isEqualTo(new LocaleList(Locale.US, Locale.ENGLISH));
+
+        // Resource id
+        assertThat(node.getId()).isEqualTo(16);
+        assertThat(node.getIdPackage()).isEqualTo("package.name");
+        assertThat(node.getIdType()).isEqualTo("type.name");
+        assertThat(node.getIdEntry()).isEqualTo("entry.name");
+
+        // Dimensions
+        assertThat(node.getLeft()).isEqualTo(4);
+        assertThat(node.getTop()).isEqualTo(8);
+        assertThat(node.getScrollX()).isEqualTo(15);
+        assertThat(node.getScrollY()).isEqualTo(16);
+        assertThat(node.getWidth()).isEqualTo(23);
+        assertThat(node.getHeight()).isEqualTo(42);
+
+        // Boolean properties
+        assertThat(node.isAssistBlocked()).isTrue();
+        assertThat(node.isEnabled()).isTrue();
+        assertThat(node.isClickable()).isTrue();
+        assertThat(node.isLongClickable()).isTrue();
+        assertThat(node.isContextClickable()).isTrue();
+        assertThat(node.isFocusable()).isTrue();
+        assertThat(node.isFocused()).isTrue();
+        assertThat(node.isAccessibilityFocused()).isTrue();
+        assertThat(node.isChecked()).isTrue();
+        assertThat(node.isActivated()).isTrue();
+        assertThat(node.isOpaque()).isTrue();
+
+        // Bundle
+        final Bundle bundle = node.getExtras();
+        assertThat(bundle).isNotNull();
+        assertThat(bundle.size()).isEqualTo(1);
+        assertThat(bundle.getString("Marlon")).isEqualTo("Bundle");
+    }
+
+    /**
+     * Asserts the properties of a {@link ViewStructureImpl} that was created by
+     * {@link #newSimpleStructure()}.
+     */
+    private void assertSimpleStructure(ViewStructureImpl structure) {
+        assertThat(structure.getAutofillId()).isEqualTo(new AutofillId(42));
+        assertThat(structure.getText()).isEqualTo("Text is set!");
+
+        // Bundle
+        final Bundle bundle = structure.getExtras();
+        assertThat(bundle.size()).isEqualTo(1);
+        assertThat(bundle.getString("Marlon")).isEqualTo("Bundle");
+    }
+
+    /**
+     * Creates a {@link ViewStructureImpl} with "complex" text properties (such as selection); it
+     * can be asserted through {@link #assertNodeWithComplexText(ViewNode)}.
+     */
+    private ViewStructureImpl newStructureWithComplexText() {
+        View view = new View(mContext);
+        ViewStructureImpl structure = new ViewStructureImpl(view);
+        structure.setText("IGNORE ME!");
+        structure.setText("Now we're talking!", 4, 8);
+        structure.setHint("Soylent Green is SPOILER ALERT");
+        structure.setTextStyle(15.0f, 16, 23, 42);
+        structure.setTextLines(new int[] {4,  8, 15} , new int[] {16, 23, 42});
+        return structure;
+    }
+
+    /**
+     * Asserts the properties of a {@link ViewNode} that was created by
+     * {@link #newStructureWithComplexText()}.
+     */
+    private void assertNodeWithComplexText(ViewNode node) {
+        assertThat(node.getText()).isEqualTo("Now we're talking!");
+        assertThat(node.getTextSelectionStart()).isEqualTo(4);
+        assertThat(node.getTextSelectionEnd()).isEqualTo(8);
+        assertThat(node.getHint()).isEqualTo("Soylent Green is SPOILER ALERT");
+        assertThat(node.getTextSize()).isWithin(1.0e-10f).of(15.0f);
+        assertThat(node.getTextColor()).isEqualTo(16);
+        assertThat(node.getTextBackgroundColor()).isEqualTo(23);
+        assertThat(node.getTextStyle()).isEqualTo(42);
+        assertThat(node.getTextLineCharOffsets()).asList().containsExactly(4, 8, 15).inOrder();
+        assertThat(node.getTextLineBaselines()).asList().containsExactly(16, 23, 42).inOrder();
+    }
+
+    /**
+     * Asserts the properties of a {@link ViewStructureImpl} that was created by
+     * {@link #newStructureWithComplexText()}.
+     */
+    private void assertStructureWithComplexText(ViewStructureImpl structure) {
+        assertThat(structure.getText()).isEqualTo("Now we're talking!");
+        assertThat(structure.getTextSelectionStart()).isEqualTo(4);
+        assertThat(structure.getTextSelectionEnd()).isEqualTo(8);
+        assertThat(structure.getHint()).isEqualTo("Soylent Green is SPOILER ALERT");
+    }
+
+    private ViewNode cloneThroughParcel(ViewNode node) {
+        Parcel parcel = Parcel.obtain();
+
+        try {
+            // Write to parcel
+            parcel.setDataPosition(0); // Sanity / paranoid check
+            ViewNode.writeToParcel(parcel, node, 0);
+
+            // Read from parcel
+            parcel.setDataPosition(0);
+            ViewNode clone = ViewNode.readFromParcel(parcel);
+            assertThat(clone).isNotNull();
+            return clone;
+        } finally {
+            parcel.recycle();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
index 0fd0136..ace6611 100644
--- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
@@ -28,11 +28,12 @@
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 import android.view.inputmethod.CursorAnchorInfo.Builder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 1b00e09..f24e232 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,9 +26,10 @@
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
index 55e5e36..9f259a8 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
@@ -20,11 +20,12 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.WindowManager;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
index 8df1848..e2fb46a 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
index c76359e..1e0e1235 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
@@ -23,10 +23,11 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
index 8c96b58..453ad72 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
@@ -23,10 +23,11 @@
 
 import android.graphics.RectF;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
index 657a7fc..ba85d76 100644
--- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
+++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.graphics.Point;
-import android.support.test.filters.MediumTest;
 import android.test.ActivityInstrumentationTestCase;
 import android.util.PollingCheck;
 import android.view.Display;
@@ -26,6 +25,8 @@
 import android.view.WindowManager;
 import android.widget.espresso.ContextMenuUtils;
 
+import androidx.test.filters.MediumTest;
+
 @MediumTest
 public class ContextMenuTest extends ActivityInstrumentationTestCase<ContextMenuActivity> {
 
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayout.java b/core/tests/coretests/src/android/view/menu/MenuLayout.java
index 356c948..33ee515 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayout.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayout.java
@@ -16,8 +16,6 @@
 
 package android.view.menu;
 
-import android.view.menu.MenuScenario.Params;
-
 import android.os.Bundle;
 import android.view.Menu;
 import android.widget.Button;
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
index 8ed0d86..ff9f166 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
@@ -16,13 +16,10 @@
 
 package android.view.menu;
 
-import android.util.KeyUtils;
-import com.android.internal.view.menu.IconMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-
-import android.content.pm.ActivityInfo;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase;
+import android.util.KeyUtils;
+
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class MenuLayoutLandscapeTest extends ActivityInstrumentationTestCase<MenuLayoutLandscape> {
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
index ccf1264..360be53 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
@@ -16,11 +16,11 @@
 
 package android.view.menu;
 
-import android.content.pm.ActivityInfo;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase;
 import android.util.KeyUtils;
 
+import androidx.test.filters.LargeTest;
+
 @LargeTest
 public class MenuLayoutPortraitTest extends ActivityInstrumentationTestCase<MenuLayoutPortrait> {
     private static final String LONG_TITLE = "Really really really really really really really really really really long title";
diff --git a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
index 82ad858..c18e361 100644
--- a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
@@ -16,17 +16,12 @@
 
 package android.view.menu;
 
-import android.view.menu.MenuWith1Item;
-import android.util.KeyUtils;
-import com.android.internal.view.menu.MenuBuilder;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.TouchUtils;
-
 import android.test.ActivityInstrumentationTestCase;
+import android.util.KeyUtils;
 import android.view.KeyEvent;
-import android.view.View;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class MenuWith1ItemTest extends ActivityInstrumentationTestCase<MenuWith1Item> {
     private MenuWith1Item mActivity;
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
index 4a6c093..780e15a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -22,8 +22,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.Person;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.google.android.textclassifier.ActionsSuggestionsModel;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
index 0180856..fef6583 100644
--- a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -29,9 +30,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.support.test.InstrumentationRegistry;
 
-import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
 
 import com.google.common.base.Preconditions;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
index aaadefb..3fc8e4c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -13,14 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.view.textclassifier;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.google.android.textclassifier.AnnotatorModel;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
index 88d162b..74b8e3b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.view.textclassifier;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -20,9 +21,10 @@
 import static org.mockito.Mockito.when;
 
 import android.os.LocaleList;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
index a3c6179..46e3a4c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 54007fb..9662182 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index a3f69d9..4fcd51c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -26,9 +26,10 @@
 import android.content.Intent;
 import android.os.LocaleList;
 import android.service.textclassifier.TextClassifierService;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index aaf7312..99c959e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -32,11 +32,12 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 81ec85e..7009fb2 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -24,11 +24,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.LocaleList;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
 import android.text.Spannable;
 import android.text.SpannableString;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -368,6 +369,7 @@
         assertThat(textLanguage, isTextLanguage("ja"));
     }
 
+    /* DISABLED: b/122467291
     @Test
     public void testSuggestConversationActions_textReplyOnly_maxThree() {
         if (isTextClassifierDisabled()) return;
@@ -376,10 +378,10 @@
                         ConversationActions.Message.PERSON_USER_REMOTE)
                         .setText("Where are you?")
                         .build();
-        ConversationActions.TypeConfig typeConfig =
-                new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+        TextClassifier.EntityConfig typeConfig =
+                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
                         .setIncludedTypes(
-                                Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+                                Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
                         .build();
         ConversationActions.Request request =
                 new ConversationActions.Request.Builder(Collections.singletonList(message))
@@ -390,12 +392,12 @@
         ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
         assertTrue(conversationActions.getConversationActions().size() > 0);
         assertTrue(conversationActions.getConversationActions().size() == 1);
-        for (ConversationActions.ConversationAction conversationAction :
+        for (ConversationAction conversationAction :
                 conversationActions.getConversationActions()) {
             assertThat(conversationAction,
-                    isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
+                    isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
         }
-    }
+    }*/
 
     @Test
     public void testSuggestConversationActions_textReplyOnly_noMax() {
@@ -405,10 +407,10 @@
                         ConversationActions.Message.PERSON_USER_REMOTE)
                         .setText("Where are you?")
                         .build();
-        ConversationActions.TypeConfig typeConfig =
-                new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+        TextClassifier.EntityConfig typeConfig =
+                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
                         .setIncludedTypes(
-                                Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+                                Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
                         .build();
         ConversationActions.Request request =
                 new ConversationActions.Request.Builder(Collections.singletonList(message))
@@ -417,10 +419,10 @@
 
         ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
         assertTrue(conversationActions.getConversationActions().size() > 1);
-        for (ConversationActions.ConversationAction conversationAction :
+        for (ConversationAction conversationAction :
                 conversationActions.getConversationActions()) {
             assertThat(conversationAction,
-                    isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
+                    isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
         }
     }
 
@@ -523,20 +525,19 @@
         };
     }
 
-    private static Matcher<ConversationActions.ConversationAction> isConversationAction(
-            String actionType) {
-        return new BaseMatcher<ConversationActions.ConversationAction>() {
+    private static Matcher<ConversationAction> isConversationAction(String actionType) {
+        return new BaseMatcher<ConversationAction>() {
             @Override
             public boolean matches(Object o) {
-                if (!(o instanceof ConversationActions.ConversationAction)) {
+                if (!(o instanceof ConversationAction)) {
                     return false;
                 }
-                ConversationActions.ConversationAction conversationAction =
-                        (ConversationActions.ConversationAction) o;
+                ConversationAction conversationAction =
+                        (ConversationAction) o;
                 if (!actionType.equals(conversationAction.getType())) {
                     return false;
                 }
-                if (ConversationActions.TYPE_TEXT_REPLY.equals(actionType)) {
+                if (ConversationAction.TYPE_TEXT_REPLY.equals(actionType)) {
                     if (conversationAction.getTextReply() == null) {
                         return false;
                     }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 1dcaed6..d0d32e3 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -21,8 +21,9 @@
 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 androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index f022d04..b9cc8f4 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -21,10 +21,11 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 2ea49f7..30cc4e8 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -21,8 +21,9 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
index 8e4f02c..5e8e582 100644
--- a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
@@ -23,13 +23,14 @@
 import static org.mockito.Mockito.mock;
 
 import android.metrics.LogMaker;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 import android.view.textclassifier.GenerateLinksLogger;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
new file mode 100644
index 0000000..b1b7416
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.logging;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.metrics.LogMaker;
+import android.view.textclassifier.ConversationAction;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassifierEvent;
+import android.view.textclassifier.TextClassifierEventTronLogger;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.logging.MetricsLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassifierEventTronLoggerTest {
+    private static final String WIDGET_TYPE = "notification";
+    private static final String PACKAGE_NAME = "pkg";
+    private static final long EVENT_TIME = System.currentTimeMillis();
+
+
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    private TextClassifierEventTronLogger mTextClassifierEventTronLogger;
+
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTextClassifierEventTronLogger = new TextClassifierEventTronLogger(mMetricsLogger);
+    }
+
+    @Test
+    public void testWriteEvent() {
+        TextClassificationContext textClassificationContext =
+                new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE)
+                        .build();
+        TextClassifierEvent textClassifierEvent =
+                new TextClassifierEvent.Builder(
+                        TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
+                        TextClassifierEvent.TYPE_SMART_ACTION)
+                        .setEntityType(ConversationAction.TYPE_CALL_PHONE)
+                        .setEventTime(EVENT_TIME)
+                        .setEventContext(textClassificationContext)
+                        .build();
+
+        mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
+
+        ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class);
+        Mockito.verify(mMetricsLogger).write(captor.capture());
+        LogMaker logMaker = captor.getValue();
+        assertThat(logMaker.getCategory()).isEqualTo(
+                CONVERSATION_ACTIONS);
+        assertThat(logMaker.getType()).isEqualTo(
+                ACTION_TEXT_SELECTION_SMART_SHARE);
+        assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE))
+                .isEqualTo(ConversationAction.TYPE_CALL_PHONE);
+        assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME))
+                .isEqualTo(EVENT_TIME);
+        assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
+        assertThat(logMaker.getTaggedData(FIELD_SELECTION_WIDGET_TYPE))
+                .isEqualTo(WIDGET_TYPE);
+    }
+
+    @Test
+    public void testWriteEvent_unsupportedCategory() {
+        TextClassifierEvent textClassifierEvent =
+                new TextClassifierEvent.Builder(
+                        TextClassifierEvent.CATEGORY_SELECTION,
+                        TextClassifierEvent.TYPE_SMART_ACTION)
+                        .build();
+
+        mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
+
+        Mockito.verify(mMetricsLogger, Mockito.never()).write(Mockito.any(LogMaker.class));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
index 4a1c414..638d894 100644
--- a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
@@ -16,15 +16,16 @@
 
 package android.view.textservice;
 
+import static android.test.MoreAsserts.assertNotEqual;
+
 import android.os.Parcel;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.util.Arrays;
 import java.util.Locale;
 
-import static android.test.MoreAsserts.assertNotEqual;
-
 /**
  * TODO: Most of part can be, and probably should be, moved to CTS.
  */
diff --git a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
index 4f31c4e..6edc162 100644
--- a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
+++ b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
@@ -24,18 +24,14 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup.OnHierarchyChangeListener;
 
-import com.android.frameworks.coretests.R;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
-import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
+import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -43,6 +39,11 @@
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+
 /**
  * Tests for AppWidgetHostView
  */
diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java
index 8e73b52..01e82a5 100644
--- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java
+++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java
@@ -18,7 +18,8 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
+
+import androidx.test.filters.FlakyTest;
 
 // TODO: tests fail intermittently. Add back MediumTest annotation when fixed
 public class AutoCompleteTextViewCallbacks
@@ -32,7 +33,7 @@
 
     /** Test that the initial popup of the suggestions does not select anything.
      */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupNoSelection() throws Exception {
         AutoCompleteTextViewSimple theActivity = getActivity();
         AutoCompleteTextView textView = theActivity.getTextView();
@@ -57,7 +58,7 @@
     }
 
     /** Test that arrow-down into the popup calls the onSelected callback. */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupEnterSelection() throws Exception {
         final AutoCompleteTextViewSimple theActivity = getActivity();
         AutoCompleteTextView textView = theActivity.getTextView();
@@ -106,7 +107,7 @@
     }
 
     /** Test that arrow-up out of the popup calls the onNothingSelected callback */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupLeaveSelection() {
         final AutoCompleteTextViewSimple theActivity = getActivity();
         AutoCompleteTextView textView = theActivity.getTextView();
diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java
index ee0abae..21e4184 100644
--- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java
+++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java
@@ -18,8 +18,8 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.FlakyTest;
 
 /**
  * A collection of tests on aspects of the AutoCompleteTextView's popup
@@ -41,7 +41,7 @@
     }
 
     /** Test that we can move the selection and it responds as expected */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupSetListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -73,7 +73,7 @@
     }
 
     /** Test that we can look at the selection as we move around */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupGetListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -100,7 +100,7 @@
     }
 
     /** Test that we can clear the selection */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupClearListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -133,7 +133,7 @@
     }
 
     /** Make sure we handle an empty adapter properly */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupNavigateNoAdapter() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -167,7 +167,7 @@
     }
 
     /** Test the show/hide behavior of the drop-down. */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupShow() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java
index b4e05aa..063bec5 100644
--- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java
+++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java
@@ -16,8 +16,6 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.util.Log;
@@ -25,6 +23,8 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemSelectedListener;
 
+import com.android.frameworks.coretests.R;
+
 public class AutoCompleteTextViewSimple extends Activity 
         implements OnItemClickListener, OnItemSelectedListener {
 
diff --git a/core/tests/coretests/src/android/widget/DatePickerActivity.java b/core/tests/coretests/src/android/widget/DatePickerActivity.java
index c3b25a1..9e455b2 100644
--- a/core/tests/coretests/src/android/widget/DatePickerActivity.java
+++ b/core/tests/coretests/src/android/widget/DatePickerActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
index be85450..f067230 100644
--- a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
+++ b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
@@ -19,11 +19,12 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 import android.view.KeyEvent;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/DateTimeViewTest.java b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
index 40a6b7a..d0bd4b8 100644
--- a/core/tests/coretests/src/android/widget/DateTimeViewTest.java
+++ b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
@@ -16,10 +16,10 @@
 
 package android.widget;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
index 9186827b..e4f55df 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -31,10 +31,11 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/ListViewTest.java b/core/tests/coretests/src/android/widget/ListViewTest.java
index 449b696..254af2a 100644
--- a/core/tests/coretests/src/android/widget/ListViewTest.java
+++ b/core/tests/coretests/src/android/widget/ListViewTest.java
@@ -16,20 +16,19 @@
 
 package android.widget;
 
-import android.test.suitebuilder.annotation.Suppress;
-import com.google.android.collect.Lists;
-
-import junit.framework.Assert;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.test.InstrumentationTestCase;
 import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.google.android.collect.Lists;
+
+import junit.framework.Assert;
 
 import java.util.List;
 
diff --git a/core/tests/coretests/src/android/widget/RadioGroupActivity.java b/core/tests/coretests/src/android/widget/RadioGroupActivity.java
index c87aa3a..dd3b30a 100644
--- a/core/tests/coretests/src/android/widget/RadioGroupActivity.java
+++ b/core/tests/coretests/src/android/widget/RadioGroupActivity.java
@@ -17,11 +17,11 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class RadioGroupActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java
index 1ab3628..18c1ea1 100644
--- a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java
+++ b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java
@@ -16,12 +16,13 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercises {@link android.widget.RadioGroup}'s check feature.
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 06b860a..da53f6d 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -38,11 +38,12 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 import com.android.internal.widget.IRemoteViewsFactory;
 
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 36792bb..8cb7e1b 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -32,12 +32,13 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
index 2add221..74aad9a 100644
--- a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
+++ b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
@@ -22,7 +22,8 @@
 
 import android.graphics.PointF;
 import android.graphics.RectF;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java b/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java
index 1731c08..a79b30d 100644
--- a/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java
@@ -16,14 +16,15 @@
 
 package android.widget;
 
-import android.test.suitebuilder.annotation.Suppress;
-import com.google.android.collect.Lists;
-
 import android.content.Context;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
+import com.google.android.collect.Lists;
 
 import java.util.ArrayList;
 import java.util.Random;
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index eafe427..483270e 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -36,13 +36,13 @@
 import static android.widget.espresso.SuggestionsPopupwindowUtils.onSuggestionsPopup;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+
 import static org.hamcrest.Matchers.is;
+
 import android.content.res.TypedArray;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.Spanned;
@@ -51,6 +51,8 @@
 import android.text.style.TextAppearanceSpan;
 import android.view.View;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index ff4a7da..41fa08b 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -43,14 +43,15 @@
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 
 import android.app.Activity;
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.Suppress;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.MotionEvent;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 90758ba..9d93421 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -27,10 +27,8 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils
-        .assertFloatingToolbarContainsItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils
-        .assertFloatingToolbarDoesNotContainItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
@@ -62,13 +60,8 @@
 import android.app.Instrumentation;
 import android.content.ClipData;
 import android.content.ClipboardManager;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.action.EspressoKey;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
-import android.test.suitebuilder.annotation.Suppress;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
@@ -85,6 +78,12 @@
 import android.view.textclassifier.TextLinksParams;
 import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java b/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java
index e9d1d3e..113db9d 100644
--- a/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java
@@ -21,8 +21,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
 import android.text.DynamicLayout;
 import android.text.FontFallbackSetup;
 import android.text.Layout;
@@ -31,6 +29,9 @@
 import android.view.View;
 import android.widget.TextView.BufferType;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
index cf173fb..a769ea4 100644
--- a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
@@ -20,14 +20,15 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.SpannedString;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 1c5610b..585360f 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -27,11 +27,6 @@
 import android.content.Intent;
 import android.graphics.Paint;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.GetChars;
 import android.text.Layout;
 import android.text.PrecomputedText;
@@ -40,6 +35,12 @@
 import android.view.View;
 import android.widget.TextView.BufferType;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java
index 75da6fe..85a4509 100644
--- a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java
+++ b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java
@@ -16,12 +16,11 @@
 
 package android.widget.focus;
 
-import android.util.InternalSelectionView;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
+import android.util.InternalSelectionView;
 import android.view.ViewGroup;
+import android.widget.LinearLayout;
 
 /**
  * {@link android.view.FocusFinder#findNextFocus(android.view.ViewGroup, android.view.View, int)}
diff --git a/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java b/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java
index f6b0520..fe6d3c8 100644
--- a/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java
+++ b/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java
@@ -16,13 +16,13 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.ViewGroup;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 public class DescendantFocusability extends Activity {
 
     public ViewGroup beforeDescendants;
diff --git a/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java b/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java
index 2af42ac..1c570df 100644
--- a/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java
+++ b/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java
@@ -16,15 +16,14 @@
 
 package android.widget.focus;
 
-import android.widget.focus.DescendantFocusability;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.UiThreadTest;
 import android.test.TouchUtils;
 import android.view.ViewGroup;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class DescendantFocusabilityTest extends ActivityInstrumentationTestCase<DescendantFocusability> {
 
     private DescendantFocusability a;
diff --git a/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java b/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java
index b3d5ec5..0cb80ce 100644
--- a/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java
+++ b/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java
@@ -16,13 +16,13 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.Button;
 import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercises cases where elements of the UI are removed (and
diff --git a/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java b/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java
index a1b7bcb..6c46d08 100644
--- a/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java
+++ b/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.focus;
 
-import android.widget.focus.FocusAfterRemoval;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.Button;
-import android.widget.LinearLayout;
 import android.view.KeyEvent;
 import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * {@link FocusAfterRemoval} is set up to exercise cases where the views that
diff --git a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java
index 8f8f184..26dc233 100644
--- a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java
+++ b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java
@@ -16,14 +16,13 @@
 
 package android.widget.focus;
 
-import android.widget.focus.AdjacentVerticalRectLists;
-import android.util.InternalSelectionView;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * {@link android.view.FocusFinder#findNextFocus(android.view.ViewGroup, android.view.View, int)}
  * and
diff --git a/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java b/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java
index dcbddef..b7974e3 100644
--- a/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java
+++ b/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java
@@ -17,10 +17,10 @@
 package android.widget.focus;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
-import android.widget.focus.GoneParentFocusedChild;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * When a parent is GONE, key events shouldn't go to its children, even if they
diff --git a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java
index 11cac1e..aab9119 100644
--- a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java
+++ b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java
@@ -17,12 +17,12 @@
 package android.widget.focus;
 
 import android.app.Activity;
-import android.widget.LinearLayout;
-import android.widget.Button;
-import android.widget.TextView;
+import android.content.Context;
 import android.os.Bundle;
 import android.view.ViewGroup;
-import android.content.Context;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 public class HorizontalFocusSearch extends Activity {
 
diff --git a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
index 43986ee..855a3219 100644
--- a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
+++ b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
@@ -16,18 +16,17 @@
 
 package android.widget.focus;
 
-import android.widget.focus.HorizontalFocusSearch;
-
-import android.support.test.filters.LargeTest;
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.LinearLayout;
-import android.widget.Button;
-import android.view.View;
-
 import static android.widget.focus.VerticalFocusSearchTest.FocusSearchAlg;
 import static android.widget.focus.VerticalFocusSearchTest.NewFocusSearchAlg;
 
+import android.test.ActivityInstrumentationTestCase;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
+
 /**
  * Tests that focus searching works on a horizontal linear layout of buttons of
  * various widths and vertical placements.
diff --git a/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java b/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java
index acd632f..ec234bc 100644
--- a/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java
+++ b/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java
@@ -21,6 +21,7 @@
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
+
 import com.android.frameworks.coretests.R;
 
 public class LinearLayoutGrid extends Activity {
diff --git a/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java b/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java
index 89cb8bb..c81317c 100644
--- a/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java
+++ b/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java
@@ -17,11 +17,11 @@
 package android.widget.focus;
 
 import android.test.SingleLaunchActivityTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.FocusFinder;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.focus.LinearLayoutGrid;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * Tests focus searching between buttons within a grid that are touching, for example,
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtons.java b/core/tests/coretests/src/android/widget/focus/ListOfButtons.java
index 308861d..5663ed2 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfButtons.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfButtons.java
@@ -16,8 +16,6 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
@@ -26,6 +24,8 @@
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * A layout with a ListView containing buttons.
  */
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
index bec6f80..4cf4a3a 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
@@ -16,17 +16,17 @@
 
 package android.widget.focus;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.focus.ListOfButtons;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListAdapter;
-import android.widget.Button;
-import android.widget.ListView;
 import android.view.KeyEvent;
 import android.view.View;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Tests that focus works as expected when navigating into and out of
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
index c2e7a26..936c999 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
@@ -21,7 +21,12 @@
 import android.os.Bundle;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
 import com.google.android.collect.Lists;
 
 import java.util.List;
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
index 53b866c..73e4ea8 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
@@ -19,11 +19,11 @@
 import android.app.Activity;
 import android.graphics.Point;
 import android.os.Bundle;
+import android.util.InternalSelectionView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.ListView;
-import android.util.InternalSelectionView;
 
 /**
  * A list of {@link InternalSelectionView}s paramatarized by the number of items,
diff --git a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java
index b908201..8accf21 100644
--- a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java
+++ b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java
@@ -27,9 +27,10 @@
 import android.widget.Button;
 import android.widget.TextView;
 
-import com.google.android.collect.Lists;
 import com.android.frameworks.coretests.R;
 
+import com.google.android.collect.Lists;
+
 import java.util.List;
 
 public class ListWithFooterViewAndNewLabels extends ListActivity {
diff --git a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java
index 57dbb78..d0fcde5 100644
--- a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java
@@ -16,14 +16,13 @@
 
 package android.widget.focus;
 
-import android.widget.focus.ListWithFooterViewAndNewLabels;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
 import android.widget.Button;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
+import com.android.frameworks.coretests.R;
+
 public class ListWithFooterViewAndNewLabelsTest
         extends ActivityInstrumentationTestCase<ListWithFooterViewAndNewLabels> {
 
diff --git a/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java b/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java
index 5c891f9..50a8614 100644
--- a/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java
+++ b/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java
@@ -16,19 +16,20 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-import com.google.android.collect.Lists;
-
 import android.app.ListActivity;
+import android.content.Context;
 import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
 import android.widget.ArrayAdapter;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.LayoutInflater;
-import android.webkit.WebView;
+
+import com.android.frameworks.coretests.R;
+
+import com.google.android.collect.Lists;
 
 import java.util.List;
 
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
index 4daf0b4..5042efd 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
@@ -16,13 +16,13 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises cases where elements of the UI are requestFocus()ed.
  */
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index cdfa217..bc770c5 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -21,16 +21,18 @@
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.AndroidRuntimeException;
 import android.view.View;
 import android.view.View.OnFocusChangeListener;
 import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
 import android.widget.Button;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
+
 import org.mockito.InOrder;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java
index 06cb75d..e6e76cc 100644
--- a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java
@@ -18,12 +18,12 @@
 
 import android.graphics.Rect;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 import android.widget.ListView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * TODO: extract base test case that launches {@link ListOfInternalSelectionViews} with
diff --git a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java
index 4a809e0..2ca9f6d 100644
--- a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java
+++ b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java
@@ -17,13 +17,13 @@
 package android.widget.focus;
 
 import android.app.Activity;
+import android.content.Context;
 import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.Button;
-import android.widget.TextView;
 import android.view.Gravity;
 import android.view.ViewGroup;
-import android.content.Context;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 /**
  * Holds a few buttons of various sizes and horizontal placements in a
diff --git a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
index f01422e..319756c 100644
--- a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
+++ b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
@@ -16,17 +16,16 @@
 
 package android.widget.focus;
 
-import android.widget.focus.VerticalFocusSearch;
-
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.FocusFinder;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
+
 /**
  * Tests that focus searching works on a vertical linear layout of buttons of
  * various widths and horizontal placements.
diff --git a/core/tests/coretests/src/android/widget/gridview/GridDelete.java b/core/tests/coretests/src/android/widget/gridview/GridDelete.java
index 57ae8f39..b040f69 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridDelete.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridDelete.java
@@ -16,6 +16,7 @@
 
 package android.widget.gridview;
 
+import android.util.GridScenario;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -23,8 +24,6 @@
 import android.widget.GridView;
 import android.widget.ListAdapter;
 
-import android.util.GridScenario;
-
 import java.util.ArrayList;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java b/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java
index 21ca655..5247009 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java
@@ -17,10 +17,9 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
-import android.widget.gridview.GridInHorizontal;
+import androidx.test.filters.MediumTest;
 
 public class GridInHorizontalTest extends ActivityInstrumentationTestCase<GridInHorizontal> {
 
diff --git a/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java b/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java
index a674db2..290f70e 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java
@@ -17,10 +17,9 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
-import android.widget.gridview.GridInVertical;
+import androidx.test.filters.MediumTest;
 
 public class GridInVerticalTest extends ActivityInstrumentationTestCase<GridInVertical> {
 
diff --git a/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java b/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java
index ecd4b1c..d2ae7cf 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java
@@ -17,9 +17,10 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
+import androidx.test.filters.MediumTest;
+
 public class GridPaddingTest extends ActivityInstrumentationTestCase2<GridPadding> {
     private GridView mGridView;
 
diff --git a/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java b/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java
index 53eeb48..466c55f 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java
@@ -19,12 +19,13 @@
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.AbsListView;
 import android.widget.GridView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class GridScrollListenerTest extends ActivityInstrumentationTestCase<GridScrollListener> implements
         AbsListView.OnScrollListener {
     private GridScrollListener mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java
index 0e362b6..db4f2dc 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.gridview;
 
-import android.util.GridScenario;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
+import android.util.GridScenario;
 import android.widget.GridView;
 
+import androidx.test.filters.MediumTest;
+
 public class GridSetSelectionBaseTest<T extends GridScenario> extends ActivityInstrumentationTestCase<T> {
     private T mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java
index 6739645..17a6044 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelectionMany;
-
 public class GridSetSelectionManyTest extends GridSetSelectionBaseTest<GridSetSelectionMany> {
     public GridSetSelectionManyTest() {
         super(GridSetSelectionMany.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java
index 46922b9..a71f732 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelectionStackFromBottomMany;
-
 public class GridSetSelectionStackFromBottomManyTest extends GridSetSelectionBaseTest<GridSetSelectionStackFromBottomMany> {
     public GridSetSelectionStackFromBottomManyTest() {
         super(GridSetSelectionStackFromBottomMany.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java
index 67dd6f1..4e6e8aa 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelectionStackFromBottom;
-
 public class GridSetSelectionStackFromBottomTest extends GridSetSelectionBaseTest<GridSetSelectionStackFromBottom> {
     public GridSetSelectionStackFromBottomTest() {
         super(GridSetSelectionStackFromBottom.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java
index 2127b3c..68802fe 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelection;
-
 public class GridSetSelectionTest extends GridSetSelectionBaseTest<GridSetSelection> {
     public GridSetSelectionTest() {
         super(GridSetSelection.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSimple.java b/core/tests/coretests/src/android/widget/gridview/GridSimple.java
index 7c2c696..67bb751 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSimple.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSimple.java
@@ -18,12 +18,11 @@
 
 import android.graphics.drawable.PaintDrawable;
 import android.os.Bundle;
+import android.util.GridScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import android.util.GridScenario;
-
 public class GridSimple extends GridScenario {
     @Override
     protected void init(Params params) {
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java b/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java
index 3b2504e..0f67522 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java
@@ -17,10 +17,9 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
-import android.widget.gridview.GridSingleColumn;
+import androidx.test.filters.MediumTest;
 
 public class GridSingleColumnTest extends ActivityInstrumentationTestCase<GridSingleColumn> {
     private GridSingleColumn mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java
index 640737e..a369616 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java
@@ -16,11 +16,10 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridStackFromBottomMany;
-
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.GridView;
 import android.test.ActivityInstrumentationTestCase;
+import android.widget.GridView;
+
+import androidx.test.filters.MediumTest;
 
 public class GridStackFromBottomManyTest extends ActivityInstrumentationTestCase<GridStackFromBottomMany> {
     private GridStackFromBottomMany mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java
index 8fec241..da1b638 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java
@@ -16,12 +16,11 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridStackFromBottom;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
+import androidx.test.filters.MediumTest;
+
 public class GridStackFromBottomTest extends ActivityInstrumentationTestCase<GridStackFromBottom> {
     private GridStackFromBottom mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridThrasher.java b/core/tests/coretests/src/android/widget/gridview/GridThrasher.java
index ad89bb6..34c19c3 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridThrasher.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridThrasher.java
@@ -16,20 +16,20 @@
 
 package android.widget.gridview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.GridView;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 import java.util.Random;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java
index ca789af..ab5fcfa 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java
@@ -17,14 +17,14 @@
 package android.widget.gridview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.View;
 import android.widget.GridView;
-
 import android.widget.gridview.GridSimple;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Tests setting the selection in touch mode
  */
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java
index 9a8d307..e312873 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.gridview.touch;
 
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
+import android.view.View;
+import android.widget.GridView;
 import android.widget.gridview.GridStackFromBottomMany;
 
-import android.widget.GridView;
-import android.view.View;
-import android.test.ActivityInstrumentationTestCase;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class GridTouchStackFromBottomManyTest extends ActivityInstrumentationTestCase<GridStackFromBottomMany> {
     private GridStackFromBottomMany mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java
index d8d4e43..c98c10a 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.gridview.touch;
 
-import android.widget.gridview.GridStackFromBottom;
-import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.widget.GridView;
+import android.test.TouchUtils;
 import android.view.View;
+import android.widget.GridView;
+import android.widget.gridview.GridStackFromBottom;
+
+import androidx.test.filters.MediumTest;
 
 public class GridTouchStackFromBottomTest extends ActivityInstrumentationTestCase<GridStackFromBottom> {
     private GridStackFromBottom mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
index 55a66d9..0d3092c 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
@@ -18,16 +18,16 @@
 
 import android.content.Context;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.GridView;
-
 import android.widget.gridview.GridVerticalSpacingStackFromBottom;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class GridTouchVerticalSpacingStackFromBottomTest extends ActivityInstrumentationTestCase<GridVerticalSpacingStackFromBottom> {
     private GridVerticalSpacingStackFromBottom mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java
index bae4ee7..e831e62 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java
@@ -17,16 +17,16 @@
 package android.widget.gridview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.GridView;
-
 import android.widget.gridview.GridVerticalSpacing;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class GridTouchVerticalSpacingTest extends ActivityInstrumentationTestCase<GridVerticalSpacing> {
     private GridVerticalSpacing mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java
index 9791e36..d159be8 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.frame;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class FrameLayoutGravity extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java
index fe4e932..0cab660 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java
@@ -16,12 +16,13 @@
 
 package android.widget.layout.frame;
 
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.ViewAsserts;
 import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.ViewAsserts;
 import android.view.View;
-import android.widget.layout.frame.FrameLayoutGravity;
+
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 public class FrameLayoutGravityTest extends ActivityInstrumentationTestCase<FrameLayoutGravity> {
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java
index 81b3ea1..8c54557 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.frame;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class FrameLayoutMargin extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java
index c052d65..4973078 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java
@@ -16,13 +16,14 @@
 
 package android.widget.layout.frame;
 
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.ViewAsserts;
 import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.ViewAsserts;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.layout.frame.FrameLayoutMargin;
+
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 public class FrameLayoutMarginTest extends ActivityInstrumentationTestCase<FrameLayoutMargin> {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java
index 766dd0a..79a34e5 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class BaselineAlignmentCenterGravity extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java
index 079e9d0..60b7c2f 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java
@@ -18,13 +18,13 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
 import android.view.View;
 import android.widget.Button;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.BaselineAlignmentCenterGravity;
 
 public class BaselineAlignmentCenterGravityTest extends ActivityInstrumentationTestCase<BaselineAlignmentCenterGravity> {
     private Button mButton1;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java
index c3bfe3a..a429b75 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java
@@ -18,12 +18,12 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.HorizontalOrientationVerticalAlignment;
 
 public class BaselineAlignmentSpinnerButton extends ActivityInstrumentationTestCase<HorizontalOrientationVerticalAlignment> {
     private View mSpinner;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java
index 5ed5e71..f33feb0 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java
@@ -16,12 +16,12 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
 
+import com.android.frameworks.coretests.R;
+
 public class BaselineAlignmentZeroWidthAndWeight extends Activity {
     @Override
     protected void onCreate(Bundle icicle) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java
index 2dd2bb8..443b0f7 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java
@@ -16,16 +16,15 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.BaselineAlignmentZeroWidthAndWeight;
-import android.widget.layout.linear.ExceptionTextView;
-
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 public class BaselineAlignmentZeroWidthAndWeightTest extends ActivityInstrumentationTestCase<BaselineAlignmentZeroWidthAndWeight> {
     private Button mShowButton;
 
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java
index c9ad831..9d91316 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class BaselineButtons extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java
index 6f1fc90..ac3c5d8 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java
@@ -18,12 +18,12 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.widget.ImageButton;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.BaselineButtons;
 
 public class BaselineButtonsTest extends ActivityInstrumentationTestCase<BaselineButtons> {
     private View mCurrentTime;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java
index 6129b5b..c684501 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java
@@ -21,7 +21,6 @@
 import android.util.AttributeSet;
 import android.widget.EditText;
 
-
 /**
  * A special EditText that sets {@link #isFailed()} to true as its internal makeNewLayout() method is called
  * with a width lower than 0. This is used to fail the unit test in
diff --git a/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java b/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java
index 50aa5b7..c8a5f34 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java
@@ -16,12 +16,12 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 public class FillInWrap extends Activity {
     @Override
     protected void onCreate(Bundle icicle) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java b/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java
index f161802..0e69efc 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java
@@ -18,9 +18,10 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 public class FillInWrapTest extends ActivityInstrumentationTestCase<FillInWrap> {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java b/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java
index 9f937a9..255f7b3 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class HorizontalOrientationVerticalAlignment extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java b/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java
index 83331ca..a0745dd 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java
@@ -16,14 +16,14 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 
+import com.android.frameworks.coretests.R;
+
 public class LLEditTextThenButton extends Activity {
     private EditText mEditText;
     private Button mButton;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java
index ab2f060..1153062 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java
@@ -18,10 +18,10 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.view.View;
 import android.widget.Button;
 import android.widget.LinearLayout;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java
index 77f564d..0aca699 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java
@@ -22,5 +22,4 @@
  */
 public class LLOfButtons2  extends LLOfButtons1 {
 
-
 }
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java
index 90db788..5d37245 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class LinearLayoutEditTexts extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java
index d5998b7..966ed6d 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.linear;
 
-import android.widget.layout.linear.LinearLayoutEditTexts;
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
 import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase;
+import android.view.View;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 public class LinearLayoutEditTextsTest extends ActivityInstrumentationTestCase<LinearLayoutEditTexts> {
     private View mChild;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/Weight.java b/core/tests/coretests/src/android/widget/layout/linear/Weight.java
index 20edd7c..3722a14f 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/Weight.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/Weight.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class Weight extends Activity {
     @Override
     protected void onCreate(Bundle icicle) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java b/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java
index 2e421da..144600b 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class WeightSum extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java b/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java
index f9a94ce..1d07db2 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java
@@ -18,11 +18,11 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.WeightSum;
 
 public class WeightSumTest extends ActivityInstrumentationTestCase<WeightSum> {
     private View mChild;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java b/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java
index 1c42e7c..db52495 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java
@@ -18,13 +18,13 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.Weight;
 
 @Suppress // Failing.
 public class WeightTest extends ActivityInstrumentationTestCase<Weight> {
diff --git a/core/tests/coretests/src/android/widget/layout/table/AddColumn.java b/core/tests/coretests/src/android/widget/layout/table/AddColumn.java
index 400c32c..b407941 100644
--- a/core/tests/coretests/src/android/widget/layout/table/AddColumn.java
+++ b/core/tests/coretests/src/android/widget/layout/table/AddColumn.java
@@ -16,8 +16,6 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
@@ -26,6 +24,8 @@
 import android.widget.TableRow;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * This test adds an extra row with an extra column in the table.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java b/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java
index bfb4d17..08ae030 100644
--- a/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java
@@ -16,16 +16,16 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.AddColumn;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.TableLayout;
 import android.widget.TableRow;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.AddColumn} is
  * setup to exercise the case of adding row programmatically in a table.
diff --git a/core/tests/coretests/src/android/widget/layout/table/CellSpan.java b/core/tests/coretests/src/android/widget/layout/table/CellSpan.java
index d91cf56..3102ac1 100644
--- a/core/tests/coretests/src/android/widget/layout/table/CellSpan.java
+++ b/core/tests/coretests/src/android/widget/layout/table/CellSpan.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells spanning.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java b/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java
index 331ec45..aa8e66c 100644
--- a/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.CellSpan;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.CellSpan} is
  * setup to exercise tables in which cells use spanning.
diff --git a/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java b/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java
index 435815a..c587f74 100644
--- a/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java
+++ b/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells having a fixed width and height.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java b/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java
index b20ec84..7d02453 100644
--- a/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.FixedWidth;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.FixedWidth} is
  * setup to exercise tables in which cells use fixed width and height.
diff --git a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java
index 1444f60..6f5148b 100644
--- a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells using a horizontal gravity.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java
index 964df82..73e8334 100644
--- a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.HorizontalGravity;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.HorizontalGravity} is
  * setup to exercise tables in which cells use horizontal gravity.
diff --git a/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java b/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java
index 4fdb378..9055b32 100644
--- a/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells using a vertical gravity.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java b/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java
index 1d6be3f..f14fa1c 100644
--- a/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.VerticalGravity;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.test.ViewAsserts;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.VerticalGravity} is
  * setup to exercise tables in which cells use vertical gravity.
diff --git a/core/tests/coretests/src/android/widget/layout/table/Weight.java b/core/tests/coretests/src/android/widget/layout/table/Weight.java
index 6d4d51d..5c08247 100644
--- a/core/tests/coretests/src/android/widget/layout/table/Weight.java
+++ b/core/tests/coretests/src/android/widget/layout/table/Weight.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells having a weight.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/WeightTest.java b/core/tests/coretests/src/android/widget/layout/table/WeightTest.java
index b665573..fcf3de2 100644
--- a/core/tests/coretests/src/android/widget/layout/table/WeightTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/WeightTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.Weight;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.Weight} is
  * setup to exercise tables in which cells use a weight.
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java
index a386ebd..cd76d70 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.view.Gravity;
-
 import android.util.ListScenario;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java
index 519816c..e048e3e 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.view.Gravity;
-
 import android.util.ListScenario;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java
index e1171eb..bd8dbe4 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java
@@ -17,10 +17,9 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
-import android.widget.listview.ListBottomGravityMany;
+import androidx.test.filters.MediumTest;
 
 public class ListBottomGravityManyTest extends ActivityInstrumentationTestCase<ListBottomGravityMany> {
     private ListBottomGravityMany mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java
index c595f62..8da7358 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java
@@ -17,10 +17,9 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
-import android.widget.listview.ListBottomGravity;
+import androidx.test.filters.MediumTest;
 
 public class ListBottomGravityTest extends ActivityInstrumentationTestCase<ListBottomGravity> {
     private ListBottomGravity mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java
index bbed73c..8640741 100644
--- a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java
+++ b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java
@@ -16,10 +16,10 @@
 
 package android.widget.listview;
 
-import android.util.ListItemFactory;
 import static android.util.ListItemFactory.Slot;
-import android.util.ListScenario;
 
+import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
diff --git a/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java b/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java
index 258d3ef..81d71a9 100644
--- a/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java
@@ -19,12 +19,13 @@
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class ListEmptyViewTest extends ActivityInstrumentationTestCase<ListWithEmptyView> {
     private ListWithEmptyView mActivity;
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/ListFilter.java b/core/tests/coretests/src/android/widget/listview/ListFilter.java
index c2ac90e..1cda717 100644
--- a/core/tests/coretests/src/android/widget/listview/ListFilter.java
+++ b/core/tests/coretests/src/android/widget/listview/ListFilter.java
@@ -25,7 +25,6 @@
 
 import com.android.frameworks.coretests.R;
 
-
 /**
  * Tests hiding and showing the list filter by hiding and showing an ancestor of the 
  * ListView
diff --git a/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java b/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java
index bf18a13..10b4a79 100644
--- a/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java
@@ -17,10 +17,11 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
-import android.widget.ListAdapter;
 import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import androidx.test.filters.MediumTest;
 
 public class ListFocusableTest extends ActivityInstrumentationTestCase<ListTopGravity> {
     private ListTopGravity mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java b/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java
index 33d61a0..c691ed7 100644
--- a/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java
@@ -17,10 +17,11 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
 import java.util.Arrays;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java
index 1f59c30..74eda3b 100644
--- a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java
+++ b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java
@@ -16,11 +16,10 @@
 
 package android.widget.listview;
 
-import android.view.View;
-import android.view.ViewGroup;
-
 import android.util.ListItemFactory;
 import android.util.ListScenario;
+import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * List that has different view types
diff --git a/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java b/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java
index 01b39db..dbd58d7 100644
--- a/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java
@@ -18,12 +18,11 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.ListView;
 
-import android.widget.listview.ListHeterogeneous;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class ListHeterogeneousTest extends ActivityInstrumentationTestCase<ListHeterogeneous> {
     private ListHeterogeneous mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java
index 2ff65de..e98de9c 100644
--- a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java
+++ b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java
@@ -16,11 +16,11 @@
 
 package android.widget.listview;
 
-import android.util.ListItemFactory;
 import static android.util.ListItemFactory.Slot;
-import android.util.ListScenario;
 
 import android.content.Context;
+import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
diff --git a/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java b/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java
index a373a5b..6622c0d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java
@@ -18,10 +18,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.Handler;
 import android.widget.ArrayAdapter;
-import android.widget.GridView;
-import android.widget.TextView;
 import android.widget.ListView;
 
 import com.android.frameworks.coretests.R;
diff --git a/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java b/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java
index 3643f79..d8508ec 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.widget.listview.ListInHorizontal;
-
 public class ListInHorizontalTest extends ListUnspecifiedMeasure<ListInHorizontal> {
     public ListInHorizontalTest() {
         super(ListInHorizontal.class);
diff --git a/core/tests/coretests/src/android/widget/listview/ListInVertical.java b/core/tests/coretests/src/android/widget/listview/ListInVertical.java
index 3b4885a..4ecf25d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInVertical.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInVertical.java
@@ -18,10 +18,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.Handler;
 import android.widget.ArrayAdapter;
-import android.widget.GridView;
-import android.widget.TextView;
 import android.widget.ListView;
 
 import com.android.frameworks.coretests.R;
diff --git a/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java b/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java
index 8586429..5e385a3 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.widget.listview.ListInVertical;
-
 public class ListInVerticalTest extends ListUnspecifiedMeasure<ListInVertical> {
     public ListInVerticalTest() {
         super(ListInVertical.class);
diff --git a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java
index d5da28e..0ec7a24 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java
@@ -16,13 +16,14 @@
 
 package android.widget.listview;
 
+import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
- import android.view.ViewGroup;
- import com.google.android.collect.Sets;
- import android.util.ListScenario;
- import android.util.ListItemFactory;
+import android.view.ViewGroup;
 
- import java.util.Set;
+import com.google.android.collect.Sets;
+
+import java.util.Set;
 
 /**
  * List that interleaves focusable items.
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java
index f7c01b1..3159e53 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java
@@ -16,12 +16,10 @@
 
 package android.widget.listview;
 
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
 import android.util.ListItemFactory;
 import android.util.ListScenario;
+import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * A list where the items may befocusable, but the second item isn't actually focusabe.
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java
index b529b2e..861e2a91 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java
@@ -16,8 +16,8 @@
 
 package android.widget.listview;
 
-import android.util.ListScenario;
 import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java
index 59987ec..e9c9c1d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java
@@ -16,10 +16,10 @@
 
 package android.widget.listview;
 
-import android.view.View;
-import android.view.ViewGroup;
 import android.util.ListItemFactory;
 import android.util.ListScenario;
+import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * A list where each item is tall with buttons that are farther apart than the screen
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java
index ea2c5f2..2a0e013 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java
@@ -17,13 +17,13 @@
 package android.widget.listview;
 
 import android.content.Context;
+import android.util.InternalSelectionView;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.util.InternalSelectionView;
-import android.util.ListScenario;
 
 /**
  * Each item is an internal selection view, a button, and some filler
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java b/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java
index 73eb0a8..91ff06b 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java
@@ -18,13 +18,13 @@
 
 import android.graphics.Rect;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.view.View;
 import android.view.KeyEvent;
+import android.view.View;
 import android.widget.ListView;
-import android.widget.listview.ListOfThinItems;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class ListItemRequestRectAboveThinFirstItemTest
         extends ActivityInstrumentationTestCase<ListOfThinItems> {
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java
index a5fe17a..d80fd90 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java
@@ -17,13 +17,12 @@
 package android.widget.listview;
 
 import android.content.Context;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.TextView;
 
-import android.util.ListScenario;
-
 /**
  * A list where each item expands by 1.5 when selected.
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java b/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java
index 12b5ef4..54f302c 100644
--- a/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java
+++ b/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java
@@ -20,14 +20,13 @@
 import android.content.Intent;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.provider.Settings;
 import android.provider.Contacts.People;
+import android.provider.Settings;
 import android.view.View;
 import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListAdapter;
 import android.widget.SimpleCursorAdapter;
-import android.widget.AdapterView.OnItemClickListener;
-
 
 public class ListManagedCursor extends ListActivity implements OnItemClickListener {
     
diff --git a/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
index bc3776c..8e0b6fe 100644
--- a/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
@@ -18,11 +18,12 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.TouchUtils;
 import android.view.KeyEvent;
 import android.widget.ListView;
-import android.test.TouchUtils;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * Tests restoring the scroll position in a list with a managed cursor.
diff --git a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java
index 919ef69..70b9081 100644
--- a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java
+++ b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java
@@ -16,12 +16,11 @@
 
 package android.widget.listview;
 
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 
-import android.util.ListScenario;
-
 /**
  * Each list item has two focusables that are close enough together that
  * it shouldn't require panning to move focus.
diff --git a/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java b/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java
index 76814fb..075712e 100644
--- a/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java
+++ b/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java
@@ -18,11 +18,11 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.ImageButton;
-import android.view.ViewDebug;
 import android.view.View;
+import android.view.ViewDebug;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ListView;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java b/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java
index 896bd19..be14de8 100644
--- a/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java
@@ -16,12 +16,11 @@
 
 package android.widget.listview;
 
-import android.widget.listview.ListItemFocusablesClose;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 
+import androidx.test.filters.MediumTest;
+
 public class ListRetainsFocusAcrossLayoutsTest extends ActivityInstrumentationTestCase<ListItemFocusablesClose> {
 
     public ListRetainsFocusAcrossLayoutsTest() {
diff --git a/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java b/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java
index 7b29a66..28addd6 100644
--- a/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java
@@ -19,12 +19,13 @@
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.AbsListView;
 import android.widget.ListView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class ListScrollListenerTest extends ActivityInstrumentationTestCase<ListScrollListener> implements
         AbsListView.OnScrollListener {
     private ListScrollListener mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java
index 6c2e264..af8e899 100644
--- a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java
+++ b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java
@@ -16,12 +16,12 @@
 
 package android.widget.listview;
 
+import android.os.Bundle;
 import android.util.ListScenario;
 import android.view.KeyEvent;
 import android.view.View;
-import android.os.Bundle;
-import android.widget.LinearLayout;
 import android.widget.Button;
+import android.widget.LinearLayout;
 
 /**
  * List of 1,000 items used to test calls to setSelection() in touch mode.
diff --git a/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java b/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java
index 4cef164..2caca13 100644
--- a/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java
@@ -17,10 +17,11 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Basic tests of setting & clearing the selection
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListSimple.java b/core/tests/coretests/src/android/widget/listview/ListSimple.java
index 6accae1..f53638e 100644
--- a/core/tests/coretests/src/android/widget/listview/ListSimple.java
+++ b/core/tests/coretests/src/android/widget/listview/ListSimple.java
@@ -16,11 +16,10 @@
 
 package android.widget.listview;
 
+import android.os.Bundle;
 import android.util.ListScenario;
-
 import android.view.View;
 import android.view.ViewGroup;
-import android.os.Bundle;
 import android.widget.TextView;
 
 public class ListSimple extends ListScenario {
diff --git a/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java b/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java
index 95f09f6..c4e9fe9 100644
--- a/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java
+++ b/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java
@@ -16,17 +16,17 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.widget.BaseAdapter;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises moving focus into the list from the side
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListThrasher.java b/core/tests/coretests/src/android/widget/listview/ListThrasher.java
index 0237a95..d82f68d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListThrasher.java
+++ b/core/tests/coretests/src/android/widget/listview/ListThrasher.java
@@ -16,19 +16,19 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 import java.util.Random;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java
index 986cc57..31339e9 100644
--- a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java
+++ b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.view.Gravity;
-
 import android.util.ListScenario;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java b/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java
index 199d069..0fc87ea 100644
--- a/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java
+++ b/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java
@@ -16,13 +16,14 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
 import android.app.Activity;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.ActivityInstrumentationTestCase;
 import android.widget.ListView;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 public class ListUnspecifiedMeasure<T extends Activity> extends ActivityInstrumentationTestCase<T> {
     private T mActivity;
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java b/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java
index 5ab2757..63941f1 100644
--- a/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java
@@ -18,12 +18,12 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.Button;
 import android.widget.ListView;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.listview.ListViewHeight;
 
 public class ListViewHeightTest extends ActivityInstrumentationTestCase<ListViewHeight> {
     private ListViewHeight mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java b/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java
index 348ea1b..10ba8b7 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.database.Cursor;
 import android.os.Bundle;
@@ -32,6 +30,8 @@
 import android.widget.SimpleCursorAdapter;
 import android.widget.Toast;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * See 1080989. You need some contacts for this adapter.
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java b/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java
index 74dd06c..52273fd 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
@@ -26,6 +24,8 @@
 import android.view.View;
 import android.widget.ArrayAdapter;
 
+import com.android.frameworks.coretests.R;
+
 
 /**
  * Tests using an empty view with a list */
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java
index aea091a..6030582 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java
@@ -16,9 +16,8 @@
 
 package android.widget.listview;
 
-import android.util.ListScenario;
-
 import android.os.Bundle;
+import android.util.ListScenario;
 import android.widget.Button;
 import android.widget.ListAdapter;
 import android.widget.ListView;
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java
index 26e1d5d..13e770c 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java
@@ -16,8 +16,8 @@
 
 package android.widget.listview;
 
-import android.widget.TextView;
 import android.util.ListScenario;
+import android.widget.TextView;
 
 /**
  * The header text view echos the value of the selected item by using (indirectly)
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java
index ec8ab7e..22c28c2 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.ListUtil;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListInterleaveFocusables;
 
+import androidx.test.filters.MediumTest;
+
 public class ListInterleaveFocusablesTest extends ActivityInstrumentationTestCase2<ListInterleaveFocusables> {
     private ListView mListView;
     private ListUtil mListUtil;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java
index 82f48801..c645b7c 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java
@@ -17,11 +17,12 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListItemFocusableAboveUnfocusable;
 
+import androidx.test.filters.MediumTest;
+
 public class ListItemFocusableAboveUnfocusableTest extends ActivityInstrumentationTestCase<ListItemFocusableAboveUnfocusable> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java
index 3b30ebe..c7525b3 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.listview.ListItemFocusablesClose;
 
+import androidx.test.filters.MediumTest;
+
 public class ListItemFocusablesCloseTest extends ActivityInstrumentationTestCase<ListItemFocusablesClose> {
     private ListView mListView;
     private int mListTop;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java
index 475930d..4bb2206 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java
@@ -17,7 +17,6 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -25,6 +24,8 @@
 import android.widget.ListView;
 import android.widget.listview.ListItemFocusablesFarApart;
 
+import androidx.test.filters.MediumTest;
+
 public class ListItemFocusablesFarApartTest extends ActivityInstrumentationTestCase<ListItemFocusablesFarApart> {
     private ListView mListView;
     private int mListTop;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java
index 91a1eba..7605291 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListItemsExpandOnSelection;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListItemsExpandOnSelectionTest extends ActivityInstrumentationTestCase<ListItemsExpandOnSelection> {
     private ListView mListView;
     private int mListTop;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java
index 5bc121a..fdae483 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
-import android.view.View;
 import android.view.KeyEvent;
+import android.view.View;
+import android.widget.ListView;
 import android.widget.listview.ListLastItemPartiallyVisible;
 
+import androidx.test.filters.MediumTest;
+
 public class ListLastItemPartiallyVisibleTest extends ActivityInstrumentationTestCase<ListLastItemPartiallyVisible> {
     private ListView mListView;
     private int mListBottom;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java
index bda71d0..d44b130 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java
@@ -17,14 +17,15 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.listview.ListOfItemsShorterThanScreen;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfItemsShorterThanScreenTest
         extends ActivityInstrumentationTestCase<ListOfItemsShorterThanScreen> {
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java
index 2135445..1decdad 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListOfItemsTallerThanScreen;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfItemsTallerThanScreenTest
         extends ActivityInstrumentationTestCase2<ListOfItemsTallerThanScreen> {
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java
index ef70b5a..67808f1 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.ListUtil;
 import android.view.KeyEvent;
 import android.widget.ListView;
 import android.widget.listview.ListOfShortShortTallShortShort;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationTestCase2<ListOfShortShortTallShortShort> {
     private ListView mListView;
     private ListUtil mListUtil;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java
index c958591..f9aa6dc 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java
@@ -17,11 +17,12 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListOfShortTallShort;
 
+import androidx.test.filters.MediumTest;
+
 public class ListOfShortTallShortTest extends ActivityInstrumentationTestCase<ListOfShortTallShort> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java
index c191d71..6bd1cb5 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java
@@ -17,14 +17,15 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListOfThinItems;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfThinItemsTest extends ActivityInstrumentationTestCase<ListOfThinItems> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
index 9a8e634..60bc115 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
@@ -16,12 +16,13 @@
 
 package android.widget.listview.arrowscroll;
 
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 import android.view.KeyEvent;
+import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.listview.ListWithFirstScreenUnSelectable;
-import android.widget.AdapterView;
+
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class ListWithFirstScreenUnSelectableTest
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java
index 56ca009..a60054e 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.ListView;
 import android.widget.listview.ListWithNoFadingEdge;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class ListWithNoFadingEdgeTest extends ActivityInstrumentationTestCase<ListWithNoFadingEdge> {
 
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java
index cf319d1..80e70c6 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java
@@ -17,14 +17,15 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.listview.ListWithOffScreenNextSelectable;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 @Suppress // Failing.
 public class ListWithOffScreenNextSelectableTest
         extends ActivityInstrumentationTestCase<ListWithOffScreenNextSelectable> {
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java
index feea9b2..819b739 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.view.KeyEvent;
 import android.widget.ListView;
 import android.widget.TextView;
-import android.view.KeyEvent;
 import android.widget.listview.ListWithOnItemSelectedAction;
 
+import androidx.test.filters.MediumTest;
+
 public class ListWithOnItemSelectedActionTest extends ActivityInstrumentationTestCase<ListWithOnItemSelectedAction> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java
index 211c8c8..9bbdc2a 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListWithScreenOfNoSelectables;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListWithScreenOfNoSelectablesTest extends ActivityInstrumentationTestCase2<ListWithScreenOfNoSelectables> {
 
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java
index 42058f0..21fa51d 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java
@@ -17,11 +17,12 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListWithSeparators;
 
+import androidx.test.filters.MediumTest;
+
 public class ListWithSeparatorsTest extends ActivityInstrumentationTestCase<ListWithSeparators> {
     private ListWithSeparators mActivity;
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java
index 6a7466b..e9baabf 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.listview.focus;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.listview.AdjacentListsWithAdjacentISVsInside;
-import android.util.InternalSelectionView;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 import android.widget.ListView;
+import android.widget.listview.AdjacentListsWithAdjacentISVsInside;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class AdjacentListsWithAdjacentISVsInsideTest extends ActivityInstrumentationTestCase<AdjacentListsWithAdjacentISVsInside> {
 
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java
index 5540d65..3fbdacc 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.listview.focus;
 
-import android.widget.listview.ListButtonsDiagonalAcrossItems;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ListView;
+import android.widget.listview.ListButtonsDiagonalAcrossItems;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * Test that ListView will override default behavior of focus searching to
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
index edc60b5..5448952 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.focus;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.ListView;
 import android.widget.listview.ListHorizontalFocusWithinItemWins;
 
+import androidx.test.filters.MediumTest;
+
 public class ListHorizontalFocusWithinItemWinsTest extends ActivityInstrumentationTestCase<ListHorizontalFocusWithinItemWins> {
 
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java
index b449b61..0b17aaf 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java
@@ -17,17 +17,18 @@
 package android.widget.listview.focus;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.AbsListView;
 import android.widget.ListView;
 import android.widget.listview.ListWithEditTextHeader;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListWithEditTextHeaderTest extends ActivityInstrumentationTestCase2<ListWithEditTextHeader> {
     private ListView mListView;
 
@@ -49,7 +50,7 @@
         assertTrue("header does not have focus", mListView.getChildAt(0).isFocused());
     }
 
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     @LargeTest
     public void testClickingHeaderKeepsFocus() {
         TouchUtils.clickView(this, mListView.getChildAt(0));
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java
index 28f899e..262d0f4 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java
@@ -17,14 +17,14 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
-import android.widget.ListView;
 import android.view.View;
-
+import android.widget.ListView;
 import android.widget.listview.ListGetSelectedView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * This test is made to check that getSelectedView() will return
  * null in touch mode.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java
index ffa9a5e..48e8c5e 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java
@@ -17,15 +17,14 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.ListView;
-
 import android.widget.listview.ListOfTouchables;
-import android.test.TouchUtils;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * Touch tests for a list where all of the items fit on the screen.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java
index aed513a..716683a 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java
@@ -17,15 +17,15 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.View;
 import android.widget.ListView;
-
 import android.widget.listview.ListSimple;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 /**
  * Tests setting the selection in touch mode
  */
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java
index 7daf64e..6a1f076 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java
@@ -17,16 +17,16 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.ListView;
-
 import android.widget.listview.ListBottomGravityMany;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items do not fit on the screen, and the list 
  * stacks from the bottom.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java
index 4086cf0..89498d66 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java
@@ -17,13 +17,13 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.View;
 import android.widget.ListView;
-
 import android.widget.listview.ListBottomGravity;
 
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items fit on the screen, and the list 
  * stacks from the bottom.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java
index 30d56ca..c7f2371 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java
@@ -17,16 +17,16 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.ListView;
-
 import android.widget.listview.ListTopGravityMany;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items do not fit on the screen.
  */
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java
index 5b064b3..8a557ab 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java
@@ -17,13 +17,13 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.View;
 import android.widget.ListView;
-
 import android.widget.listview.ListTopGravity;
 
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items fit on the screen.
  */
diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java
index a7f5c05..a30985b 100644
--- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java
+++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java
@@ -18,7 +18,6 @@
 
 import android.util.InternalSelectionView;
 import android.util.ScrollViewScenario;
-
 import android.widget.Button;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java
index 8123228..825aa1a 100644
--- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java
@@ -16,14 +16,13 @@
 
 package android.widget.scroll;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.scroll.ButtonAboveTallInternalSelectionView;
-import android.util.InternalSelectionView;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 @Suppress // Failing.
 public class ButtonAboveTallInternalSelectionViewTest extends
         ActivityInstrumentationTestCase<ButtonAboveTallInternalSelectionView> {
diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java
index 3d5f86d..47d36dd 100644
--- a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java
+++ b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java
@@ -17,7 +17,6 @@
 package android.widget.scroll;
 
 import android.util.ScrollViewScenario;
-
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java
index afc275f..dba07a0 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java
@@ -16,15 +16,15 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
-import android.os.Bundle;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.view.View;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * A screen with some scenarios that exercise {@link ScrollView}'s implementation
  * of {@link android.view.ViewGroup#requestChildRectangleOnScreen}:
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java
index f8abdb2..7c3df91 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java
@@ -16,19 +16,19 @@
 
 package android.widget.scroll;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.scroll.RequestRectangleVisible;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
+import android.view.KeyEvent;
+import android.view.View;
 import android.widget.Button;
 import android.widget.ScrollView;
 import android.widget.TextView;
-import android.view.View;
-import android.view.KeyEvent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * {@link RequestRectangleVisible} is set up to exercise the cases of moving a
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java
index 731b25a..105686a 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java
@@ -16,15 +16,14 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
-import android.os.Bundle;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Button;
-import android.view.View;
 import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 public class RequestRectangleVisibleWithInternalScroll extends Activity {
 
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java
index 5e9b520..1a221ff 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java
@@ -16,16 +16,17 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
 import android.test.ViewAsserts;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * This is suppressed because {@link TextView#scrollBy} isn't working.
  */
diff --git a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java
index 027ea0f..92a3152 100644
--- a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java
+++ b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java
@@ -16,15 +16,14 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
+import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
-import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
 
 /**
  * Basic scroll view example
diff --git a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java
index 3fd17ee..8d71f84 100644
--- a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java
@@ -17,14 +17,14 @@
 package android.widget.scroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class ScrollViewButtonsAndLabelsTest
         extends ActivityInstrumentationTestCase<ScrollViewButtonsAndLabels> {
diff --git a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java
index 3a0f29a..90ede7d 100644
--- a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java
+++ b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java
@@ -17,7 +17,6 @@
 package android.widget.scroll;
 
 import android.util.ScrollViewScenario;
-
 import android.widget.Button;
 import android.widget.LinearLayout;
 
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java
index 56d7ed2..04f5ac8 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java
@@ -16,16 +16,16 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.widget.scroll.ButtonsWithTallTextViewInBetween;
-
 import android.graphics.Rect;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ScrollView;
 import android.widget.TextView;
+import android.widget.scroll.ButtonsWithTallTextViewInBetween;
+
+import androidx.test.filters.MediumTest;
 
 public class ButtonsWithTallTextViewInBetweenTest
         extends ActivityInstrumentationTestCase<ButtonsWithTallTextViewInBetween> {
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
index 6ce4c15..2f45098 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.scroll.arrowscroll.MultiPageTextWithPadding;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
-import android.widget.TextView;
 import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 @Suppress // Flaky
 public class MultiPageTextWithPaddingTest extends
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java
index 267d8ee..a2928cb 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.widget.scroll.ShortButtons;
-
 import android.graphics.Rect;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.ScrollView;
+import android.widget.scroll.ShortButtons;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class ShortButtonsTest extends ActivityInstrumentationTestCase<ShortButtons> {
 
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java
index 5351839..0681081 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.widget.scroll.TallTextAboveButton;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.ScrollView;
 import android.widget.TextView;
+import android.widget.scroll.TallTextAboveButton;
+
+import androidx.test.filters.MediumTest;
 
 public class TallTextAboveButtonTest extends ActivityInstrumentationTestCase<TallTextAboveButton> {
     private ScrollView mScrollView;
diff --git a/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java b/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java
index 449c95c..aa20c9b 100644
--- a/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java
@@ -16,16 +16,17 @@
 
 package android.widget.touchmode;
 
-import android.widget.layout.linear.LLOfButtons1;
-import android.widget.layout.linear.LLOfButtons2;
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterClick;
-import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap;
+import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
+import android.widget.layout.linear.LLOfButtons1;
+import android.widget.layout.linear.LLOfButtons2;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * Tests that the touch mode changes from various events, and that the state
diff --git a/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java b/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java
index 691b25a..97c982f 100644
--- a/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java
@@ -16,12 +16,12 @@
 
 package android.widget.touchmode;
 
-import android.widget.layout.linear.LLOfTwoFocusableInTouchMode;
-
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.layout.linear.LLOfTwoFocusableInTouchMode;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class FocusableInTouchModeClickTest extends ActivityInstrumentationTestCase2<LLOfTwoFocusableInTouchMode> {
 
diff --git a/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java b/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java
index 5339188..88aa9ca 100644
--- a/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java
@@ -16,14 +16,15 @@
 
 package android.widget.touchmode;
 
-import android.widget.layout.linear.LLEditTextThenButton;
 import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.layout.linear.LLEditTextThenButton;
+
+import androidx.test.filters.MediumTest;
 
 public class StartInTouchWithViewInFocusTest extends 
         ActivityInstrumentationTestCase2<LLEditTextThenButton> {
diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
index 5a6110c..f192f68 100644
--- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
@@ -21,11 +21,11 @@
 import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.layout.linear.LLOfButtons1;
 
+import androidx.test.filters.MediumTest;
 
 /**
  * Make sure focus isn't kept by buttons when entering touch mode.
diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java
index 3ddeef0..87f33a4 100644
--- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java
@@ -20,12 +20,13 @@
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.layout.linear.LLEditTextThenButton;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Some views, like edit texts, can keep and gain focus even when in touch mode.
  */
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index a302657..aadfcbc 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -57,7 +57,6 @@
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.Voice;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 import android.view.Window;
@@ -66,6 +65,8 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.widget.Toast;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider;
 import com.android.internal.util.test.FakeSettingsProvider;
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 1859378..aaa624e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -16,6 +16,30 @@
 
 package com.android.internal.app;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
@@ -25,32 +49,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
-import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
 
 /**
  * Chooser activity instrumentation tests
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index c446f3c..60529f6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,14 +16,14 @@
 
 package com.android.internal.app;
 
+import static org.mockito.Mockito.mock;
+
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 
 import java.util.function.Function;
 
-import static org.mockito.Mockito.mock;
-
 public class ChooserWrapperActivity extends ChooserActivity {
     /*
      * Simple wrapper around chooser activity to be able to initiate it under test
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index cfb6bdd..9b13af2 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -46,9 +46,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 404c99c..fe2fb85 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -16,6 +16,29 @@
 
 package com.android.internal.app;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.widget.ResolverDrawerLayout;
@@ -26,37 +49,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
-import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-import android.widget.RelativeLayout;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-
-import static android.os.SystemClock.sleep;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 /**
  * Resolver activity instrumentation tests
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
index c710b9a..850b466 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
@@ -23,10 +23,6 @@
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 /**
  * Utility class used by resolver tests to create mock data
  */
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 284ab60..fcec00e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -25,34 +25,31 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.when;
 
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.Intent;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
-import java.io.File;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 163211e..83f6bc2 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -16,17 +16,12 @@
 
 package com.android.internal.app;
 
-import android.app.usage.UsageStatsManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-
-import java.util.function.Function;
-
 import static org.mockito.Mockito.mock;
 
+import android.app.usage.UsageStatsManager;
+import android.content.pm.PackageManager;
+
+import java.util.function.Function;
 
 /*
  * Simple wrapper around chooser activity to be able to initiate it under test
diff --git a/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java b/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java
index 472958a..87ad124 100644
--- a/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java
@@ -17,12 +17,13 @@
 package com.android.internal.app;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ActionMode;
 import android.view.Menu;
 import android.view.MenuItem;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+
 /**
  * Tests for {@link WindowDecorActionBar}.
  */
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
index 115af5e..f7fea3b 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
@@ -16,20 +16,14 @@
 
 package com.android.internal.app.procstats;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-
-import android.os.BatteryStats;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
-import org.mockito.Mockito;
-
 /**
  * Provides test cases for SparseMappingTable.
  */
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
index 629f7b6..222e494 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
@@ -18,10 +18,11 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.WindowManager.LayoutParams;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
index 8179328..ba63908 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
@@ -18,8 +18,8 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index 992b46f..b687801 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_SINCE_CHARGED;
@@ -20,10 +21,11 @@
 import android.app.ActivityManager;
 import android.os.BatteryStats;
 import android.os.WorkSource;
-import android.support.test.filters.SmallTest;
 import android.util.ArrayMap;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 /**
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
index 08f8dd1..37f818a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
@@ -13,11 +13,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index ee8d508..0179ead 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_SINCE_CHARGED;
@@ -36,11 +37,12 @@
 
 import android.os.BatteryStats;
 import android.os.UserHandle;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseLongArray;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.ArrayUtils;
 
 import org.junit.Before;
@@ -64,7 +66,7 @@
  * Install: adb install -r \
  * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
index 3a5a9f5..efb8710 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
@@ -13,10 +13,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
index 19dab79..a42286f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
@@ -13,11 +13,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index 7467114..355601c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -17,7 +17,6 @@
 
 package com.android.internal.os;
 
-
 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -37,12 +36,13 @@
 import android.content.pm.PackageManager;
 import android.os.BatteryStats;
 import android.os.Process;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.format.DateUtils;
 import android.util.StatsLog;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import junit.framework.TestCase;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
index 10a3189..cc0ddb7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import static org.junit.Assert.assertEquals;
-
 import android.content.Context;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 613de45..dc93675 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_SINCE_CHARGED;
@@ -31,12 +32,13 @@
 import static org.mockito.Mockito.when;
 
 import android.os.BatteryStats;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.Display;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.ArrayUtils;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 8cbe5d6..3e33273 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_CURRENT;
@@ -20,21 +21,20 @@
 import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
 
 import android.app.ActivityManager;
-import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.HistoryItem;
 import android.os.BatteryStats.Uid.Sensor;
 import android.os.WorkSource;
-import android.support.test.filters.SmallTest;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.internal.os.BatteryStatsImpl.DualTimer;
 import com.android.internal.os.BatteryStatsImpl.Uid;
+
 import junit.framework.TestCase;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 /**
@@ -47,7 +47,7 @@
  * Install: adb install -r \
  *      ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsNoteTest -w \
- *      com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *      com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 public class BatteryStatsNoteTest extends TestCase {
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
index 251ceb0..61d20df 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
@@ -13,11 +13,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index a751f90..b851f0a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -13,13 +13,15 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.app.ActivityManager;
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 /**
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
index 5fd8225..b9995c4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
@@ -18,7 +18,8 @@
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.Assert;
 import junit.framework.TestCase;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
index 015314e..f76f316 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
@@ -13,10 +13,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 225515e..d69e1d1 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
index 3190d9e..bce8b40 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
@@ -16,19 +16,20 @@
 
 package com.android.internal.os;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
 import org.mockito.Mockito;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
 /**
  * Provides test cases for android.os.BatteryStats.
  */
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
index 98d0f7f..87dc2f3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
@@ -18,16 +18,17 @@
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
 import android.util.StringBuilderPrinter;
 
-import junit.framework.Assert;
-import junit.framework.TestCase;
+import androidx.test.filters.SmallTest;
 
 import com.android.internal.os.BatteryStatsImpl.Clocks;
 import com.android.internal.os.BatteryStatsImpl.TimeBase;
 import com.android.internal.os.BatteryStatsImpl.Timer;
 
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
 /**
  * Provides test cases for android.os.BatteryStats.
  */
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
index a7e75a2..4df3190 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
@@ -16,22 +16,10 @@
 
 package com.android.internal.os;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
+import androidx.test.filters.SmallTest;
 
-import android.os.BatteryStats;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import junit.framework.Assert;
 import junit.framework.TestCase;
 
-import com.android.internal.os.BatteryStatsImpl;
-
-import org.mockito.Mockito;
-
 /**
  * Provides test cases for android.os.BatteryStats.
  */
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
index 450473d..e7a1bca 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
@@ -28,12 +28,13 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
 import android.util.ArraySet;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index dc3a12f..1d35143 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -22,11 +22,12 @@
 import android.os.Binder;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.BinderInternal.CallSession;
 
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index 42c9139..01515bd 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.UID_TIMES_TYPE_ALL;
@@ -33,9 +34,6 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
-import com.android.frameworks.coretests.aidl.ICmdCallback;
-import com.android.frameworks.coretests.aidl.ICmdReceiver;
-
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.content.ComponentName;
@@ -52,14 +50,18 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
 import android.text.TextUtils;
 import android.util.DebugUtils;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.aidl.ICmdCallback;
+import com.android.frameworks.coretests.aidl.ICmdReceiver;
+
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/os/DebugTest.java b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
index efb78d7..2a8a857 100644
--- a/core/tests/coretests/src/com/android/internal/os/DebugTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
@@ -18,7 +18,8 @@
 
 import android.os.Debug;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 @SmallTest
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
index 8360126..a25a7489 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
@@ -24,9 +24,10 @@
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index 2663f2b..7a31605 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -24,9 +24,10 @@
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
new file mode 100644
index 0000000..c03d1f3
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -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.
+ */
+
+package com.android.internal.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.KernelCpuThreadReader.ProcessCpuUsage;
+import com.android.internal.os.KernelCpuThreadReader.ThreadCpuUsage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.OptionalDouble;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * End to end test for {@link KernelCpuThreadReader} that checks the accuracy of the reported times
+ * by spawning threads that do a predictable amount of work
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class KernelCpuThreadReaderEndToEndTest {
+
+    private static final int TIMED_NUM_SAMPLES = 5;
+    private static final int TIMED_START_MILLIS = 500;
+    private static final int TIMED_END_MILLIS = 2000;
+    private static final int TIMED_INCREMENT_MILLIS = 500;
+    private static final int TIMED_COMPARISON_DELTA_MILLIS = 200;
+
+    private static final int ITERATIVE_NUM_SAMPLES = 100;
+    private static final long ITERATIVE_LOW_ITERATIONS = (long) 1e8;
+    private static final long ITERATIVE_HIGH_ITERATIONS = (long) 2e8;
+    private static final double ITERATIONS_COMPARISONS_DELTA = 0.25;
+
+    /**
+     * Test that when we busy-wait for the thread-local time to reach N seconds, the time reported
+     * is also N seconds. Takes ~10s.
+     */
+    @Test
+    public void testTimedWork() throws InterruptedException {
+        for (int millis = TIMED_START_MILLIS;
+                millis <= TIMED_END_MILLIS;
+                millis += TIMED_INCREMENT_MILLIS) {
+            final Duration targetDuration = Duration.ofMillis(millis);
+            final Runnable work = timedWork(targetDuration);
+            Duration resultDuration = getAverageWorkTime(
+                    work, String.format("timed%dms", millis), TIMED_NUM_SAMPLES);
+            assertEquals(
+                    "Time worked according to currentThreadTimeMillis doesn't match "
+                            + "KernelCpuThreadReader",
+                    targetDuration.toMillis(), resultDuration.toMillis(),
+                    TIMED_COMPARISON_DELTA_MILLIS);
+        }
+    }
+
+    /**
+     * Test that when we scale up the amount of work by N, the time reported also scales by N. Takes
+     * ~15s.
+     */
+    @Test
+    public void testIterativeWork() throws InterruptedException {
+        final Runnable lowAmountWork = iterativeWork(ITERATIVE_LOW_ITERATIONS);
+        final Runnable highAmountWork = iterativeWork(ITERATIVE_HIGH_ITERATIONS);
+        final Duration lowResultDuration =
+                getAverageWorkTime(lowAmountWork, "iterlow", ITERATIVE_NUM_SAMPLES);
+        final Duration highResultDuration =
+                getAverageWorkTime(highAmountWork, "iterhigh", ITERATIVE_NUM_SAMPLES);
+        assertEquals(
+                "Work scale and CPU time scale do not match",
+                ((double) ITERATIVE_HIGH_ITERATIONS) / ((double) ITERATIVE_LOW_ITERATIONS),
+                ((double) highResultDuration.toMillis()) / ((double) lowResultDuration.toMillis()),
+                ITERATIONS_COMPARISONS_DELTA);
+    }
+
+    /**
+     * Run some work {@code numSamples} times, and take the average CPU duration used for that work
+     * according to {@link KernelCpuThreadReader}
+     */
+    private Duration getAverageWorkTime(
+            Runnable work, String tag, int numSamples) throws InterruptedException {
+        // Count down every time a thread finishes work, so that we can wait for work to complete
+        final CountDownLatch workFinishedLatch = new CountDownLatch(numSamples);
+        // Count down once when threads can terminate (after we get them from
+        // `KernelCpuThreadReader`)
+        final CountDownLatch threadFinishedLatch = new CountDownLatch(1);
+
+        // Start `NUM_SAMPLE` threads to do the work
+        for (int i = 0; i < numSamples; i++) {
+            final String threadName = String.format("%s%d", tag, i);
+            // Check the thread name, as we rely on it later to identify threads
+            assertTrue("Max name length for linux threads is 15", threadName.length() <= 15);
+            doWork(work, threadName, workFinishedLatch, threadFinishedLatch);
+        }
+
+        // Wait for threads to finish
+        workFinishedLatch.await();
+
+        // Get thread data from KernelCpuThreadReader
+        final KernelCpuThreadReader kernelCpuThreadReader = KernelCpuThreadReader.create();
+        assertNotNull(kernelCpuThreadReader);
+        final ProcessCpuUsage currentProcessCpuUsage =
+                kernelCpuThreadReader.getCurrentProcessCpuUsage();
+
+        // Threads can terminate, as we've finished crawling them from /proc
+        threadFinishedLatch.countDown();
+
+        // Check that we've got times for every thread we spawned
+        final List<ThreadCpuUsage> threadCpuUsages = currentProcessCpuUsage.threadCpuUsages
+                .stream()
+                .filter((thread) -> thread.threadName.startsWith(tag))
+                .collect(Collectors.toList());
+        assertEquals(
+                "Incorrect number of threads returned by KernelCpuThreadReader",
+                numSamples, threadCpuUsages.size());
+
+        // Calculate the average time spent working
+        final OptionalDouble averageWorkTimeMillis = threadCpuUsages.stream()
+                .mapToDouble((t) -> Arrays.stream(t.usageTimesMillis).sum())
+                .average();
+        assertTrue(averageWorkTimeMillis.isPresent());
+        return Duration.ofMillis((long) averageWorkTimeMillis.getAsDouble());
+    }
+
+    /**
+     * Work that lasts {@code duration} according to {@link SystemClock#currentThreadTimeMillis()}
+     */
+    private Runnable timedWork(Duration duration) {
+        return () -> {
+            // Busy loop until `duration` has elapsed for the thread timer
+            final long startTimeMillis = SystemClock.currentThreadTimeMillis();
+            final long durationMillis = duration.toMillis();
+            while (true) {
+                final long elapsedMillis = SystemClock.currentThreadTimeMillis() - startTimeMillis;
+                if (elapsedMillis >= durationMillis) {
+                    break;
+                }
+            }
+        };
+    }
+
+    /**
+     * Work that iterates {@code iterations} times
+     */
+    private Runnable iterativeWork(long iterations) {
+        Consumer<Long> empty = (i) -> {
+        };
+        return () -> {
+            long count = 0;
+            for (long i = 0; i < iterations; i++) {
+                // Alternate branching to reduce effect of branch prediction
+                if (i % 2 == 0) {
+                    count++;
+                }
+            }
+            // Call empty function with value to avoid loop getting optimized away
+            empty.accept(count);
+        };
+    }
+
+    /**
+     * Perform some work in another thread
+     *
+     * @param work                the work to perform
+     * @param threadName          the name of the spawned thread
+     * @param workFinishedLatch   latch to register that the work has been completed
+     * @param threadFinishedLatch latch to pause termination of the thread until the latch is
+     *                            decremented
+     */
+    private void doWork(
+            Runnable work,
+            String threadName,
+            CountDownLatch workFinishedLatch,
+            CountDownLatch threadFinishedLatch) {
+        Runnable workWrapped = () -> {
+            // Do the work
+            work.run();
+            // Notify that the work is finished
+            workFinishedLatch.countDown();
+            // Wait until `threadFinishLatch` has been released in order to keep the thread alive so
+            // we can see it in `proc` filesystem
+            try {
+                threadFinishedLatch.await();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        };
+        new Thread(workWrapped, threadName).start();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index b242a34..0c56b8a 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -23,9 +23,10 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
index adafda0..1b13a99 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertEquals;
@@ -20,11 +21,12 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseLongArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
index ad20d84..2ea80da 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -22,11 +23,12 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index 1d3a98a..0b6fed3 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -24,11 +25,12 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
index 9b4512b..8f81ea2 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -23,11 +24,12 @@
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
index 32317ee..60dac85 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
@@ -1,11 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.internal.os;
 
-import android.support.test.filters.SmallTest;
 import android.util.LongSparseLongArray;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
-import org.junit.Assert;
 import org.mockito.Mockito;
 
 import java.io.BufferedReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 29227f9..479e19e 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -21,10 +21,11 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelSingleUidTimeReader.Injector;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
index 28570e8..12f6c18 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -20,8 +20,8 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
index 85dce02..532f337 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
@@ -21,10 +21,11 @@
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.when;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
index 67c4e61..6d2980b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
@@ -24,10 +24,11 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,7 +54,7 @@
  * Install: adb install -r \
  * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.KernelUidCpuFreqTimeReaderTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
index 4e4bb3507..78b6843 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
@@ -13,9 +13,10 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
index fe62764..cb8a62c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
@@ -16,7 +16,9 @@
 
 package com.android.internal.os;
 
-import android.test.suitebuilder.annotation.Suppress;
+import androidx.test.filters.Suppress;
+
+import junit.framework.TestCase;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -25,8 +27,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import junit.framework.TestCase;
-
 // this test causes a IllegalAccessError: superclass not accessible
 @Suppress
 public class LoggingPrintStreamTest extends TestCase {
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
index 37b4e41a..0516bb7 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
@@ -30,8 +30,9 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -55,7 +56,7 @@
  * Install: adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.LongSamplingCounterArrayTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
index 853bf8a..d2f5735 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
@@ -28,8 +28,9 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index b65c1e6..2c597b1 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -23,8 +23,9 @@
 import android.os.Looper;
 import android.os.Message;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index b68f6b1..c18445e 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -21,6 +21,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.location.gnssmetrics.GnssMetrics;
+
 import java.util.ArrayList;
 import java.util.Queue;
 import java.util.concurrent.Future;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
index 14d62e0..c592ab6 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
@@ -17,13 +17,13 @@
 
 package com.android.internal.os;
 
-
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 2853c96..5862368 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -17,8 +17,8 @@
 
 package com.android.internal.os;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
index 489e164..e97caf8 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
@@ -20,9 +20,10 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index 2893066..9db3f8a 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -22,9 +22,10 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
index c051a1c..85eafc5 100644
--- a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
@@ -15,15 +15,17 @@
  */
 
 package com.android.internal.os;
+
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -36,7 +38,6 @@
 import java.io.File;
 import java.nio.file.Files;
 
-
 /**
  * Test class for {@link StoragedUidIoStatsReader}.
  *
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
index 2a24881..8e0c1fe 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
@@ -17,8 +17,6 @@
 package com.android.internal.policy;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
 import android.view.KeyEvent;
@@ -32,7 +30,8 @@
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 
-import java.util.List;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
 
 /**
  * Tests {@link PhoneWindow}'s {@link ActionMode} related methods.
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index d4b8566..6c2d630 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -28,12 +28,13 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 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.view.ActionMode;
 import android.view.ContextThemeWrapper;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
index b3897ce..d026735 100644
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -13,9 +13,11 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.util;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.util.ArrayList;
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
index a44b860..4716312 100644
--- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.util;
 
 import static com.android.internal.util.DumpUtils.CRITICAL_SECTION_COMPONENTS;
diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
index 4845c4e..b2a2265 100644
--- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
@@ -19,11 +19,8 @@
 import junit.framework.TestCase;
 
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
 import java.io.Writer;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 
 /**
diff --git a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
index f00c48c..867152e 100644
--- a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.util;
 
 import junit.framework.TestCase;
diff --git a/core/tests/coretests/src/android/util/TokenBucketTest.java b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
similarity index 90%
rename from core/tests/coretests/src/android/util/TokenBucketTest.java
rename to core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
index f7ac20c..6c50bce 100644
--- a/core/tests/coretests/src/android/util/TokenBucketTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
@@ -18,6 +18,7 @@
 
 import android.os.SystemClock;
 import android.text.format.DateUtils;
+
 import junit.framework.TestCase;
 
 public class TokenBucketTest extends TestCase {
@@ -54,9 +55,9 @@
 
         drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 100), 10);
 
-        drain(new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50), 50);
-        drain(new TokenBucket((int)DateUtils.HOUR_IN_MILLIS, 10), 10);
-        drain(new TokenBucket((int)DateUtils.DAY_IN_MILLIS, 200), 200);
+        drain(new TokenBucket((int) DateUtils.MINUTE_IN_MILLIS, 50), 50);
+        drain(new TokenBucket((int) DateUtils.HOUR_IN_MILLIS, 10), 10);
+        drain(new TokenBucket((int) DateUtils.DAY_IN_MILLIS, 200), 200);
     }
 
     public void testReset() {
@@ -163,16 +164,16 @@
 
     void assertDuration(long expected, long elapsed) {
         String msg = String.format(
-            "expected elapsed time at least %d ms, but was %d ms", expected, elapsed);
+                "expected elapsed time at least %d ms, but was %d ms", expected, elapsed);
         elapsed += 1; // one millisecond extra guard
         assertTrue(msg, elapsed >= expected);
     }
 
-    void assertThrow(Fn fn)     {
-      try {
-          fn.call();
-          fail("expected n exception to be thrown.");
-      } catch (Throwable t) {}
+    void assertThrow(Fn fn) {
+        try {
+            fn.call();
+            fail("expected n exception to be thrown.");
+        } catch (Throwable t) { }
     }
 
     interface Fn { void call(); }
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java
index 912b7ec..b372366 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java
@@ -18,11 +18,12 @@
 
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ActionMode;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.filters.SmallTest;
+
 /**
  * Tests for {@link ActionBarContainer}.
  */
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index 218566e..d10f173 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -29,9 +29,6 @@
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.DisplayCutout;
 import android.view.View;
 import android.view.View.OnApplyWindowInsetsListener;
@@ -40,6 +37,10 @@
 import android.widget.FrameLayout;
 import android.widget.Toolbar;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -167,7 +168,7 @@
     }
 
     private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
-        return new WindowInsets(content.toRect(), null, null, false, false, cutout);
+        return new WindowInsets(content.toRect(), null, false, false, cutout);
     }
 
     private ViewGroup createViewGroupWithId(int id) {
diff --git a/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java b/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java
index e21143d..0fada06 100644
--- a/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java
@@ -32,10 +32,11 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.support.test.InstrumentationRegistry;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
index 1806b22..2e0dbb4 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
@@ -19,12 +19,13 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
 import android.text.Layout;
 import android.view.View.MeasureSpec;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index f73950a..6167c4b 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.UserHandle;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
index 41082b7..6af7c88 100644
--- a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
@@ -21,12 +21,12 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.text.Layout;
 import android.view.LayoutInflater;
 import android.view.View.MeasureSpec;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk
index e0d2c09..2ca31a6 100644
--- a/core/tests/hdmitests/Android.mk
+++ b/core/tests/hdmitests/Android.mk
@@ -20,7 +20,7 @@
 # Include all test java files
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test frameworks-base-testutils
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules frameworks-base-testutils
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := HdmiCecTests
diff --git a/core/tests/hdmitests/AndroidManifest.xml b/core/tests/hdmitests/AndroidManifest.xml
index 1460b41..f8ed118 100644
--- a/core/tests/hdmitests/AndroidManifest.xml
+++ b/core/tests/hdmitests/AndroidManifest.xml
@@ -22,7 +22,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.hardware.hdmi"
         android:label="HDMI CEC Tests"/>
 
diff --git a/core/tests/hdmitests/AndroidTest.xml b/core/tests/hdmitests/AndroidTest.xml
index 7ef672d..0c8da28 100644
--- a/core/tests/hdmitests/AndroidTest.xml
+++ b/core/tests/hdmitests/AndroidTest.xml
@@ -29,6 +29,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.hardware.hdmi" />
         <option name="hidden-api-checks" value="false"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
     </test>
 </configuration>
\ No newline at end of file
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 7b76a08..28a8afe 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -18,15 +18,18 @@
 
 import android.os.Handler;
 import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
 import android.util.Log;
-import java.util.List;
+
+import androidx.test.filters.SmallTest;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.List;
+
 /**
  * Tests for {@link HdmiAudioSystemClient}
  */
@@ -315,6 +318,27 @@
             mMaxVolume = maxVolume;
             mIsMute = isMute;
         }
+
+        @Override
+        public void setSystemAudioModeOnForAudioOnlySource() {
+        }
+
+        @Override
+        public int getPhysicalAddress() {
+            return 0x0000;
+        }
+
+        @Override
+        public void powerOffRemoteDevice(int logicalAddress, int powerStatus) {
+        }
+
+        @Override
+        public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {
+        }
+
+        @Override
+        public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
+        }
     }
 
 }
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
index 80ab4ea..cc2d8d2 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -18,7 +18,7 @@
 ## The application with a minimal main dex
 include $(CLEAR_VARS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex android-support-multidex-instrumentation android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex android-support-multidex-instrumentation androidx.test.rules
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -38,16 +38,11 @@
 
 LOCAL_MIN_SDK_VERSION := 8
 
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex \
-     -D jack.dex.output.multidex.legacy=true
-
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacyandexception/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
-endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
index 98d8f27..d9d9eb2 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
@@ -30,7 +30,7 @@
                      android:targetPackage="com.android.multidexlegacyandexception"
                      android:label="Test for MultiDexLegacyAndException" />
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.multidexlegacyandexception"
                      android:label="Test for MultiDexLegacyAndException" />
 </manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java
index 92a3b0c..fae345f 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java
@@ -17,8 +17,9 @@
 package com.android.multidexlegacyandexception.tests;
 
 import android.os.Bundle;
+
 import androidx.multidex.MultiDex;
-import android.support.test.runner.AndroidJUnitRunner;
+import androidx.test.runner.AndroidJUnitRunner;
 
 public class MultiDexAndroidJUnitRunner extends AndroidJUnitRunner {
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/NoActivityJUnit4Test.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/NoActivityJUnit4Test.java
index 94a5b7f..f4b02d0 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/NoActivityJUnit4Test.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/NoActivityJUnit4Test.java
@@ -16,7 +16,8 @@
 
 package com.android.multidexlegacyandexception.tests;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.runner.RunWith;
 
 /**
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index cf8fc92..c577eef 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -38,9 +38,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
@@ -69,9 +69,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList2): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList2): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList2)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
index f2bd353..2dc30ea 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
@@ -18,7 +18,7 @@
 ## The tests with only one dex
 include $(CLEAR_VARS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation androidx.test.rules
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -41,7 +41,7 @@
 ## The tests with a minimal main dex
 include $(CLEAR_VARS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation androidx.test.rules
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/InstrumentationTest.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/InstrumentationTest.java
index 4e6ec14..7812c58 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/InstrumentationTest.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/InstrumentationTest.java
@@ -15,10 +15,13 @@
  */
 package com.android.multidexlegacytestapp.test2;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.multidexlegacytestapp.manymethods.Big001;
 import com.android.multidexlegacytestapp.manymethods.Big079;
+
 import junit.framework.Assert;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java
index 9e41a92..b3044dc 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java
@@ -1,8 +1,9 @@
 package com.android.multidexlegacytestapp.test2;
 
 import android.os.Bundle;
+
 import androidx.multidex.MultiDex;
-import android.support.test.runner.AndroidJUnitRunner;
+import androidx.test.runner.AndroidJUnitRunner;
 
 public class MultiDexAndroidJUnitRunner extends AndroidJUnitRunner {
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index 2ce50b3..da40940 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -30,14 +30,13 @@
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true
 
 LOCAL_DEX_PREOPT := false
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
index f3d98a8..afbcd46 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
@@ -23,7 +23,7 @@
 LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2
 
 LOCAL_JAVA_LIBRARIES := android-support-multidex
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 
 LOCAL_SDK_VERSION := 9
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
index 0ab2959..01285e7 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
@@ -7,7 +7,7 @@
     <uses-sdk android:minSdkVersion="9" />
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.framework.multidexlegacytestservices" />
 
     <application
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
index 900f203..f2c72f0 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
@@ -19,22 +19,26 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
 import java.io.FileFilter;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.util.concurrent.TimeoutException;
-import junit.framework.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Run the tests with: <code>adb shell am instrument -w
- * com.android.framework.multidexlegacytestservices.test2/android.support.test.runner.AndroidJUnitRunner
+ * com.android.framework.multidexlegacytestservices.test2/androidx.test.runner.AndroidJUnitRunner
  * </code>
  */
 @RunWith(AndroidJUnit4.class)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index 8b0c750..665e22d 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -35,9 +35,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index a36c993..c827fa8 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -35,9 +35,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 6b7418c..3d6ad7d 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -35,9 +35,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk
index 680ebeb..5630749 100644
--- a/core/tests/overlaytests/device/Android.mk
+++ b/core/tests/overlaytests/device/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_PACKAGE_NAME := OverlayDeviceTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_TARGET_REQUIRED_MODULES := \
     OverlayDeviceTests_AppOverlayOne \
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index d14fdf5..4881636 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -23,7 +23,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.overlaytest"
         android:label="Runtime resource overlay tests" />
 </manifest>
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 5a86885..91fcdbb 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -25,10 +25,11 @@
 import android.content.res.XmlResourceParser;
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
 import android.util.AttributeSet;
 import android.util.Xml;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index f35e511..cd3ed9d 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 037449f..c0d4281 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index f657b5c..33c7b25 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 25dabad..035ee10 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -42,6 +42,38 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.carrierconfig",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.carrierconfig.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.contacts",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.contacts.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.launcher3",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.launcher3.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.provision",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.provision.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.settings",
     product_specific: true,
     sub_dir: "permissions",
@@ -50,6 +82,22 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.settings.intelligence",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.settings.intelligence.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.storagemanager",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.storagemanager.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.systemui",
     product_specific: true,
     sub_dir: "permissions",
diff --git a/data/etc/com.android.carrierconfig.xml b/data/etc/com.android.carrierconfig.xml
new file mode 100644
index 0000000..17efb03
--- /dev/null
+++ b/data/etc/com.android.carrierconfig.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.carrierconfig">
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.contacts.xml b/data/etc/com.android.contacts.xml
new file mode 100644
index 0000000..78eae40
--- /dev/null
+++ b/data/etc/com.android.contacts.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.contacts">
+        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+        <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
new file mode 100644
index 0000000..337e153
--- /dev/null
+++ b/data/etc/com.android.launcher3.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.launcher3">
+        <permission name="android.permission.BIND_APPWIDGET"/>
+        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
+        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml
new file mode 100644
index 0000000..05404ef
--- /dev/null
+++ b/data/etc/com.android.provision.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.provision">
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.settings.intelligence.xml b/data/etc/com.android.settings.intelligence.xml
new file mode 100644
index 0000000..6ca30c1
--- /dev/null
+++ b/data/etc/com.android.settings.intelligence.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.settings.intelligence">
+        <permission name="android.permission.MANAGE_FINGERPRINT"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+        <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.storagemanager.xml b/data/etc/com.android.storagemanager.xml
new file mode 100644
index 0000000..e85a82c
--- /dev/null
+++ b/data/etc/com.android.storagemanager.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.storagemanager">
+        <permission name="android.permission.DELETE_PACKAGES"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index b65bc1d..3562a8f 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -29,7 +29,6 @@
         <permission name="android.permission.DUMP"/>
         <permission name="android.permission.GET_APP_OPS_STATS"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
         <permission name="android.permission.MANAGE_DEBUGGING"/>
         <permission name="android.permission.MANAGE_SENSOR_PRIVACY"/>
         <permission name="android.permission.MANAGE_USB"/>
@@ -45,6 +44,7 @@
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
+        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
         <permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
         <permission name="android.permission.START_TASKS_FROM_RECENTS"/>
         <permission name="android.permission.STATUS_BAR"/>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 4a2db0a..fb43e41 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -223,12 +223,12 @@
          code to link against. -->
 
     <library name="android.test.base"
-            file="/system/framework/android.test.base.impl.jar" />
+            file="/system/framework/android.test.base.jar" />
     <library name="android.test.mock"
-            file="/system/framework/android.test.mock.impl.jar"
+            file="/system/framework/android.test.mock.jar"
             dependency="android.test.base" />
     <library name="android.test.runner"
-            file="/system/framework/android.test.runner.impl.jar"
+            file="/system/framework/android.test.runner.jar"
             dependency="android.test.base:android.test.mock" />
 
     <!-- In BOOT_JARS historically, and now added to legacy applications. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 58b57e5..597d14a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,10 +33,6 @@
         <permission name="android.permission.CRYPT_KEEPER"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.carrierconfig">
-        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.cellbroadcastreceiver">
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
@@ -45,11 +41,6 @@
         <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.contacts">
-        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
-        <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.defcontainer">
         <permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/>
         <permission name="android.permission.ALLOCATE_AGGRESSIVE"/>
@@ -79,12 +70,6 @@
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.launcher3">
-        <permission name="android.permission.BIND_APPWIDGET"/>
-        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
-        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.location.fused">
         <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
     </privapp-permissions>
@@ -238,7 +223,29 @@
         <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.provision">
+    <privapp-permissions package="com.android.mainline.networkstack">
+        <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
+        <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/>
+        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+        <permission name="android.permission.CONTROL_VPN"/>
+        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.MANAGE_IPSEC_TUNNELS"/>
+        <permission name="android.permission.MANAGE_NETWORK_POLICY"/>
+        <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
+        <permission name="android.permission.MANAGE_USB"/>
+        <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
+        <permission name="android.permission.NETWORK_SETTINGS"/>
+        <permission name="android.permission.NETWORK_STACK" />
+        <permission name="android.permission.NET_TUNNELING"/>
+        <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+        <permission name="android.permission.PEERS_MAC_ADDRESS"/>
+        <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+        <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.READ_WIFI_CREDENTIAL"/>
+        <permission name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"/>
+        <permission name="android.permission.TETHER_PRIVILEGED"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
@@ -254,13 +261,6 @@
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.settings.intelligence">
-        <permission name="android.permission.MANAGE_FINGERPRINT"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
-        <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.sharedstoragebackup">
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
@@ -289,7 +289,6 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_ACCESSIBILITY"/>
-        <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
@@ -317,6 +316,7 @@
         <permission name="android.permission.SET_TIME"/>
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/>
+        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
         <permission name="android.permission.START_TASKS_FROM_RECENTS" />
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
@@ -331,15 +331,6 @@
         <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.storagemanager">
-        <permission name="android.permission.DELETE_PACKAGES"/>
-        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.MANAGE_USERS"/>
-        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
-        <permission name="android.permission.USE_RESERVED_DISK"/>
-        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.tv">
         <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
         <permission name="android.permission.DVB_DEVICE"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 6a40792..21531ab 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -99,6 +99,46 @@
         <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
     </family>
 
+    <family name="arbutus-slab">
+        <font weight="400" style="normal">ArbutusSlab-Regular.ttf</font>
+    </family>
+
+    <family name="arvo">
+        <font weight="400" style="normal">Arvo-Regular.ttf</font>
+        <font weight="400" style="italic">Arvo-Italic.ttf</font>
+        <font weight="700" style="normal">Arvo-Bold.ttf</font>
+        <font weight="700" style="italic">Arvo-BoldItalic.ttf</font>
+    </family>
+    <alias name="arvo-bold" to="arvo" weight="700" />
+
+    <family name="lato">
+        <font weight="400" style="normal">Lato-Regular.ttf</font>
+        <font weight="400" style="italic">Lato-Italic.ttf</font>
+        <font weight="700" style="normal">Lato-Bold.ttf</font>
+        <font weight="700" style="italic">Lato-BoldItalic.ttf</font>
+    </family>
+    <alias name="lato-bold" to="lato" weight="700" />
+
+    <family name="rubik">
+        <font weight="400" style="normal">Rubik-Regular.ttf</font>
+        <font weight="400" style="italic">Rubik-Italic.ttf</font>
+        <font weight="500" style="normal">Rubik-Medium.ttf</font>
+        <font weight="500" style="italic">Rubik-MediumItalic.ttf</font>
+        <font weight="700" style="normal">Rubik-Bold.ttf</font>
+        <font weight="700" style="italic">Rubik-BoldItalic.ttf</font>
+    </family>
+    <alias name="rubik-medium" to="rubik" weight="500" />
+
+    <family name="source-sans-pro">
+        <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
+        <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
+        <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
+        <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
+        <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
+        <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
+    </family>
+    <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600" />
+
     <!-- fallback fonts -->
     <family lang="und-Arab" variant="elegant">
         <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
@@ -312,9 +352,8 @@
         <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
     </family>
     <family lang="und-Mymr" variant="elegant">
-        <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
+        <font weight="400" style="normal">NotoSansMyanmar-Regular-ZawDecode.ttf</font>
+        <font weight="700" style="normal">NotoSansMyanmar-Bold-ZawDecode.ttf</font>
         <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
     </family>
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index bb8add1..c6c7d3b 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -15,227 +15,227 @@
 LOCAL_PATH := frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-    $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-    $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-    $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-    $(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
-    $(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-    $(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-    $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \
-    $(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
-    $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \
-    $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-    $(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
-    $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \
-    $(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \
-    $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-    $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-    $(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \
-    $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-    $(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \
-    $(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
-    $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-    $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-    $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \
-    $(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \
-    $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \
-    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \
-    $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-    $(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-    $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \
-    $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-    $(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
-    $(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-    $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
-    $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-    $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
-    $(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
-    $(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-    $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-    $(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-    $(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
-    $(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
-    $(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-    $(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
-    $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-    $(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
-    $(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \
-    $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-    $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-    $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-    $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-    $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-    $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-    $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
-    $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
-    $(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-    $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-    $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-    $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-    $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-    $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-    $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-    $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-    $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-    $(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \
-    $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-    $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \
-    $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-    $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \
-    $(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-    $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-    $(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \
-    $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-    $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-    $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-    $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-    $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-    $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-    $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-    $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-    $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-    $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-    $(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-    $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-    $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-    $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \
-    $(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \
-    $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-    $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-    $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-    $(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
-    $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-    $(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-    $(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-    $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-    $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-    $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-    $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-    $(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \
-    $(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-    $(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-    $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-    $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-    $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-    $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-    $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-    $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-    $(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-    $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-    $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \
-    $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-    $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-    $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-    $(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg \
-    $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg \
-    $(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-    $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-    $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-    $(LOCAL_PATH)/effects/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-    $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-    $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \
-    $(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCFailure.ogg:system/media/audio/ui/NFCFailure.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCInitiated.ogg:system/media/audio/ui/NFCInitiated.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCSuccess.ogg:system/media/audio/ui/NFCSuccess.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCTransferComplete.ogg:system/media/audio/ui/NFCTransferComplete.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCTransferInitiated.ogg:system/media/audio/ui/NFCTransferInitiated.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+    $(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+    $(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+    $(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Barium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Barium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Promethium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Scandium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+    $(LOCAL_PATH)/notifications/Aldebaran.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Aldebaran.ogg \
+    $(LOCAL_PATH)/notifications/Altair.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Altair.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+    $(LOCAL_PATH)/notifications/Antares.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antares.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antimony.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+    $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beryllium.ogg \
+    $(LOCAL_PATH)/notifications/Betelgeuse.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Betelgeuse.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg \
+    $(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+    $(LOCAL_PATH)/notifications/Castor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Castor.ogg \
+    $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cobalt.ogg \
+    $(LOCAL_PATH)/notifications/Cricket.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cricket.ogg \
+    $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+    $(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+    $(LOCAL_PATH)/notifications/Doink.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Doink.ogg \
+    $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+    $(LOCAL_PATH)/notifications/Drip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Drip.ogg \
+    $(LOCAL_PATH)/notifications/Electra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Electra.ogg \
+    $(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+    $(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+    $(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fluorine.ogg \
+    $(LOCAL_PATH)/notifications/Fomalhaut.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fomalhaut.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Gallium.ogg \
+    $(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Helium.ogg \
+    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Krypton.ogg \
+    $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+    $(LOCAL_PATH)/notifications/Merope.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Merope.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+    $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Palladium.ogg \
+    $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Plastic_Pipe.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Radon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Radon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Rubidium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Selenium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+    $(LOCAL_PATH)/notifications/Sirrah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Sirrah.ogg \
+    $(LOCAL_PATH)/notifications/SpaceSeed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/SpaceSeed.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Strontium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+    $(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Thallium.ogg \
+    $(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+    $(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Xenon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Zirconium.ogg \
+    $(LOCAL_PATH)/notifications/arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/arcturus.ogg \
+    $(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+    $(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+    $(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+    $(LOCAL_PATH)/notifications/regulus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/regulus.ogg \
+    $(LOCAL_PATH)/notifications/sirius.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/sirius.ogg \
+    $(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+    $(LOCAL_PATH)/notifications/vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/vega.ogg \
+    $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ANDROMEDA.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+    $(LOCAL_PATH)/ringtones/BOOTES.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BOOTES.ogg \
+    $(LOCAL_PATH)/newwavelabs/Backroad.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Backroad.ogg \
+    $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+    $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+    $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+    $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+    $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+    $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+    $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CANISMAJOR.ogg \
+    $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CASSIOPEIA.ogg \
+    $(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+    $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+    $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+    $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+    $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+    $(LOCAL_PATH)/newwavelabs/Ding.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ding.ogg \
+    $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+    $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg \
+    $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+    $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Enter_the_Nexus.ogg \
+    $(LOCAL_PATH)/ringtones/Eridani.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eridani.ogg \
+    $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+    $(LOCAL_PATH)/ringtones/FreeFlight.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FreeFlight.ogg \
+    $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+    $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+    $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+    $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+    $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+    $(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+    $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+    $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+    $(LOCAL_PATH)/ringtones/Lyra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Lyra.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+    $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+    $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nairobi.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nassau.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nassau.ogg \
+    $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+    $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+    $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+    $(LOCAL_PATH)/ringtones/PERSEUS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/PERSEUS.ogg \
+    $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+    $(LOCAL_PATH)/newwavelabs/Revelation.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Revelation.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+    $(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+    $(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+    $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+    $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+    $(LOCAL_PATH)/newwavelabs/Safari.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Safari.ogg \
+    $(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+    $(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+    $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+    $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+    $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+    $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+    $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+    $(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+    $(LOCAL_PATH)/ringtones/Testudo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Testudo.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+    $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+    $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg \
+    $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+    $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/URSAMINOR.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+    $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+    $(LOCAL_PATH)/ringtones/Vespa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Vespa.ogg \
+    $(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg \
+    $(LOCAL_PATH)/ringtones/hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/hydra.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+    $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+    $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+    $(LOCAL_PATH)/effects/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+    $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
+    $(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCFailure.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCFailure.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCInitiated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCInitiated.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCSuccess.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCSuccess.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCTransferComplete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCTransferComplete.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCTransferInitiated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCTransferInitiated.ogg \
diff --git a/data/sounds/AudioPackage10.mk b/data/sounds/AudioPackage10.mk
index 72aa7fe..699dbd6 100644
--- a/data/sounds/AudioPackage10.mk
+++ b/data/sounds/AudioPackage10.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \
-	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
+	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index 665ce52..99dfd0a 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \.
-	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat_proc48.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \.
+	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat_proc48.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage12.mk b/data/sounds/AudioPackage12.mk
index 44a8f9e..6159a89 100644
--- a/data/sounds/AudioPackage12.mk
+++ b/data/sounds/AudioPackage12.mk
@@ -16,15 +16,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord LowBattery WirelessChargingStarted VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage12_48.mk b/data/sounds/AudioPackage12_48.mk
index 09fab04..2899cd1 100644
--- a/data/sounds/AudioPackage12_48.mk
+++ b/data/sounds/AudioPackage12_48.mk
@@ -17,21 +17,21 @@
 
 # Alarms not yet available in 48 kHz
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/ogg/$(fn)_48k.ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/ogg/$(fn)_48k.ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 
 # no gold-plated version yet
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg
diff --git a/data/sounds/AudioPackage13.mk b/data/sounds/AudioPackage13.mk
index de4ee04..9423c0b 100644
--- a/data/sounds/AudioPackage13.mk
+++ b/data/sounds/AudioPackage13.mk
@@ -17,15 +17,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage13_48.mk b/data/sounds/AudioPackage13_48.mk
index 889d581..806c4e2 100644
--- a/data/sounds/AudioPackage13_48.mk
+++ b/data/sounds/AudioPackage13_48.mk
@@ -17,21 +17,21 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn)_48k.ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn)_48k.ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn)_48k.ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 
 # no gold-plated version yet
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg
diff --git a/data/sounds/AudioPackage14.mk b/data/sounds/AudioPackage14.mk
index c903a2b..3d161aa 100644
--- a/data/sounds/AudioPackage14.mk
+++ b/data/sounds/AudioPackage14.mk
@@ -18,15 +18,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk
index 40319c4..bc4e8fb 100644
--- a/data/sounds/AudioPackage2.mk
+++ b/data/sounds/AudioPackage2.mk
@@ -10,98 +10,98 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/Revelation.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Revelation.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg
 endif
diff --git a/data/sounds/AudioPackage3.mk b/data/sounds/AudioPackage3.mk
index a05de72..a98fb74 100644
--- a/data/sounds/AudioPackage3.mk
+++ b/data/sounds/AudioPackage3.mk
@@ -10,94 +10,94 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \
-	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-	$(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg \
+	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+	$(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg
 endif
diff --git a/data/sounds/AudioPackage4.mk b/data/sounds/AudioPackage4.mk
index d376a2d..54c3c02 100644
--- a/data/sounds/AudioPackage4.mk
+++ b/data/sounds/AudioPackage4.mk
@@ -10,98 +10,98 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \
-	$(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \
-	$(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \
-	$(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Cricket.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cricket.ogg \
+	$(LOCAL_PATH)/notifications/Doink.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Doink.ogg \
+	$(LOCAL_PATH)/notifications/Drip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Drip.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/SpaceSeed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/SpaceSeed.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Plastic_Pipe.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/ringtones/FreeFlight.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FreeFlight.ogg \
+	$(LOCAL_PATH)/newwavelabs/Backroad.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Backroad.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-	$(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \
-	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \
-	$(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+	$(LOCAL_PATH)/newwavelabs/Ding.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ding.ogg \
+	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Enter_the_Nexus.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/Nairobi.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nairobi.ogg \
+	$(LOCAL_PATH)/newwavelabs/Nassau.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nassau.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Safari.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Safari.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg
 endif
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
index 72384c8..8a03a2e1 100644
--- a/data/sounds/AudioPackage5.mk
+++ b/data/sounds/AudioPackage5.mk
@@ -8,69 +8,69 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
-	$(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
-	$(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
-	$(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
-	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
-	$(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-	$(LOCAL_PATH)/notifications/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
-	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-	$(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
-	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
-	$(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
-	$(LOCAL_PATH)/notifications/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
-	$(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
-	$(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
-	$(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
-	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
-	$(LOCAL_PATH)/ringtones/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
-	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
-	$(LOCAL_PATH)/ringtones/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
-	$(LOCAL_PATH)/ringtones/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-	$(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-	$(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-	$(LOCAL_PATH)/ringtones/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
-	$(LOCAL_PATH)/ringtones/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
-	$(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/Aldebaran.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Aldebaran.ogg \
+	$(LOCAL_PATH)/notifications/Altair.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Altair.ogg \
+	$(LOCAL_PATH)/notifications/Antares.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antares.ogg \
+	$(LOCAL_PATH)/notifications/arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/arcturus.ogg \
+	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Betelgeuse.ogg \
+	$(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+	$(LOCAL_PATH)/notifications/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/Castor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Castor.ogg \
+	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+	$(LOCAL_PATH)/notifications/Electra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Electra.ogg \
+	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fomalhaut.ogg \
+	$(LOCAL_PATH)/notifications/Merope.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Merope.ogg \
+	$(LOCAL_PATH)/notifications/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/regulus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/regulus.ogg \
+	$(LOCAL_PATH)/notifications/sirius.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/sirius.ogg \
+	$(LOCAL_PATH)/notifications/Sirrah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Sirrah.ogg \
+	$(LOCAL_PATH)/notifications/vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/vega.ogg \
+	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ANDROMEDA.ogg \
+	$(LOCAL_PATH)/ringtones/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/BOOTES.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BOOTES.ogg \
+	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CANISMAJOR.ogg \
+	$(LOCAL_PATH)/ringtones/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CASSIOPEIA.ogg \
+	$(LOCAL_PATH)/ringtones/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/Eridani.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eridani.ogg \
+	$(LOCAL_PATH)/ringtones/hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/hydra.ogg \
+	$(LOCAL_PATH)/ringtones/Lyra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Lyra.ogg \
+	$(LOCAL_PATH)/ringtones/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/PERSEUS.ogg \
+	$(LOCAL_PATH)/ringtones/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/Testudo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Testudo.ogg \
+	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/URSAMINOR.ogg \
+	$(LOCAL_PATH)/ringtones/Vespa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk
index 5413704..a778261 100644
--- a/data/sounds/AudioPackage6.mk
+++ b/data/sounds/AudioPackage6.mk
@@ -8,41 +8,41 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Barium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Barium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Scandium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Scandium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Antimony.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antimony.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beryllium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cobalt.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fluorine.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Gallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Gallium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Helium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Krypton.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Palladium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Palladium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Radon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Radon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Rubidium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Selenium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Selenium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Strontium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Strontium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Thallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Thallium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Xenon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Xenon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Zirconium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index e4763be..27e349d2 100644
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -8,64 +8,64 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage7alt.mk b/data/sounds/AudioPackage7alt.mk
index 30e6173..a0f4d89 100644
--- a/data/sounds/AudioPackage7alt.mk
+++ b/data/sounds/AudioPackage7alt.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg-jp/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg-jp/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage8.mk b/data/sounds/AudioPackage8.mk
index b38e62d..032b4d2 100644
--- a/data/sounds/AudioPackage8.mk
+++ b/data/sounds/AudioPackage8.mk
@@ -8,66 +8,66 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Promethium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Promethium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage9.mk b/data/sounds/AudioPackage9.mk
index dbe1350..53cc8c0 100644
--- a/data/sounds/AudioPackage9.mk
+++ b/data/sounds/AudioPackage9.mk
@@ -8,43 +8,43 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg
+        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg
diff --git a/data/sounds/AudioPackageGo.mk b/data/sounds/AudioPackageGo.mk
index 0296219..e3b27f2 100644
--- a/data/sounds/AudioPackageGo.mk
+++ b/data/sounds/AudioPackageGo.mk
@@ -20,30 +20,30 @@
 # Ring_Synth_04 : Flutey Phone
 # Alarm_Beep_03 : Beep Beep Beep
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-    $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-    $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-    $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-    $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-    $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-    $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-    $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+    $(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+    $(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+    $(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+    $(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+    $(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+    $(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+    $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
diff --git a/data/sounds/AudioTv.mk b/data/sounds/AudioTv.mk
index 91265af..d0006b7 100644
--- a/data/sounds/AudioTv.mk
+++ b/data/sounds/AudioTv.mk
@@ -15,8 +15,8 @@
 LOCAL_PATH := frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
index f683752..4d74d12 100644
--- a/data/sounds/OriginalAudio.mk
+++ b/data/sounds/OriginalAudio.mk
@@ -9,67 +9,67 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg
 endif
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 76eab4a..fcebad3 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -831,6 +831,7 @@
      *     content://media/<table_name>/<row_index> (or)
      *     file://sdcard/test.mp4
      *     http://test.com/test.mp4
+     *     https://test.com/test.mp4
      *
      * Here <table_name> shall be "video" or "audio" or "images"
      * <row_index> the index of the content in given table
@@ -843,7 +844,7 @@
                     scheme.equals(ContentResolver.SCHEME_FILE)) {
                 path = uri.getPath();
 
-            } else if (scheme.equals("http")) {
+            } else if (scheme.equals("http") || scheme.equals("https")) {
                 path = uri.toString();
 
             } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index ca9dc47..65aaba1 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,6 +22,7 @@
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas.VertexMode;
+import android.graphics.text.MeasuredText;
 import android.text.GraphicsOperations;
 import android.text.MeasuredParagraph;
 import android.text.PrecomputedText;
@@ -554,14 +555,12 @@
                     final int paraStart = pt.getParagraphStart(paraIndex);
                     final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
                     // Only support the text in the same paragraph.
-                    nDrawTextRun(mNativeCanvasWrapper,
-                            mp.getChars(),
-                            start - paraStart,
-                            end - start,
-                            contextStart - paraStart,
-                            contextEnd - contextStart,
-                            x, y, isRtl, paint.getNativeInstance(),
-                            mp.getMeasuredText().getNativePtr());
+                    drawTextRun(mp.getMeasuredText(),
+                                start - paraStart,
+                                end - paraStart,
+                                contextStart - paraStart,
+                                contextEnd - paraStart,
+                                x, y, isRtl, paint);
                     return;
                 }
             }
@@ -576,6 +575,14 @@
         }
     }
 
+    public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
+            int contextStart, int contextEnd, float x, float y, boolean isRtl,
+            @NonNull Paint paint) {
+        nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
+                contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
+                measuredText.getNativePtr());
+    }
+
     public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
             int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
             int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 901c211..4f60935 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.graphics.text.MeasuredText;
 import android.text.GraphicsOperations;
 import android.text.MeasuredParagraph;
 import android.text.PrecomputedText;
@@ -522,14 +523,12 @@
                     final int paraStart = pt.getParagraphStart(paraIndex);
                     final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
                     // Only support if the target is in the same paragraph.
-                    nDrawTextRun(mNativeCanvasWrapper,
-                            mp.getChars(),
+                    drawTextRun(mp.getMeasuredText(),
                             start - paraStart,
-                            end - start,
+                            end - paraStart,
                             contextStart - paraStart,
-                            contextEnd - contextStart,
-                            x, y, isRtl, paint.getNativeInstance(),
-                            mp.getMeasuredText().getNativePtr());
+                            contextEnd - paraStart,
+                            x, y, isRtl, paint);
                     return;
                 }
             }
@@ -545,6 +544,15 @@
     }
 
     @Override
+    public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
+            int contextStart, int contextEnd, float x, float y, boolean isRtl,
+            @NonNull Paint paint) {
+        nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
+                contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
+                measuredText.getNativePtr());
+    }
+
+    @Override
     public final void drawVertices(@NonNull VertexMode mode, int vertexCount,
             @NonNull float[] verts, int vertOffset, @Nullable float[] texs, int texOffset,
             @Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset,
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 790b37e..30f0bfa 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -18,9 +18,11 @@
 
 import android.annotation.CheckResult;
 import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.content.res.ResourcesImpl;
@@ -1780,6 +1782,30 @@
     }
 
     /**
+     * Fills the bitmap's pixels with the specified {@link Color}.
+     *
+     * @throws IllegalStateException if the bitmap is not mutable.
+     * @throws IllegalArgumentException if the color space encoded in the long
+     *                                  is invalid or unknown.
+     *
+     * @hide pending API approval
+     */
+    @TestApi
+    public void eraseColor(@ColorLong long c) {
+        checkRecycled("Can't erase a recycled bitmap");
+        if (!isMutable()) {
+            throw new IllegalStateException("cannot erase immutable bitmaps");
+        }
+
+        ColorSpace cs = Color.colorSpace(c);
+        float r = Color.red(c);
+        float g = Color.green(c);
+        float b = Color.blue(c);
+        float a = Color.alpha(c);
+        nativeErase(mNativePtr, cs, r, g, b, a);
+    }
+
+    /**
      * Returns the {@link Color} at the specified location. Throws an exception
      * if x or y are out of bounds (negative or >= to the width or height
      * respectively). The returned color is a non-premultiplied ARGB value in
@@ -2123,6 +2149,8 @@
                                             int quality, OutputStream stream,
                                             byte[] tempStorage);
     private static native void nativeErase(long nativeBitmap, int color);
+    private static native void nativeErase(long nativeBitmap, ColorSpace cs,
+                                           float r, float g, float b, float a);
     private static native int nativeRowBytes(long nativeBitmap);
     private static native int nativeConfig(long nativeBitmap);
 
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index adab1a9c..022fbdc 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -440,7 +440,8 @@
             if (opts == null) return;
 
             if (opts.inBitmap != null && opts.inBitmap.getConfig() == Bitmap.Config.HARDWARE) {
-                throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
+                throw new IllegalArgumentException(
+                        "Bitmaps with Config.HARDWARE are always immutable");
             }
 
             if (opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 63a806e..8c1bae2 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
+import android.graphics.text.MeasuredText;
 import android.os.Build;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -2122,7 +2123,8 @@
      * the text next to it.
      * <p>
      * All text outside the range {@code contextStart..contextEnd} is ignored. The text between
-     * {@code start} and {@code end} will be laid out and drawn.
+     * {@code start} and {@code end} will be laid out and drawn. The context range is useful for
+     * contextual shaping, e.g. Kerning, Arabic contextural form.
      * <p>
      * The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
      * suitable only for runs of a single direction. Alignment of the text is as determined by the
@@ -2151,6 +2153,31 @@
     }
 
     /**
+     * Draw a run of text, all in a single direction, with optional context for complex text
+     * shaping.
+     * <p>
+     * See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)} for
+     * more details. This method uses a {@link MeasuredText} rather than CharSequence to represent
+     * the string.
+     *
+     * @param text the text to render
+     * @param start the start of the text to render. Data before this position can be used for
+     *            shaping context.
+     * @param end the end of the text to render. Data at or after this position can be used for
+     *            shaping context.
+     * @param contextStart the index of the start of the shaping context
+     * @param contextEnd the index of the end of the shaping context
+     * @param x the x position at which to draw the text
+     * @param y the y position at which to draw the text
+     * @param isRtl whether the run is in RTL direction
+     * @param paint the paint
+     */
+    public void drawTextRun(@NonNull MeasuredText text, int start, int end, int contextStart,
+            int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) {
+        super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, isRtl, paint);
+    }
+
+    /**
      * Draw the array of vertices, interpreted as triangles (based on mode). The verts array is
      * required, and specifies the x,y pairs for each vertex. If texs is non-null, then it is used
      * to specify the coordinate in shader coordinates to use at each vertex (the paint must have a
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 2227cf5..9fa70a5 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -984,11 +984,12 @@
      *         {@link Named#SRGB sRGB} primaries.
      *     </li>
      *     <li>
-     *         Its white point is withing 1e-3 of the CIE standard
+     *         Its white point is within 1e-3 of the CIE standard
      *         illuminant {@link #ILLUMINANT_D65 D65}.
      *     </li>
      *     <li>Its opto-electronic transfer function is not linear.</li>
      *     <li>Its electro-optical transfer function is not linear.</li>
+     *     <li>Its transfer functions yield values within 1e-3 of {@link Named#SRGB}.</li>
      *     <li>Its range is \([0..1]\).</li>
      * </ul>
      * <p>This method always returns true for {@link Named#SRGB}.</p>
@@ -1354,9 +1355,9 @@
      */
     @NonNull
     static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
-        if (index < 0 || index > Named.values().length) {
+        if (index < 0 || index >= Named.values().length) {
             throw new IllegalArgumentException("Invalid ID, must be in the range [0.." +
-                    Named.values().length + "]");
+                    Named.values().length + ")");
         }
         return sNamedColorSpaces[index];
     }
@@ -1660,10 +1661,12 @@
      * @param rhs 3x3 matrix, as a non-null array of 9 floats
      * @return A new array of 9 floats containing the result of the multiplication
      *         of rhs by lhs
+     *
+     * @hide
      */
     @NonNull
     @Size(9)
-    private static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) {
+    public static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) {
         float[] r = new float[9];
         r[0] = lhs[0] * rhs[0] + lhs[3] * rhs[1] + lhs[6] * rhs[2];
         r[1] = lhs[1] * rhs[0] + lhs[4] * rhs[1] + lhs[7] * rhs[2];
@@ -3145,19 +3148,35 @@
                 float max,
                 @IntRange(from = MIN_ID, to = MAX_ID) int id) {
             if (id == 0) return true;
-            if (!compare(primaries, SRGB_PRIMARIES)) {
+            if (!ColorSpace.compare(primaries, SRGB_PRIMARIES)) {
                 return false;
             }
-            if (!compare(whitePoint, ILLUMINANT_D65)) {
+            if (!ColorSpace.compare(whitePoint, ILLUMINANT_D65)) {
                 return false;
             }
-            if (OETF.applyAsDouble(0.5) < 0.5001) return false;
-            if (EOTF.applyAsDouble(0.5) > 0.5001) return false;
+
             if (min != 0.0f) return false;
             if (max != 1.0f) return false;
+
+            // We would have already returned true if this was SRGB itself, so
+            // it is safe to reference it here.
+            ColorSpace.Rgb srgb = (ColorSpace.Rgb) get(Named.SRGB);
+
+            for (double x = 0.0; x <= 1.0; x += 1 / 255.0) {
+                if (!compare(x, OETF, srgb.mOetf)) return false;
+                if (!compare(x, EOTF, srgb.mEotf)) return false;
+            }
+
             return true;
         }
 
+        private static boolean compare(double point, @NonNull DoubleUnaryOperator a,
+                @NonNull DoubleUnaryOperator b) {
+            double rA = a.applyAsDouble(point);
+            double rB = b.applyAsDouble(point);
+            return Math.abs(rA - rB) <= 1e-3;
+        }
+
         /**
          * Computes whether the specified CIE xyY or XYZ primaries (with Y set to 1) form
          * a wide color gamut. A color gamut is considered wide if its area is &gt; 90%
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3342fd2..54e1abc 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,12 +17,14 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.annotation.Size;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.Build;
@@ -66,26 +68,27 @@
                 Paint.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_PAINT_SIZE);
     }
 
-    private ColorFilter mColorFilter;
-    private MaskFilter  mMaskFilter;
-    private PathEffect  mPathEffect;
-    private Shader      mShader;
+    @ColorLong private long mColor;
+    private ColorFilter     mColorFilter;
+    private MaskFilter      mMaskFilter;
+    private PathEffect      mPathEffect;
+    private Shader          mShader;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private Typeface    mTypeface;
-    private Xfermode    mXfermode;
+    private Typeface        mTypeface;
+    private Xfermode        mXfermode;
 
-    private boolean     mHasCompatScaling;
-    private float       mCompatScaling;
-    private float       mInvCompatScaling;
+    private boolean         mHasCompatScaling;
+    private float           mCompatScaling;
+    private float           mInvCompatScaling;
 
-    private LocaleList  mLocales;
-    private String      mFontFeatureSettings;
-    private String      mFontVariationSettings;
+    private LocaleList      mLocales;
+    private String          mFontFeatureSettings;
+    private String          mFontVariationSettings;
 
-    private float mShadowLayerRadius;
-    private float mShadowLayerDx;
-    private float mShadowLayerDy;
-    private int mShadowLayerColor;
+    private float           mShadowLayerRadius;
+    private float           mShadowLayerDx;
+    private float           mShadowLayerDy;
+    @ColorLong private long mShadowLayerColor;
 
     private static final Object sCacheLock = new Object();
 
@@ -503,6 +506,7 @@
         //        ? HINTING_OFF : HINTING_ON);
         mCompatScaling = mInvCompatScaling = 1;
         setTextLocales(LocaleList.getAdjustedDefault());
+        mColor = Color.pack(Color.BLACK);
     }
 
     /**
@@ -528,6 +532,7 @@
         // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
         //        ? HINTING_OFF : HINTING_ON);
 
+        mColor = Color.pack(Color.BLACK);
         mColorFilter = null;
         mMaskFilter = null;
         mPathEffect = null;
@@ -549,7 +554,7 @@
         mShadowLayerRadius = 0.0f;
         mShadowLayerDx = 0.0f;
         mShadowLayerDy = 0.0f;
-        mShadowLayerColor = 0;
+        mShadowLayerColor = Color.pack(0);
     }
 
     /**
@@ -570,6 +575,7 @@
      * {@link Paint}.
      */
     private void setClassVariablesFrom(Paint paint) {
+        mColor = paint.mColor;
         mColorFilter = paint.mColorFilter;
         mMaskFilter = paint.mMaskFilter;
         mPathEffect = paint.mPathEffect;
@@ -947,7 +953,7 @@
     }
 
     /**
-     * Return the paint's color. Note that the color is a 32bit value
+     * Return the paint's color in sRGB. Note that the color is a 32bit value
      * containing alpha as well as r,g,b. This 32bit value is not premultiplied,
      * meaning that its alpha can be any value, regardless of the values of
      * r,g,b. See the Color class for more details.
@@ -956,7 +962,25 @@
      */
     @ColorInt
     public int getColor() {
-        return nGetColor(mNativePaint);
+        return Color.toArgb(mColor);
+    }
+
+    /**
+     * Return the paint's color. Note that the color is a long with an encoded
+     * {@link ColorSpace} as well as alpha and r,g,b. These values are not
+     * premultiplied, meaning that alpha can be any value, regardless of the
+     * values of r,g,b. See the {@link Color} class for more details.
+     *
+     * @see Color for APIs that help manipulate a color long.
+     *
+     * @return the paint's color (and alpha).
+     *
+     * @hide pending API approval
+     */
+    @TestApi
+    @ColorLong
+    public long getColorLong() {
+        return mColor;
     }
 
     /**
@@ -968,7 +992,33 @@
      * @param color The new color (including alpha) to set in the paint.
      */
     public void setColor(@ColorInt int color) {
-        nSetColor(mNativePaint, color);
+        setColor(Color.pack(color));
+    }
+
+    /**
+     * Set the paint's color with a {@link ColorLong}. Note that the color is
+     * a long with an encoded {@link ColorSpace} as well as alpha and r,g,b.
+     * These values are not premultiplied, meaning that alpha can be any value,
+     * regardless of the values of r,g,b. See the {@link Color} class for more
+     * details.
+     *
+     * @param color The new color (including alpha and {@link ColorSpace})
+     *      to set in the paint.
+     * @throws IllegalArgumentException if the color space encoded in the long
+     *      is invalid or unknown.
+     *
+     * @hide pending API approval
+     */
+    @TestApi
+    public void setColor(@ColorLong long color) {
+        ColorSpace cs = Color.colorSpace(color);
+        float r = Color.red(color);
+        float g = Color.green(color);
+        float b = Color.blue(color);
+        float a = Color.alpha(color);
+
+        nSetColor(mNativePaint, cs, r, g, b, a);
+        mColor = color;
     }
 
     /**
@@ -979,7 +1029,7 @@
      * @return the alpha component of the paint's color.
      */
     public int getAlpha() {
-        return nGetAlpha(mNativePaint);
+        return Math.round(Color.alpha(mColor) * 255.0f);
     }
 
     /**
@@ -990,6 +1040,13 @@
      * @param a set the alpha component [0..255] of the paint's color.
      */
     public void setAlpha(int a) {
+        // FIXME: No need to unpack this. Instead, just update the alpha bits.
+        // b/122959599
+        ColorSpace cs = Color.colorSpace(mColor);
+        float r = Color.red(mColor);
+        float g = Color.green(mColor);
+        float b = Color.blue(mColor);
+        mColor = Color.pack(r, g, b, a * (1.0f / 255), cs);
         nSetAlpha(mNativePaint, a);
     }
 
@@ -1370,12 +1427,40 @@
      * The alpha of the shadow will be the paint's alpha if the shadow color is
      * opaque, or the alpha from the shadow color if not.
      */
-    public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
-      mShadowLayerRadius = radius;
-      mShadowLayerDx = dx;
-      mShadowLayerDy = dy;
-      mShadowLayerColor = shadowColor;
-      nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
+    public void setShadowLayer(float radius, float dx, float dy, @ColorInt int shadowColor) {
+        setShadowLayer(radius, dx, dy, Color.pack(shadowColor));
+    }
+
+    /**
+     * This draws a shadow layer below the main layer, with the specified
+     * offset and color, and blur radius. If radius is 0, then the shadow
+     * layer is removed.
+     * <p>
+     * Can be used to create a blurred shadow underneath text. Support for use
+     * with other drawing operations is constrained to the software rendering
+     * pipeline.
+     * <p>
+     * The alpha of the shadow will be the paint's alpha if the shadow color is
+     * opaque, or the alpha from the shadow color if not.
+     *
+     * @throws IllegalArgumentException if the color space encoded in the long
+     *      is invalid or unknown.
+     *
+     * @hide pending API approval
+     */
+    @TestApi
+    public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) {
+        ColorSpace cs = Color.colorSpace(shadowColor);
+        float r = Color.red(shadowColor);
+        float g = Color.green(shadowColor);
+        float b = Color.blue(shadowColor);
+        float a = Color.alpha(shadowColor);
+        nSetShadowLayer(mNativePaint, radius, dx, dy, cs, r, g, b, a);
+
+        mShadowLayerRadius = radius;
+        mShadowLayerDx = dx;
+        mShadowLayerDy = dy;
+        mShadowLayerColor = shadowColor;
     }
 
     /**
@@ -1422,8 +1507,20 @@
     /**
      * Returns the color of the shadow layer.
      * @see #setShadowLayer(float,float,float,int)
+     * @see #setShadowLayer(float,float,float,long)
      */
     public @ColorInt int getShadowLayerColor() {
+        return Color.toArgb(mShadowLayerColor);
+    }
+
+    /**
+     * Returns the color of the shadow layer.
+     * @see #setShadowLayer(float,float,float,int)
+     * @see #setShadowLayer(float,float,float,long)
+     * @hide pending API approval
+     */
+    @TestApi
+    public @ColorLong long getShadowLayerColorLong() {
         return mShadowLayerColor;
     }
 
@@ -2906,6 +3003,11 @@
             int contextStart, int contextEnd, boolean isRtl, int offset);
     private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, float advance);
+    private static native void nSetColor(long paintPtr, ColorSpace cs,
+            float r, float g, float b, float a);
+    private static native void nSetShadowLayer(long paintPtr,
+            float radius, float dx, float dy, ColorSpace cs,
+            float r, float g, float b, float a);
 
 
     // ---------------- @FastNative ------------------------
@@ -2961,7 +3063,7 @@
             int mMinikinLocaleListId);
     @CriticalNative
     private static native void nSetShadowLayer(long paintPtr,
-            float radius, float dx, float dy, int color);
+            float radius, float dx, float dy, @ColorInt int color);
     @CriticalNative
     private static native boolean nHasShadowLayer(long paintPtr);
     @CriticalNative
@@ -3009,12 +3111,6 @@
     @CriticalNative
     private static native void nSetFilterBitmap(long paintPtr, boolean filter);
     @CriticalNative
-    private static native int nGetColor(long paintPtr);
-    @CriticalNative
-    private static native void nSetColor(long paintPtr, @ColorInt int color);
-    @CriticalNative
-    private static native int nGetAlpha(long paintPtr);
-    @CriticalNative
     private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
     @CriticalNative
     private static native boolean nIsElegantTextHeight(long paintPtr);
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 9b86b77..25f6775 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1075,6 +1075,11 @@
                 continue; // If alias and named family are conflict, use named family.
             }
             final Typeface base = systemFontMap.get(alias.getToName());
+            if (base == null) {
+                // The missing target is a valid thing, some configuration don't have font files,
+                // e.g. wear devices. Just skip this alias.
+                continue;
+            }
             final int weight = alias.getWeight();
             final Typeface newFace = weight == 400 ? base :
                     new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index ad9ec02..3c35d9b 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -20,8 +20,9 @@
 
 #include <algorithm>
 #include <iterator>
-#include <set>
 #include <map>
+#include <set>
+#include <sstream>
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
@@ -372,6 +373,9 @@
   uint32_t best_offset = 0u;
   uint32_t type_flags = 0u;
 
+  Resolution::Step::Type resolution_type;
+  std::vector<Resolution::Step> resolution_steps;
+
   // If desired_config is the same as the set configuration, then we can use our filtered list
   // and we don't need to match the configurations, since they already matched.
   const bool use_fast_path = desired_config == &configuration_;
@@ -403,8 +407,8 @@
     // If the package is an overlay, then even configurations that are the same MUST be chosen.
     const bool package_is_overlay = loaded_package->IsOverlay();
 
-    const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
     if (use_fast_path) {
+      const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
       const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
       const size_t type_count = candidate_configs.size();
       for (uint32_t i = 0; i < type_count; i++) {
@@ -412,21 +416,34 @@
 
         // We can skip calling ResTable_config::match() because we know that all candidate
         // configurations that do NOT match have been filtered-out.
-        if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
-            (package_is_overlay && this_config.compare(*best_config) == 0)) {
-          // The configuration matches and is better than the previous selection.
-          // Find the entry value if it exists for this configuration.
-          const ResTable_type* type_chunk = filtered_group.types[i];
-          const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
-          if (offset == ResTable_type::NO_ENTRY) {
-            continue;
-          }
+        if (best_config == nullptr) {
+          resolution_type = Resolution::Step::Type::INITIAL;
+        } else if (this_config.isBetterThan(*best_config, desired_config)) {
+          resolution_type = Resolution::Step::Type::BETTER_MATCH;
+        } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
+          resolution_type = Resolution::Step::Type::OVERLAID;
+        } else {
+          continue;
+        }
 
-          best_cookie = cookie;
-          best_package = loaded_package;
-          best_type = type_chunk;
-          best_config = &this_config;
-          best_offset = offset;
+        // The configuration matches and is better than the previous selection.
+        // Find the entry value if it exists for this configuration.
+        const ResTable_type* type = filtered_group.types[i];
+        const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
+        if (offset == ResTable_type::NO_ENTRY) {
+          continue;
+        }
+
+        best_cookie = cookie;
+        best_package = loaded_package;
+        best_type = type;
+        best_config = &this_config;
+        best_offset = offset;
+
+        if (resource_resolution_logging_enabled_) {
+          resolution_steps.push_back(Resolution::Step{resolution_type,
+                                                      this_config.toString(),
+                                                      &loaded_package->GetPackageName()});
         }
       }
     } else {
@@ -440,23 +457,38 @@
         ResTable_config this_config;
         this_config.copyFromDtoH((*iter)->config);
 
-        if (this_config.match(*desired_config)) {
-          if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
-              (package_is_overlay && this_config.compare(*best_config) == 0)) {
-            // The configuration matches and is better than the previous selection.
-            // Find the entry value if it exists for this configuration.
-            const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
-            if (offset == ResTable_type::NO_ENTRY) {
-              continue;
-            }
+        if (!this_config.match(*desired_config)) {
+          continue;
+        }
 
-            best_cookie = cookie;
-            best_package = loaded_package;
-            best_type = *iter;
-            best_config_copy = this_config;
-            best_config = &best_config_copy;
-            best_offset = offset;
-          }
+        if (best_config == nullptr) {
+          resolution_type = Resolution::Step::Type::INITIAL;
+        } else if (this_config.isBetterThan(*best_config, desired_config)) {
+          resolution_type = Resolution::Step::Type::BETTER_MATCH;
+        } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
+          resolution_type = Resolution::Step::Type::OVERLAID;
+        } else {
+          continue;
+        }
+
+        // The configuration matches and is better than the previous selection.
+        // Find the entry value if it exists for this configuration.
+        const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
+        if (offset == ResTable_type::NO_ENTRY) {
+          continue;
+        }
+
+        best_cookie = cookie;
+        best_package = loaded_package;
+        best_type = *iter;
+        best_config_copy = this_config;
+        best_config = &best_config_copy;
+        best_offset = offset;
+
+        if (resource_resolution_logging_enabled_) {
+          resolution_steps.push_back(Resolution::Step{resolution_type,
+                                                      this_config.toString(),
+                                                      &loaded_package->GetPackageName()});
         }
       }
     }
@@ -478,9 +510,95 @@
   out_entry->entry_string_ref =
       StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
   out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
+
+  if (resource_resolution_logging_enabled_) {
+    last_resolution.resid = resid;
+    last_resolution.cookie = best_cookie;
+    last_resolution.steps = resolution_steps;
+
+    // Cache only the type/entry refs since that's all that's needed to build name
+    last_resolution.type_string_ref =
+        StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
+    last_resolution.entry_string_ref =
+        StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
+  }
+
   return best_cookie;
 }
 
+void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
+  resource_resolution_logging_enabled_ = enabled;
+
+  if (!enabled) {
+    last_resolution.cookie = kInvalidCookie;
+    last_resolution.resid = 0;
+    last_resolution.steps.clear();
+    last_resolution.type_string_ref = StringPoolRef();
+    last_resolution.entry_string_ref = StringPoolRef();
+  }
+}
+
+std::string AssetManager2::GetLastResourceResolution() const {
+  if (!resource_resolution_logging_enabled_) {
+    LOG(ERROR) << "Must enable resource resolution logging before getting path.";
+    return std::string();
+  }
+
+  auto cookie = last_resolution.cookie;
+  if (cookie == kInvalidCookie) {
+    LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
+    return std::string();
+  }
+
+  uint32_t resid = last_resolution.resid;
+  std::vector<Resolution::Step>& steps = last_resolution.steps;
+
+  ResourceName resource_name;
+  std::string resource_name_string;
+
+  const LoadedPackage* package =
+      apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+
+  if (package != nullptr) {
+    ToResourceName(last_resolution.type_string_ref,
+                   last_resolution.entry_string_ref,
+                   package,
+                   &resource_name);
+    resource_name_string = ToFormattedResourceString(&resource_name);
+  }
+
+  std::stringstream log_stream;
+  log_stream << base::StringPrintf("Resolution for 0x%08x ", resid)
+            << resource_name_string
+            << "\n\tFor config -"
+            << configuration_.toString();
+
+  std::string prefix;
+  for (Resolution::Step step : steps) {
+    switch (step.type) {
+      case Resolution::Step::Type::INITIAL:
+        prefix = "Found initial";
+        break;
+      case Resolution::Step::Type::BETTER_MATCH:
+        prefix = "Found better";
+        break;
+      case Resolution::Step::Type::OVERLAID:
+        prefix = "Overlaid";
+        break;
+    }
+
+    if (!prefix.empty()) {
+      log_stream << "\n\t" << prefix << ": " << *step.package_name;
+
+      if (!step.config_name.isEmpty()) {
+        log_stream << " -" << step.config_name;
+      }
+    }
+  }
+
+  return log_stream.str();
+}
+
 bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
   FindEntryResult entry;
   ApkAssetsCookie cookie =
@@ -495,27 +613,10 @@
     return false;
   }
 
-  out_name->package = package->GetPackageName().data();
-  out_name->package_len = package->GetPackageName().size();
-
-  out_name->type = entry.type_string_ref.string8(&out_name->type_len);
-  out_name->type16 = nullptr;
-  if (out_name->type == nullptr) {
-    out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
-    if (out_name->type16 == nullptr) {
-      return false;
-    }
-  }
-
-  out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
-  out_name->entry16 = nullptr;
-  if (out_name->entry == nullptr) {
-    out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
-    if (out_name->entry16 == nullptr) {
-      return false;
-    }
-  }
-  return true;
+  return ToResourceName(entry.type_string_ref,
+                        entry.entry_string_ref,
+                        package,
+                        out_name);
 }
 
 bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 57e3491..18d74ef 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -51,7 +51,7 @@
 class BagAttributeFinder
     : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
  public:
-  BagAttributeFinder(const ResolvedBag* bag)
+  explicit BagAttributeFinder(const ResolvedBag* bag)
       : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr,
                                     bag != nullptr ? bag->entries + bag->entry_count : nullptr) {
   }
@@ -286,6 +286,7 @@
     value.dataType = Res_value::TYPE_NULL;
     value.data = Res_value::DATA_NULL_UNDEFINED;
     config.density = 0;
+    uint32_t source_style_resid = 0;
 
     // Try to find a value for this attribute...  we prioritize values
     // coming from, first XML attributes, then XML style, then default
@@ -309,6 +310,7 @@
         cookie = entry->cookie;
         type_set_flags = style_flags;
         value = entry->value;
+        source_style_resid = entry->style;
         if (kDebugStyles) {
           ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
               entry->style);
@@ -325,8 +327,10 @@
         type_set_flags = def_style_flags;
         value = entry->value;
         if (kDebugStyles) {
-          ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
+          ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
+              entry->style);
         }
+        source_style_resid = entry->style;
       }
     }
 
@@ -382,6 +386,7 @@
     out_values[STYLE_RESOURCE_ID] = resid;
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
+    out_values[SYTLE_SOURCE_STYLE] = source_style_resid;
 
     if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
       indices_idx++;
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 5694115..a99e77f 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -94,7 +94,7 @@
         if (size < 0) {
             result = UNKNOWN_ERROR;
         } else {
-            int dupAshmemFd = ::dup(ashmemFd);
+            int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0);
             if (dupAshmemFd < 0) {
                 result = -errno;
             } else {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 5a26780..70ce9bc 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -593,7 +593,12 @@
           return {};
         }
 
-        // Iterate over the overlayable policy chunks
+        std::string name;
+        util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name);
+        std::string actor;
+        util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor);
+
+        // Iterate over the overlayable policy chunks contained within the overlayable chunk data
         ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
         while (overlayable_iter.HasNext()) {
           const Chunk overlayable_child_chunk = overlayable_iter.Next();
@@ -613,7 +618,7 @@
                 return {};
               }
 
-              // Retrieve all the ids belonging to this policy
+              // Retrieve all the resource ids belonging to this policy chunk
               std::unordered_set<uint32_t> ids;
               const auto ids_begin =
                   reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
@@ -622,8 +627,10 @@
                 ids.insert(dtohl(id_iter->ident));
               }
 
-              // Add the pairing of overlayable properties to resource ids to the package
+              // Add the pairing of overlayable properties and resource ids to the package
               OverlayableInfo overlayable_info{};
+              overlayable_info.name = name;
+              overlayable_info.actor = actor;
               overlayable_info.policy_flags = policy_header->policy_flags;
               loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
               break;
@@ -636,7 +643,7 @@
         }
 
         if (overlayable_iter.HadError()) {
-          LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s",
+          LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_TYPE: %s",
                                      overlayable_iter.GetLastError().c_str());
           if (overlayable_iter.HadFatalError()) {
             return {};
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index d63feb01..645984d 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -48,4 +48,65 @@
          !(has_type_separator && out_type->empty());
 }
 
+bool ToResourceName(StringPoolRef& type_string_ref,
+                    StringPoolRef& entry_string_ref,
+                    const LoadedPackage* package,
+                    AssetManager2::ResourceName* out_name) {
+  out_name->package = package->GetPackageName().data();
+  out_name->package_len = package->GetPackageName().size();
+
+  out_name->type = type_string_ref.string8(&out_name->type_len);
+  out_name->type16 = nullptr;
+  if (out_name->type == nullptr) {
+    out_name->type16 = type_string_ref.string16(&out_name->type_len);
+    if (out_name->type16 == nullptr) {
+      return false;
+    }
+  }
+
+  out_name->entry = entry_string_ref.string8(&out_name->entry_len);
+  out_name->entry16 = nullptr;
+  if (out_name->entry == nullptr) {
+    out_name->entry16 = entry_string_ref.string16(&out_name->entry_len);
+    if (out_name->entry16 == nullptr) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) {
+  std::string result;
+  if (resource_name->package != nullptr) {
+    result.append(resource_name->package, resource_name->package_len);
+  }
+
+  if (resource_name->type != nullptr || resource_name->type16 != nullptr) {
+    if (!result.empty()) {
+      result += ":";
+    }
+
+    if (resource_name->type != nullptr) {
+      result.append(resource_name->type, resource_name->type_len);
+    } else {
+      result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len));
+    }
+  }
+
+  if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) {
+    if (!result.empty()) {
+      result += "/";
+    }
+
+    if (resource_name->entry != nullptr) {
+      result.append(resource_name->entry, resource_name->entry_len);
+    } else {
+      result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len));
+    }
+  }
+
+  return result;
+}
+
 }  // namespace android
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 5d243da..5be2105 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -37,7 +37,7 @@
 // TODO: This can go away once the only remaining usage in aapt goes away.
 class FileReader : public zip_archive::Reader {
   public:
-    FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
+    explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
     }
 
     bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
diff --git a/libs/androidfw/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h
index 7aef02d..ce6e066 100644
--- a/libs/androidfw/include/androidfw/AssetDir.h
+++ b/libs/androidfw/include/androidfw/AssetDir.h
@@ -78,7 +78,7 @@
     class FileInfo {
     public:
         FileInfo(void) {}
-        FileInfo(const String8& path)      // useful for e.g. svect.indexOf
+        explicit FileInfo(const String8& path)      // useful for e.g. svect.indexOf
             : mFileName(path), mFileType(kFileTypeUnknown)
             {}
         ~FileInfo(void) {}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 0d49298..f29769b 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -229,6 +229,14 @@
                                    ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
                                    uint32_t* out_last_reference) const;
 
+  // Enables or disables resource resolution logging. Clears stored steps when
+  // disabled.
+  void SetResourceResolutionLoggingEnabled(bool enabled);
+
+  // Returns formatted log of last resource resolution path, or empty if no
+  // resource has been resolved yet.
+  std::string GetLastResourceResolution() const;
+
   // Retrieves the best matching bag/map resource with ID `resid`.
   // This method will resolve all parent references for this bag and merge keys with the child.
   // To iterate over the keys, use the following idiom:
@@ -346,6 +354,48 @@
   // Cached set of bags. These are cached because they can inherit keys from parent bags,
   // which involves some calculation.
   std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+
+  // Whether or not to save resource resolution steps
+  bool resource_resolution_logging_enabled_ = false;
+
+  struct Resolution {
+
+    struct Step {
+
+      enum class Type {
+        INITIAL,
+        BETTER_MATCH,
+        OVERLAID
+      };
+
+      // Marks what kind of override this step was.
+      Type type;
+
+      // Built name of configuration for this step.
+      String8 config_name;
+
+      // Marks the package name of the better resource found in this step.
+      const std::string* package_name;
+    };
+
+    // Last resolved resource ID.
+    uint32_t resid;
+
+    // Last resolved resource result cookie.
+    ApkAssetsCookie cookie = kInvalidCookie;
+
+    // Last resolved resource type.
+    StringPoolRef type_string_ref;
+
+    // Last resolved resource entry.
+    StringPoolRef entry_string_ref;
+
+    // Steps taken to resolve last resource.
+    std::vector<Step> steps;
+  };
+
+  // Record of the last resolved resource's resolution path.
+  mutable Resolution last_resolution;
 };
 
 class Theme {
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index 35ef98d..c88004c 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -23,15 +23,17 @@
 namespace android {
 
 // Offsets into the outValues array populated by the methods below. outValues is a uint32_t
-// array, but each logical element takes up 6 uint32_t-sized physical elements.
+// array, but each logical element takes up 7 uint32_t-sized physical elements.
+// Keep these in sync with android.content.res.TypedArray java class
 enum {
-  STYLE_NUM_ENTRIES = 6,
+  STYLE_NUM_ENTRIES = 7,
   STYLE_TYPE = 0,
   STYLE_DATA = 1,
   STYLE_ASSET_COOKIE = 2,
   STYLE_RESOURCE_ID = 3,
   STYLE_CHANGING_CONFIGURATIONS = 4,
-  STYLE_DENSITY = 5
+  STYLE_DENSITY = 5,
+  SYTLE_SOURCE_STYLE = 6
 };
 
 // These are all variations of the same method. They each perform the exact same operation,
diff --git a/libs/androidfw/include/androidfw/BackupHelpers.h b/libs/androidfw/include/androidfw/BackupHelpers.h
index fc1ad47..2da247b 100644
--- a/libs/androidfw/include/androidfw/BackupHelpers.h
+++ b/libs/androidfw/include/androidfw/BackupHelpers.h
@@ -67,7 +67,7 @@
 class BackupDataWriter
 {
 public:
-    BackupDataWriter(int fd);
+    explicit BackupDataWriter(int fd);
     // does not close fd
     ~BackupDataWriter();
 
@@ -104,7 +104,7 @@
 class BackupDataReader
 {
 public:
-    BackupDataReader(int fd);
+    explicit BackupDataReader(int fd);
     // does not close fd
     ~BackupDataReader();
 
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index 29424c4..6fa089a 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -82,7 +82,7 @@
   static void ApplyVersionForCompatibility(ConfigDescription* config);
 
   ConfigDescription();
-  ConfigDescription(const android::ResTable_config& o);  // NOLINT(implicit)
+  ConfigDescription(const android::ResTable_config& o);  // NOLINT(google-explicit-constructor)
   ConfigDescription(const ConfigDescription& o);
   ConfigDescription(ConfigDescription&& o) noexcept;
 
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
index e1dfb94..bf35aa3 100644
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
@@ -22,7 +22,7 @@
 
 class DisplayEventDispatcher : public LooperCallback {
 public:
-    DisplayEventDispatcher(const sp<Looper>& looper,
+    explicit DisplayEventDispatcher(const sp<Looper>& looper,
             ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
 
     status_t initialize();
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 8c5c3b7..be62f30 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -78,6 +78,8 @@
 using TypeSpecPtr = util::unique_cptr<TypeSpec>;
 
 struct OverlayableInfo {
+  std::string name;
+  std::string actor;
   uint32_t policy_flags;
 };
 
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index cf2d8fb..1655e89 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -693,7 +693,7 @@
 class ResXMLParser
 {
 public:
-    ResXMLParser(const ResXMLTree& tree);
+    explicit ResXMLParser(const ResXMLTree& tree);
 
     enum event_code_t {
         BAD_DOCUMENT = -1,
@@ -806,7 +806,7 @@
      * The tree stores a clone of the specified DynamicRefTable, so any changes to the original
      * DynamicRefTable will not affect this tree after instantiation.
      **/
-    ResXMLTree(const DynamicRefTable* dynamicRefTable);
+    explicit ResXMLTree(const DynamicRefTable* dynamicRefTable);
     ResXMLTree();
     ~ResXMLTree();
 
@@ -1611,6 +1611,12 @@
 struct ResTable_overlayable_header
 {
   struct ResChunk_header header;
+
+  // The name of the overlayable set of resources that overlays target.
+  uint16_t name[256];
+
+ // The component responsible for enabling and disabling overlays targeting this chunk.
+  uint16_t actor[256];
 };
 
 /**
@@ -1637,10 +1643,6 @@
     // The overlay must reside of the product partition or must have existed on the product
     // partition before an upgrade to overlay these resources.
     POLICY_PRODUCT_PARTITION = 0x00000008,
-
-    // The overlay must reside of the product services partition or must have existed on the product
-    // services partition before an upgrade to overlay these resources.
-    POLICY_PRODUCT_SERVICES_PARTITION = 0x00000010,
   };
   uint32_t policy_flags;
 
@@ -1844,7 +1846,7 @@
 
     class Theme {
     public:
-        Theme(const ResTable& table);
+        explicit Theme(const ResTable& table);
         ~Theme();
 
         inline const ResTable& getResTable() const { return mTable; }
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index d94779b..eb6eb8e 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -17,6 +17,7 @@
 #ifndef ANDROIDFW_RESOURCEUTILS_H
 #define ANDROIDFW_RESOURCEUTILS_H
 
+#include "androidfw/AssetManager2.h"
 #include "androidfw/StringPiece.h"
 
 namespace android {
@@ -27,6 +28,17 @@
 bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type,
                          StringPiece* out_entry);
 
+// Convert a type_string_ref, entry_string_ref, and package
+// to AssetManager2::ResourceName. Useful for getting
+// resource name without re-running AssetManager2::FindEntry searches.
+bool ToResourceName(StringPoolRef& type_string_ref,
+                    StringPoolRef& entry_string_ref,
+                    const LoadedPackage* package,
+                    AssetManager2::ResourceName* out_name);
+
+// Formats a ResourceName to "package:type/entry_name".
+std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name);
+
 inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
   return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
 }
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index a33865f..921877d 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -52,8 +52,8 @@
 
   BasicStringPiece();
   BasicStringPiece(const BasicStringPiece<TChar>& str);
-  BasicStringPiece(const std::basic_string<TChar>& str);  // NOLINT(implicit)
-  BasicStringPiece(const TChar* str);                     // NOLINT(implicit)
+  BasicStringPiece(const std::basic_string<TChar>& str);  // NOLINT(google-explicit-constructor)
+  BasicStringPiece(const TChar* str);                     // NOLINT(google-explicit-constructor)
   BasicStringPiece(const TChar* str, size_t len);
 
   BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index 5cfe54e5..fb2fad6 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct TypeVariant {
-    TypeVariant(const ResTable_type* data);
+    explicit TypeVariant(const ResTable_type* data);
 
     class iterator {
     public:
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 10d088e..aa1466f 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -46,7 +46,7 @@
   using pointer = typename std::add_pointer<T>::type;
 
   constexpr unique_cptr() : ptr_(nullptr) {}
-  constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
+  constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
   explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
   unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
 
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 5449a54..105dcd2 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -586,4 +586,111 @@
   EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory));
 }
 
+TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) {
+  ResTable_config desired_config;
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+  assetmanager.SetResourceResolutionLoggingEnabled(false);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) {
+  ResTable_config desired_config;
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetResourceResolutionLoggingEnabled(true);
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetResourceResolutionLoggingEnabled(true);
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
+
+  Res_value value = Res_value();
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+
+  AssetManager2 assetmanager;
+  assetmanager.SetResourceResolutionLoggingEnabled(true);
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value = Res_value();
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto resultEnabled = assetmanager.GetLastResourceResolution();
+  ASSERT_NE("", resultEnabled);
+
+  assetmanager.SetResourceResolutionLoggingEnabled(false);
+
+  auto resultDisabled = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("", resultDisabled);
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 22d587a..b8d3c6b 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -294,22 +294,29 @@
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable1);
   ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->name, Eq("OverlayableResources1"));
+  EXPECT_THAT(info->actor, Eq("overlay://theme"));
   EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
   ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->name, Eq("OverlayableResources1"));
+  EXPECT_THAT(info->actor, Eq("overlay://theme"));
   EXPECT_THAT(info->policy_flags,
               Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
                  | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
   ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->name, Eq("OverlayableResources2"));
+  EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable"));
   EXPECT_THAT(info->policy_flags,
               Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
-                 | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION
                  | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
+  EXPECT_THAT(info->name, Eq("OverlayableResources1"));
+  EXPECT_THAT(info->actor, Eq("overlay://theme"));
   ASSERT_THAT(info, NotNull());
   EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
 }
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
index 85ab4be..047e6af 100644
--- a/libs/androidfw/tests/data/overlayable/overlayable.apk
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
index 11aa735..fcdbe94 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -15,7 +15,7 @@
 -->
 
 <resources>
-<overlayable>
+<overlayable name="OverlayableResources1" actor="overlay://theme">
     <!-- Any overlay can overlay the value of @string/overlayable1 -->
     <item type="string" name="overlayable1" />
 
@@ -31,10 +31,10 @@
     </policy>
 </overlayable>
 
-<overlayable>
-    <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of
-   @string/overlayable3 -->
-    <policy type="product_services|vendor|product">
+<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
+    <!-- Any overlay on the vendor or product partition can overlay the value of
+        @string/overlayable3 -->
+    <policy type="vendor|product">
         <item type="string" name="overlayable3" />
     </policy>
 </overlayable>
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index ed167e5..56b1885 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -79,8 +79,7 @@
     switch (wcgDataspace) {
         case ui::Dataspace::DISPLAY_P3:
             *colorGamut = SkColorSpace::Gamut::kDCIP3_D65_Gamut;
-            *colorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                SkColorSpace::Gamut::kDCIP3_D65_Gamut);
+            *colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
             break;
         case ui::Dataspace::V0_SCRGB:
             *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index b9860ad..635d0ec 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -34,7 +34,7 @@
 namespace android::uirenderer {
 
 static std::mutex sLock{};
-static ThreadBase* sUploadThread = nullptr;
+static sp<ThreadBase> sUploadThread = nullptr;
 static renderthread::EglManager sEglManager;
 static int sPendingUploads = 0;
 static nsecs_t sLastUpload = 0;
@@ -257,4 +257,15 @@
                               Bitmap::computePalette(bitmap));
 }
 
+void HardwareBitmapUploader::terminate() {
+    std::lock_guard _lock{sLock};
+    LOG_ALWAYS_FATAL_IF(sPendingUploads, "terminate called while uploads in progress");
+    if (sUploadThread) {
+        sUploadThread->requestExit();
+        sUploadThread->join();
+        sUploadThread = nullptr;
+    }
+    sEglManager.destroy();
+}
+
 }  // namespace android::uirenderer
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 6298013..40f2b0c 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -23,6 +23,7 @@
 class HardwareBitmapUploader {
 public:
     static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
+    static void terminate();
 };
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index b33cfe2..0c515a4 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -81,7 +81,7 @@
 
     explicit Matrix4(const float* v) { load(v); }
 
-    Matrix4(const SkMatrix& v) {  // NOLINT, implicit
+    Matrix4(const SkMatrix& v) {  // NOLINT(google-explicit-constructor)
         load(v);
     }
 
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index d6362ef..24443c8 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -57,15 +57,15 @@
 
     inline Rect(float width, float height) : left(0.0f), top(0.0f), right(width), bottom(height) {}
 
-    inline Rect(const SkIRect& rect)
-            :  // NOLINT, implicit
+    inline Rect(const SkIRect& rect) // NOLINT(google-explicit-constructor)
+            :
             left(rect.fLeft)
             , top(rect.fTop)
             , right(rect.fRight)
             , bottom(rect.fBottom) {}
 
-    inline Rect(const SkRect& rect)
-            :  // NOLINT, implicit
+    inline Rect(const SkRect& rect) // NOLINT(google-explicit-constructor)
+            :
             left(rect.fLeft)
             , top(rect.fTop)
             , right(rect.fRight)
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index ab95e69..cc62fdc 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -678,15 +678,15 @@
 // Canvas draw operations: Text
 // ----------------------------------------------------------------------------
 
-void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
                             float y, float boundsLeft, float boundsTop, float boundsRight,
                             float boundsBottom, float totalAdvance) {
     if (count <= 0 || paint.nothingToDraw()) return;
-    SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
     SkPaint paintCopy(paint);
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
+    SkFont font = SkFont::LEGACY_ExtractFromPaint(paintCopy);
     SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
     // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
     // older.
@@ -708,13 +708,13 @@
 }
 
 void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
-                                  const SkPaint& paint, const SkPath& path, size_t start,
+                                  const Paint& paint, const SkPath& path, size_t start,
                                   size_t end) {
-    SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
     SkPaint paintCopy(paint);
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
+    SkFont font = SkFont::LEGACY_ExtractFromPaint(paintCopy);
     SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
 
     const int N = end - start;
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 4ab0a59..3fe2bce 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -158,11 +158,11 @@
     void reset(SkCanvas* skiaCanvas);
     void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); }
 
-    virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+    virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
                             float y, float boundsLeft, float boundsTop, float boundsRight,
                             float boundsBottom, float totalAdvance) override;
     virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
-                                  const SkPaint& paint, const SkPath& path, size_t start,
+                                  const Paint& paint, const SkPath& path, size_t start,
                                   size_t end) override;
 
     /** This class acts as a copy on write SkPaint.
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 5b7ae70..68541b4 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -18,6 +18,7 @@
 
 #include <private/hwui/WebViewFunctor.h>
 #include "Properties.h"
+#include "renderthread/RenderThread.h"
 
 #include <log/log.h>
 #include <utils/Trace.h>
@@ -85,11 +86,35 @@
     mCallbacks.gles.draw(mFunctor, mData, drawInfo);
 }
 
+void WebViewFunctor::initVk(const VkFunctorInitParams& params) {
+    ATRACE_NAME("WebViewFunctor::initVk");
+    if (!mHasContext) {
+        mHasContext = true;
+    } else {
+        return;
+    }
+    mCallbacks.vk.initialize(mFunctor, mData, params);
+}
+
+void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {
+    ATRACE_NAME("WebViewFunctor::drawVk");
+    mCallbacks.vk.draw(mFunctor, mData, params);
+}
+
+void WebViewFunctor::postDrawVk() {
+    ATRACE_NAME("WebViewFunctor::postDrawVk");
+    mCallbacks.vk.postDraw(mFunctor, mData);
+}
+
 void WebViewFunctor::destroyContext() {
     if (mHasContext) {
         mHasContext = false;
         ATRACE_NAME("WebViewFunctor::onContextDestroyed");
         mCallbacks.onContextDestroyed(mFunctor, mData);
+
+        // grContext may be null in unit tests.
+        auto* grContext = renderthread::RenderThread::getInstance().getGrContext();
+        if (grContext) grContext->resetContext();
     }
 }
 
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 1719ce7..2846cb1 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -42,6 +42,12 @@
 
         void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
 
+        void initVk(const VkFunctorInitParams& params) { mReference.initVk(params); }
+
+        void drawVk(const VkFunctorDrawParams& params) { mReference.drawVk(params); }
+
+        void postDrawVk() { mReference.postDrawVk(); }
+
     private:
         friend class WebViewFunctor;
 
@@ -53,6 +59,9 @@
     int id() const { return mFunctor; }
     void sync(const WebViewSyncData& syncData) const;
     void drawGl(const DrawGlInfo& drawInfo);
+    void initVk(const VkFunctorInitParams& params);
+    void drawVk(const VkFunctorDrawParams& params);
+    void postDrawVk();
     void destroyContext();
 
     sp<Handle> createHandle() {
diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hwui/debug/GlesErrorCheckWrapper.h
index ee5cc1f..791400b 100644
--- a/libs/hwui/debug/GlesErrorCheckWrapper.h
+++ b/libs/hwui/debug/GlesErrorCheckWrapper.h
@@ -24,7 +24,7 @@
 
 class GlesErrorCheckWrapper : public GlesDriver {
 public:
-    GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {}
+    explicit GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {}
 
 #define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override;
 #include "gles_decls.in"
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index a09da6b..277148e 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -38,7 +38,7 @@
     canvas->drawRect(left, top, right, bottom, paint);
 }
 
-void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
+void Canvas::drawTextDecorations(float x, float y, float length, const Paint& paint) {
     uint32_t flags;
     PaintFilter* paintFilter = getPaintFilter();
     if (paintFilter) {
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 4c5365d..11e8579 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -303,18 +303,18 @@
     static int GetApiLevel() { return sApiLevel; }
 
 protected:
-    void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+    void drawTextDecorations(float x, float y, float length, const Paint& paint);
 
     /**
      * glyphFunc: valid only for the duration of the call and should not be cached.
      * drawText: count is of glyphs
      * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
      */
-    virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+    virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
                             float y, float boundsLeft, float boundsTop, float boundsRight,
                             float boundsBottom, float totalAdvance) = 0;
     virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
-                                  const SkPaint& paint, const SkPath& path, size_t start,
+                                  const Paint& paint, const SkPath& path, size_t start,
                                   size_t end) = 0;
     static int sApiLevel;
 
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index c1a3b6d..92ffda9 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -46,7 +46,7 @@
 
     Paint();
     Paint(const Paint& paint);
-    Paint(const SkPaint& paint);  // NOLINT(implicit)
+    Paint(const SkPaint& paint);  // NOLINT(google-explicit-constructor)
     ~Paint();
 
     Paint& operator=(const Paint& other);
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 562a3b2..1661905 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -23,6 +23,7 @@
 #include "FileBlobCache.h"
 #include "Properties.h"
 #include "utils/TraceUtils.h"
+#include <GrContext.h>
 
 namespace android {
 namespace uirenderer {
@@ -168,6 +169,24 @@
     const void* value = data.data();
 
     BlobCache* bc = getBlobCacheLocked();
+    if (mInStoreVkPipelineInProgress) {
+        if (mOldPipelineCacheSize == -1) {
+            // Record the initial pipeline cache size stored in the file.
+            mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0);
+        }
+        if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) {
+            // There has not been change in pipeline cache size. Stop trying to save.
+            mTryToStorePipelineCache = false;
+            return;
+        }
+        mNewPipelineCacheSize = valueSize;
+    } else {
+        mCacheDirty = true;
+        // If there are new shaders compiled, we probably have new pipeline state too.
+        // Store pipeline cache on the next flush.
+        mNewPipelineCacheSize = -1;
+        mTryToStorePipelineCache = true;
+    }
     bc->set(key.data(), keySize, value, valueSize);
 
     if (!mSavePending && mDeferredSaveDelay > 0) {
@@ -175,12 +194,31 @@
         std::thread deferredSaveThread([this]() {
             sleep(mDeferredSaveDelay);
             std::lock_guard<std::mutex> lock(mMutex);
-            saveToDiskLocked();
+            // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
+            if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
+                saveToDiskLocked();
+                mOldPipelineCacheSize = mNewPipelineCacheSize;
+                mTryToStorePipelineCache = false;
+                mCacheDirty = false;
+            }
         });
         deferredSaveThread.detach();
     }
 }
 
+void ShaderCache::onVkFrameFlushed(GrContext* context) {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+
+        if (!mInitialized || !mTryToStorePipelineCache) {
+            return;
+        }
+    }
+    mInStoreVkPipelineInProgress = true;
+    context->storeVkPipelineCacheData();
+    mInStoreVkPipelineInProgress = false;
+}
+
 } /* namespace skiapipeline */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index d41aadb..0898017 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -75,6 +75,13 @@
      */
     void store(const SkData& key, const SkData& data) override;
 
+    /**
+     * "onVkFrameFlushed" tries to store Vulkan pipeline cache state.
+     * Pipeline cache is saved on disk only if the size of the data has changed or there was
+     * a new shader compiled.
+     */
+    void onVkFrameFlushed(GrContext* context);
+
 private:
     // Creation and (the lack of) destruction is handled internally.
     ShaderCache();
@@ -167,6 +174,33 @@
     mutable std::mutex mMutex;
 
     /**
+     *  If set to "true", the next call to onVkFrameFlushed, will invoke
+     * GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
+     */
+    bool mTryToStorePipelineCache = true;
+
+    /**
+     * This flag is used by "ShaderCache::store" to distinguish between shader data and
+     * Vulkan pipeline data.
+     */
+    bool mInStoreVkPipelineInProgress = false;
+
+    /**
+     *  "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
+     *  to prevent unnecessary disk writes, if the pipeline cache size has not changed.
+     */
+    size_t mNewPipelineCacheSize = -1;
+    /**
+     *  "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
+     */
+    size_t mOldPipelineCacheSize = -1;
+
+    /**
+     *  "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
+     */
+    bool mCacheDirty = false;
+
+    /**
      * "sCache" is the singleton ShaderCache object.
      */
     static ShaderCache sCache;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 4338b1c..cfbb995 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -96,11 +96,12 @@
 
     SkASSERT(mRenderThread.getGrContext() != nullptr);
     sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
-            mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType,
+            mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,
             mSurfaceColorSpace, &props));
 
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
-    renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     layerUpdateQueue->clear();
 
     // Draw visual debugging features
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 47991069..6692922 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -39,6 +39,7 @@
               const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
               const std::vector<sp<RenderNode> >& renderNodes,
               FrameInfoVisualizer* profiler) override;
+    GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; }
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 2e7850d..df82243 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -174,7 +174,8 @@
         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
         SkASSERT(mRenderThread.getGrContext() != nullptr);
         node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
-                                                          SkBudgeted::kYes, info, 0, &props));
+                                                          SkBudgeted::kYes, info, 0,
+                                                          this->getSurfaceOrigin(), &props));
         if (node->getLayerSurface()) {
             // update the transform in window of the layer to reset its origin wrt light source
             // position
@@ -311,7 +312,8 @@
 
 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
                                const std::vector<sp<RenderNode>>& nodes, bool opaque,
-                               const Rect& contentDrawBounds, sk_sp<SkSurface> surface) {
+                               const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
+                               const SkMatrix& preTransform) {
     renderVectorDrawableCache();
 
     // draw all layers up front
@@ -322,12 +324,12 @@
     std::unique_ptr<SkPictureRecorder> recorder;
     SkCanvas* canvas = tryCapture(surface.get());
 
-    renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
+    renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
 
     endCapture(surface.get());
 
     if (CC_UNLIKELY(Properties::debugOverdraw)) {
-        renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
+        renderOverdraw(layers, clip, nodes, contentDrawBounds, surface, preTransform);
     }
 
     ATRACE_NAME("flush commands");
@@ -343,9 +345,11 @@
 
 void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
                                    const std::vector<sp<RenderNode>>& nodes, bool opaque,
-                                   const Rect& contentDrawBounds, SkCanvas* canvas) {
+                                   const Rect& contentDrawBounds, SkCanvas* canvas,
+                                   const SkMatrix& preTransform) {
     SkAutoCanvasRestore saver(canvas, true);
-    canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
+    canvas->androidFramework_setDeviceClipRestriction(preTransform.mapRect(clip).roundOut());
+    canvas->concat(preTransform);
 
     // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
     if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) {
@@ -485,7 +489,8 @@
 
 void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
                                   const std::vector<sp<RenderNode>>& nodes,
-                                  const Rect& contentDrawBounds, sk_sp<SkSurface> surface) {
+                                  const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
+                                  const SkMatrix& preTransform) {
     // Set up the overdraw canvas.
     SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
     sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
@@ -495,7 +500,7 @@
     // each time a pixel would have been drawn.
     // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
     // initialized.
-    renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas);
+    renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform);
     sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
 
     // Draw overdraw colors to the canvas.  The color filter will convert counts to colors.
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index ff87313..cf6f5b2 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -48,12 +48,13 @@
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                              ErrorHandler* errorHandler) override;
 
-    SkColorType getSurfaceColorType() const { return mSurfaceColorType; }
+    SkColorType getSurfaceColorType() const override { return mSurfaceColorType; }
     sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; }
 
     void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
                      const std::vector<sp<RenderNode>>& nodes, bool opaque,
-                     const Rect& contentDrawBounds, sk_sp<SkSurface> surface);
+                     const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
+                     const SkMatrix& preTransform);
 
     std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
 
@@ -116,7 +117,8 @@
 private:
     void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
                          const std::vector<sp<RenderNode>>& nodes, bool opaque,
-                         const Rect& contentDrawBounds, SkCanvas* canvas);
+                         const Rect& contentDrawBounds, SkCanvas* canvas,
+                         const SkMatrix& preTransform);
 
     /**
      *  Debugging feature.  Draws a semi-transparent overlay on each pixel, indicating
@@ -124,7 +126,7 @@
      */
     void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
                         const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds,
-                        sk_sp<SkSurface>);
+                        sk_sp<SkSurface> surface, const SkMatrix& preTransform);
 
     /**
      *  Render mVectorDrawables into offscreen buffers.
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 6eefed9..d54275f 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -125,8 +125,6 @@
                                              uirenderer::GlFunctorLifecycleListener* listener) {
     FunctorDrawable* functorDrawable;
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
-        // interop is disabled/moved.
         functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
                 functor, listener, asSkCanvas());
     } else {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 1d3a244..53495a7 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -23,6 +23,7 @@
 #include "VkInteropFunctorDrawable.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
+#include "ShaderCache.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -57,7 +58,8 @@
         return Frame(-1, -1, 0);
     }
 
-    Frame frame(backBuffer->width(), backBuffer->height(), mVkManager.getAge(mVkSurface));
+    Frame frame(mVkSurface->windowWidth(), mVkSurface->windowHeight(),
+                mVkManager.getAge(mVkSurface));
     return frame;
 }
 
@@ -72,7 +74,9 @@
         return false;
     }
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
-    renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
+    renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
+            backBuffer, mVkSurface->preTransform());
+    ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext());
     layerUpdateQueue->clear();
 
     // Draw visual debugging features
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 53ffc44..9343076 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -35,6 +35,7 @@
               const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
               const std::vector<sp<RenderNode> >& renderNodes,
               FrameInfoVisualizer* profiler) override;
+    GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; }
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 156f74a..2f8d381 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -17,6 +17,8 @@
 #include "VkFunctorDrawable.h"
 #include <private/hwui/DrawVkInfo.h>
 
+#include "renderthread/VulkanManager.h"
+#include "renderthread/RenderThread.h"
 #include <GrBackendDrawableInfo.h>
 #include <SkImage.h>
 #include <utils/Color.h>
@@ -31,34 +33,58 @@
 namespace uirenderer {
 namespace skiapipeline {
 
-VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
+VkFunctorDrawHandler::VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle,
+                                           const SkMatrix& matrix, const SkIRect& clip,
+                                           const SkImageInfo& image_info)
+        : INHERITED()
+        , mFunctorHandle(functor_handle)
+        , mMatrix(matrix)
+        , mClip(clip)
+        , mImageInfo(image_info) {}
 
 VkFunctorDrawHandler::~VkFunctorDrawHandler() {
-    // TODO(cblume) Fill in the DrawVkInfo parameters.
-    (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr);
+    mFunctorHandle->postDrawVk();
 }
 
 void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
     ATRACE_CALL();
+    if (!renderthread::RenderThread::isCurrent())
+        LOG_ALWAYS_FATAL("VkFunctorDrawHandler::draw not called on render thread");
 
     GrVkDrawableInfo vulkan_info;
     if (!info.getVkDrawableInfo(&vulkan_info)) {
         return;
     }
+    renderthread::VulkanManager& vk_manager =
+            renderthread::RenderThread::getInstance().vulkanManager();
+    mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams());
 
-    DrawVkInfo draw_vk_info;
-    // TODO(cblume) Fill in the rest of the parameters and test the actual call.
-    draw_vk_info.isLayer = true;
+    SkMatrix44 mat4(mMatrix);
+    VkFunctorDrawParams params{
+      .width = mImageInfo.width(),
+      .height = mImageInfo.height(),
+      .is_layer = false,  // TODO(boliu): Populate is_layer.
+      .color_space_ptr = mImageInfo.colorSpace(),
+      .clip_left = mClip.fLeft,
+      .clip_top = mClip.fTop,
+      .clip_right = mClip.fRight,
+      .clip_bottom = mClip.fBottom,
+    };
+    mat4.asColMajorf(&params.transform[0]);
+    params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
+    params.color_attachment_index = vulkan_info.fColorAttachmentIndex;
+    params.compatible_render_pass = vulkan_info.fCompatibleRenderPass;
+    params.format = vulkan_info.fFormat;
 
-    (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
+    mFunctorHandle->drawVk(params);
+
+    vulkan_info.fDrawBounds->offset.x = mClip.fLeft;
+    vulkan_info.fDrawBounds->offset.y = mClip.fTop;
+    vulkan_info.fDrawBounds->extent.width = mClip.fRight - mClip.fLeft;
+    vulkan_info.fDrawBounds->extent.height = mClip.fBottom - mClip.fTop;
 }
 
 VkFunctorDrawable::~VkFunctorDrawable() {
-    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
-        if (lp->listener) {
-            lp->listener->onGlFunctorReleased(lp->functor);
-        }
-    }
 }
 
 void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
@@ -67,16 +93,17 @@
 }
 
 std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
-        GrBackendApi backendApi, const SkMatrix& matrix) {
+        GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip,
+        const SkImageInfo& image_info) {
     if (backendApi != GrBackendApi::kVulkan) {
         return nullptr;
     }
     std::unique_ptr<VkFunctorDrawHandler> draw;
     if (mAnyFunctor.index() == 0) {
-        LOG_ALWAYS_FATAL("Not implemented");
-        return nullptr;
+        return std::make_unique<VkFunctorDrawHandler>(std::get<0>(mAnyFunctor).handle, matrix, clip,
+                                                      image_info);
     } else {
-        return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
+        LOG_ALWAYS_FATAL("Not implemented");
     }
 }
 
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index d6fefc1..1a53c8f 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -32,15 +32,18 @@
  */
 class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler {
 public:
-    explicit VkFunctorDrawHandler(Functor* functor);
+    VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle, const SkMatrix& matrix,
+                         const SkIRect& clip, const SkImageInfo& image_info);
     ~VkFunctorDrawHandler() override;
 
     void draw(const GrBackendDrawableInfo& info) override;
 
 private:
     typedef GpuDrawHandler INHERITED;
-
-    Functor* mFunctor;
+    sp<WebViewFunctor::Handle> mFunctorHandle;
+    const SkMatrix mMatrix;
+    const SkIRect mClip;
+    const SkImageInfo mImageInfo;
 };
 
 /**
@@ -57,7 +60,8 @@
     // SkDrawable functions:
     void onDraw(SkCanvas* canvas) override;
     std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
-            GrBackendApi backendApi, const SkMatrix& matrix) override;
+            GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip,
+            const SkImageInfo& image_info) override;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index a5faae7..c3563db 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -36,8 +36,6 @@
 namespace uirenderer {
 namespace skiapipeline {
 
-static std::mutex sLock{};
-static ThreadBase* sGLDrawThread = nullptr;
 static renderthread::EglManager sEglManager;
 
 // ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
@@ -47,32 +45,20 @@
 
 private:
     void beginDraw() {
-        std::lock_guard _lock{sLock};
-
-        if (!sGLDrawThread) {
-            sGLDrawThread = new ThreadBase{};
-        }
-
-        if (!sGLDrawThread->isRunning()) {
-            sGLDrawThread->start("GLFunctorThread");
-        }
-
         if (!sEglManager.hasEglContext()) {
-            sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
+            sEglManager.initialize();
         }
     }
 };
 
 void VkInteropFunctorDrawable::vkInvokeFunctor(Functor* functor) {
     ScopedDrawRequest _drawRequest{};
-    sGLDrawThread->queue().runSync([&]() {
-        EGLDisplay display = sEglManager.eglDisplay();
-        DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
-        if (display != EGL_NO_DISPLAY) {
-            mode = DrawGlInfo::kModeProcess;
-        }
-        (*functor)(mode, nullptr);
-    });
+    EGLDisplay display = sEglManager.eglDisplay();
+    DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+    if (display != EGL_NO_DISPLAY) {
+        mode = DrawGlInfo::kModeProcess;
+    }
+    (*functor)(mode, nullptr);
 }
 
 #define FENCE_TIMEOUT 2000000000
@@ -113,7 +99,7 @@
     // TODO: draw command has completed.
     // TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
     // TODO: GrVkGpu::destroyResources() for example.
-    bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+    {
         ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
         EGLDisplay display = sEglManager.eglDisplay();
         LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
@@ -125,7 +111,7 @@
         if (autoImage.image == EGL_NO_IMAGE_KHR) {
             ALOGW("Could not create EGL image, err =%s",
                   uirenderer::renderthread::EglManager::eglErrorString());
-            return false;
+            return;
         }
 
         AutoSkiaGlTexture glTexture;
@@ -156,7 +142,7 @@
         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
             ALOGE("Failed framebuffer check for created target buffer: %s",
                   GLUtils::getGLFramebufferError());
-            return false;
+            return;
         }
 
         glDisable(GL_STENCIL_TEST);
@@ -183,11 +169,6 @@
         LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
                             "Failed to wait for the fence %#x", eglGetError());
         eglDestroySyncKHR(display, glDrawFinishedFence);
-        return true;
-    });
-
-    if (!success) {
-        return;
     }
 
     SkPaint paint;
@@ -208,15 +189,14 @@
     if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
         if (lp->listener) {
             ScopedDrawRequest _drawRequest{};
-            sGLDrawThread->queue().runSync(
-                    [&]() { lp->listener->onGlFunctorReleased(lp->functor); });
+            lp->listener->onGlFunctorReleased(lp->functor);
         }
     }
 }
 
 void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
     ScopedDrawRequest _drawRequest{};
-    sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
+    FunctorDrawable::syncFunctor(data);
 }
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index 019950f..abc4dbf 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -17,89 +17,61 @@
 #ifndef ANDROID_HWUI_DRAW_VK_INFO_H
 #define ANDROID_HWUI_DRAW_VK_INFO_H
 
+#include <SkColorSpace.h>
 #include <vulkan/vulkan.h>
 
 namespace android {
 namespace uirenderer {
 
-/**
- * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan
- * functors.
- */
-struct DrawVkInfo {
-    // Input: current width/height of destination surface
-    int width;
-    int height;
+struct VkFunctorInitParams {
+  VkInstance instance;
+  VkPhysicalDevice physical_device;
+  VkDevice device;
+  VkQueue queue;
+  uint32_t graphics_queue_index;
+  uint32_t instance_version;
+  const char* const* enabled_instance_extension_names;
+  uint32_t enabled_instance_extension_names_length;
+  const char* const* enabled_device_extension_names;
+  uint32_t enabled_device_extension_names_length;
+  const VkPhysicalDeviceFeatures2* device_features_2;
+};
 
-    // Input: is the render target an FBO
-    bool isLayer;
+struct VkFunctorDrawParams {
+  // Input: current width/height of destination surface.
+  int width;
+  int height;
 
-    // Input: current transform matrix, in OpenGL format
-    float transform[16];
+  // Input: is the render target a FBO
+  bool is_layer;
 
-    // Input: WebView should do its main compositing draws into this. It cannot do anything that
-    // would require stopping the render pass.
-    VkCommandBuffer secondaryCommandBuffer;
+  // Input: current transform matrix
+  float transform[16];
 
-    // Input: The main color attachment index where secondaryCommandBuffer will eventually be
-    // submitted.
-    uint32_t colorAttachmentIndex;
+  // Input WebView should do its main compositing draws into this. It cannot do
+  // anything that would require stopping the render pass.
+  VkCommandBuffer secondary_command_buffer;
 
-    // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer
-    // will be submitted into.
-    VkRenderPass compatibleRenderPass;
+  // Input: The main color attachment index where secondary_command_buffer will
+  // eventually be submitted.
+  uint32_t color_attachment_index;
 
-    // Input: Format of the destination surface.
-    VkFormat format;
+  // Input: A render pass which will be compatible to the one which the
+  // secondary_command_buffer will be submitted into.
+  VkRenderPass compatible_render_pass;
 
-    // Input: Color space transfer params
-    float G;
-    float A;
-    float B;
-    float C;
-    float D;
-    float E;
-    float F;
+  // Input: Format of the destination surface.
+  VkFormat format;
 
-    // Input: Color space transformation from linear RGB to D50-adapted XYZ
-    float matrix[9];
+  // Input: Color space.
+  const SkColorSpace* color_space_ptr;
 
-    // Input: current clip rect
-    int clipLeft;
-    int clipTop;
-    int clipRight;
-    int clipBottom;
-
-    /**
-     * Values used as the "what" parameter of the functor.
-     */
-    enum Mode {
-        // Called once at WebView start
-        kModeInit,
-        // Called when things need to be re-created
-        kModeReInit,
-        // Notifies the app that the composite functor will be called soon. This allows WebView to
-        // begin work early.
-        kModePreComposite,
-        // Do the actual composite work
-        kModeComposite,
-        // This allows WebView to begin using the previously submitted objects in future work.
-        kModePostComposite,
-        // Invoked every time the UI thread pushes over a frame to the render thread and the owning
-        // view has a dirty display list*. This is a signal to sync any data that needs to be
-        // shared between the UI thread and the render thread. During this time the UI thread is
-        // blocked.
-        kModeSync
-    };
-
-    /**
-     * Values used by Vulkan functors to tell the framework what to do next.
-     */
-    enum Status {
-        // The functor is done
-        kStatusDone = 0x0,
-    };
-};  // struct DrawVkInfo
+  // Input: current clip rect
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+};
 
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index da3d06a..96da947 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -19,6 +19,7 @@
 
 #include <cutils/compiler.h>
 #include <private/hwui/DrawGlInfo.h>
+#include <private/hwui/DrawVkInfo.h>
 
 namespace android::uirenderer {
 
@@ -52,18 +53,12 @@
             // Called on RenderThread. initialize is guaranteed to happen before this call
             void (*draw)(int functor, void* data, const DrawGlInfo& params);
         } gles;
-        // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
-        // what params are valid on what callbacks
         struct {
             // Called either the first time the functor is used or the first time it's used after
             // a call to onContextDestroyed.
-            // void (*initialize)(int functor, const InitParams& params);
-            // void (*frameStart)(int functor, /* todo: what params are actually needed for this to
-            // be useful? Is this useful? */)
-            // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite
-            // almost always means something else, and we aren't compositing */);
-            // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as
-            // CompositeParams - rename */);
+            void (*initialize)(int functor, void* data, const VkFunctorInitParams& params);
+            void (*draw)(int functor, void* data, const VkFunctorDrawParams& params);
+            void (*postDraw)(int functor, void*);
         } vk;
     };
 };
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 7acc44c..6c04232 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -30,6 +30,7 @@
 #include <gui/Surface.h>
 #include <math.h>
 #include <set>
+#include <SkMathPriv.h>
 
 namespace android {
 namespace uirenderer {
@@ -42,11 +43,6 @@
 #define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
 #define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
 
-// for super large fonts we will draw them as paths so no need to keep linearly
-// increasing the font cache size.
-#define FONT_CACHE_MIN_MB (0.5f)
-#define FONT_CACHE_MAX_MB (4.0f)
-
 CacheManager::CacheManager(const DisplayInfo& display) : mMaxSurfaceArea(display.w * display.h) {
     mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
             mMaxSurfaceArea / 2,
@@ -106,25 +102,10 @@
 void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) {
     contextOptions->fAllowPathMaskCaching = true;
 
-    float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f;
-    float fontCacheMB = 0;
-    float decimalVal = std::modf(screenMP, &fontCacheMB);
-
-    // This is a basic heuristic to size the cache to a multiple of 512 KB
-    if (decimalVal > 0.8f) {
-        fontCacheMB += 1.0f;
-    } else if (decimalVal > 0.5f) {
-        fontCacheMB += 0.5f;
-    }
-
-    // set limits on min/max size of the cache
-    fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB));
-
-    // We must currently set the size of the text cache based on the size of the
-    // display even though we like to  be dynamicallysizing it to the size of the window.
-    // Skia's implementation doesn't provide a mechanism to resize the font cache due to
-    // the potential cost of recreating the glyphs.
-    contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024;
+    // This sets the maximum size for a single texture atlas in the GPU font cache.  If necessary,
+    // the cache can allocate additional textures that are counted against the total cache limits
+    // provided to Skia.
+    contextOptions->fGlyphCacheTextureMaximumBytes = GrNextSizePow2(mMaxSurfaceArea);
 
     if (mTaskManager.canRunTasks()) {
         if (!mTaskProcessor.get()) {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 56eedff..8cd97ed 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -93,7 +93,9 @@
         , mHasWideColorGamutSupport(false) {}
 
 EglManager::~EglManager() {
-    destroy();
+    if (hasEglContext()) {
+        ALOGW("~EglManager() leaked an EGL context");
+    }
 }
 
 void EglManager::initialize() {
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 42e17b273..d4dd629 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -84,6 +84,7 @@
     virtual void onPrepareTree() = 0;
     virtual SkColorType getSurfaceColorType() const = 0;
     virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
+    virtual GrSurfaceOrigin getSurfaceOrigin() = 0;
 
     virtual ~IRenderPipeline() {}
 };
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c06fadd..8bef359 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -171,6 +171,9 @@
     mRenderState = new RenderState(*this);
     mVkManager = new VulkanManager(*this);
     mCacheManager = new CacheManager(mDisplayInfo);
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        mVkManager->initialize();
+    }
 }
 
 void RenderThread::requireGlContext() {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 12666b3..1ef83fb 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -47,6 +47,10 @@
 class RenderState;
 class TestUtils;
 
+namespace skiapipeline {
+class VkFunctorDrawHandler;
+}
+
 namespace renderthread {
 
 class CanvasContext;
@@ -123,6 +127,8 @@
     friend class RenderProxy;
     friend class DummyVsyncSource;
     friend class android::uirenderer::TestUtils;
+    friend class android::uirenderer::WebViewFunctor;
+    friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler;
 
     RenderThread();
     virtual ~RenderThread();
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index aa7a141..5c6cb9a 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -34,6 +34,23 @@
 namespace uirenderer {
 namespace renderthread {
 
+static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
+    // All Vulkan structs that could be part of the features chain will start with the
+    // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
+    // so we can get access to the pNext for the next struct.
+    struct CommonVulkanHeader {
+        VkStructureType sType;
+        void*           pNext;
+    };
+
+    void* pNext = features.pNext;
+    while (pNext) {
+        void* current = pNext;
+        pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
+        free(current);
+    }
+}
+
 #define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
 #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
 #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
@@ -66,6 +83,11 @@
     mDevice = VK_NULL_HANDLE;
     mPhysicalDevice = VK_NULL_HANDLE;
     mInstance = VK_NULL_HANDLE;
+    mInstanceVersion = 0u;
+    mInstanceExtensions.clear();
+    mDeviceExtensions.clear();
+    free_features_extensions_structs(mPhysicalDeviceFeatures2);
+    mPhysicalDeviceFeatures2 = {};
 }
 
 bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) {
@@ -81,7 +103,6 @@
         VK_MAKE_VERSION(1, 1, 0),           // apiVersion
     };
 
-    std::vector<const char*> instanceExtensions;
     {
         GET_PROC(EnumerateInstanceExtensionProperties);
 
@@ -99,7 +120,7 @@
         bool hasKHRSurfaceExtension = false;
         bool hasKHRAndroidSurfaceExtension = false;
         for (uint32_t i = 0; i < extensionCount; ++i) {
-            instanceExtensions.push_back(extensions[i].extensionName);
+            mInstanceExtensions.push_back(extensions[i].extensionName);
             if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
                 hasKHRSurfaceExtension = true;
             }
@@ -120,8 +141,8 @@
         &app_info,                                 // pApplicationInfo
         0,                                         // enabledLayerNameCount
         nullptr,                                   // ppEnabledLayerNames
-        (uint32_t) instanceExtensions.size(),      // enabledExtensionNameCount
-        instanceExtensions.data(),                 // ppEnabledExtensionNames
+        (uint32_t) mInstanceExtensions.size(),     // enabledExtensionNameCount
+        mInstanceExtensions.data(),                // ppEnabledExtensionNames
     };
 
     GET_PROC(CreateInstance);
@@ -201,7 +222,6 @@
     // presentation with any native window. So just use the first one.
     mPresentQueueIndex = 0;
 
-    std::vector<const char*> deviceExtensions;
     {
         uint32_t extensionCount = 0;
         err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
@@ -220,7 +240,7 @@
         }
         bool hasKHRSwapchainExtension = false;
         for (uint32_t i = 0; i < extensionCount; ++i) {
-            deviceExtensions.push_back(extensions[i].extensionName);
+            mDeviceExtensions.push_back(extensions[i].extensionName);
             if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
                 hasKHRSwapchainExtension = true;
             }
@@ -237,8 +257,8 @@
         }
         return vkGetInstanceProcAddr(instance, proc_name);
     };
-    grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(),
-            instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data());
+    grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(),
+            mInstanceExtensions.data(), mDeviceExtensions.size(), mDeviceExtensions.data());
 
     if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
         this->destroy();
@@ -308,8 +328,8 @@
         queueInfo,                               // pQueueCreateInfos
         0,                                       // layerCount
         nullptr,                                 // ppEnabledLayerNames
-        (uint32_t) deviceExtensions.size(),      // extensionCount
-        deviceExtensions.data(),                 // ppEnabledExtensionNames
+        (uint32_t) mDeviceExtensions.size(),     // extensionCount
+        mDeviceExtensions.data(),                // ppEnabledExtensionNames
         nullptr,                                 // ppEnabledFeatures
     };
 
@@ -351,36 +371,17 @@
     return true;
 }
 
-static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
-    // All Vulkan structs that could be part of the features chain will start with the
-    // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
-    // so we can get access to the pNext for the next struct.
-    struct CommonVulkanHeader {
-        VkStructureType sType;
-        void*           pNext;
-    };
-
-    void* pNext = features.pNext;
-    while (pNext) {
-        void* current = pNext;
-        pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
-        free(current);
-    }
-}
-
 void VulkanManager::initialize() {
     if (mDevice != VK_NULL_HANDLE) {
         return;
     }
 
     GET_PROC(EnumerateInstanceVersion);
-    uint32_t instanceVersion = 0;
-    LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
-    LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
+    LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&mInstanceVersion));
+    LOG_ALWAYS_FATAL_IF(mInstanceVersion < VK_MAKE_VERSION(1, 1, 0));
 
     GrVkExtensions extensions;
-    VkPhysicalDeviceFeatures2 features;
-    LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features));
+    LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, mPhysicalDeviceFeatures2));
 
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
 
@@ -397,9 +398,9 @@
     backendContext.fDevice = mDevice;
     backendContext.fQueue = mGraphicsQueue;
     backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
-    backendContext.fInstanceVersion = instanceVersion;
+    backendContext.fInstanceVersion = mInstanceVersion;
     backendContext.fVkExtensions = &extensions;
-    backendContext.fDeviceFeatures2 = &features;
+    backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
     backendContext.fGetProc = std::move(getProc);
 
     // create the command pool for the command buffers
@@ -433,13 +434,29 @@
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     mRenderThread.setGrContext(grContext);
 
-    free_features_extensions_structs(features);
-
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
     }
 }
 
+VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
+    return VkFunctorInitParams{
+            .instance = mInstance,
+            .physical_device = mPhysicalDevice,
+            .device = mDevice,
+            .queue = mGraphicsQueue,
+            .graphics_queue_index = mGraphicsQueueIndex,
+            .instance_version = mInstanceVersion,
+            .enabled_instance_extension_names = mInstanceExtensions.data(),
+            .enabled_instance_extension_names_length =
+                    static_cast<uint32_t>(mInstanceExtensions.size()),
+            .enabled_device_extension_names = mDeviceExtensions.data(),
+            .enabled_device_extension_names_length =
+                    static_cast<uint32_t>(mDeviceExtensions.size()),
+            .device_features_2 = &mPhysicalDeviceFeatures2,
+    };
+}
+
 // Returns the next BackbufferInfo to use for the next draw. The function will make sure all
 // previous uses have finished before returning.
 VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) {
@@ -463,6 +480,32 @@
     return backbuffer;
 }
 
+static SkMatrix getPreTransformMatrix(int width, int height,
+                                      VkSurfaceTransformFlagBitsKHR transform) {
+    switch (transform) {
+        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+            return SkMatrix::I();
+        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+            return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
+        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+            return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
+        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+            return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
+            return SkMatrix::MakeAll(-1, 0, width, 0, 1, 0, 0, 0, 1);
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
+            return SkMatrix::MakeAll(0, -1, height, -1, 0, width, 0, 0, 1);
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
+            return SkMatrix::MakeAll(1, 0, 0, 0, -1, height, 0, 0, 1);
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
+            return SkMatrix::MakeAll(0, 1, 0, 1, 0, 0, 0, 0, 1);
+        default:
+            LOG_ALWAYS_FATAL("Unsupported pre transform of swapchain.");
+    }
+    return SkMatrix::I();
+}
+
+
 SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) {
     // Recreate VulkanSurface, if ANativeWindow has been resized.
     VulkanSurface* surface = *surfaceOut;
@@ -499,7 +542,7 @@
         // maybe use attach somehow? but need a Window
         return nullptr;
     }
-    if (VK_ERROR_OUT_OF_DATE_KHR == res) {
+    if (VK_ERROR_OUT_OF_DATE_KHR == res || VK_SUBOPTIMAL_KHR == res) {
         // tear swapchain down and try again
         if (!createSwapchain(surface)) {
             return nullptr;
@@ -578,6 +621,10 @@
     }
     backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 
+    surface->mPreTransform = getPreTransformMatrix(surface->windowWidth(),
+                                                   surface->windowHeight(),
+                                                   surface->mTransform);
+
     surface->mBackbuffer = std::move(skSurface);
     return surface->mBackbuffer.get();
 }
@@ -732,6 +779,17 @@
         return false;
     }
 
+    if (!SkToBool(caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) {
+        return false;
+    }
+    VkSurfaceTransformFlagBitsKHR transform;
+    if (SkToBool(caps.supportedTransforms & caps.currentTransform) &&
+        !SkToBool(caps.currentTransform & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {
+        transform = caps.currentTransform;
+    } else {
+        transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+    }
+
     VkExtent2D extent = caps.currentExtent;
     // clamp width; to handle currentExtent of -1 and  protect us from broken hints
     if (extent.width < caps.minImageExtent.width) {
@@ -743,6 +801,16 @@
         extent.height = caps.minImageExtent.height;
     }
     SkASSERT(extent.height <= caps.maxImageExtent.height);
+
+    VkExtent2D swapExtent = extent;
+    if (transform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
+        transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR ||
+        transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR ||
+        transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) {
+        swapExtent.width = extent.height;
+        swapExtent.height = extent.width;
+    }
+
     surface->mWindowWidth = extent.width;
     surface->mWindowHeight = extent.height;
 
@@ -758,7 +826,7 @@
                                    VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
                                    VK_IMAGE_USAGE_TRANSFER_DST_BIT;
     SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
-    SkASSERT(caps.supportedTransforms & caps.currentTransform);
+
     SkASSERT(caps.supportedCompositeAlpha &
              (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
     VkCompositeAlphaFlagBitsKHR composite_alpha =
@@ -805,7 +873,7 @@
     swapchainCreateInfo.minImageCount = imageCount;
     swapchainCreateInfo.imageFormat = surfaceFormat;
     swapchainCreateInfo.imageColorSpace = colorSpace;
-    swapchainCreateInfo.imageExtent = extent;
+    swapchainCreateInfo.imageExtent = swapExtent;
     swapchainCreateInfo.imageArrayLayers = 1;
     swapchainCreateInfo.imageUsage = usageFlags;
 
@@ -820,7 +888,7 @@
         swapchainCreateInfo.pQueueFamilyIndices = nullptr;
     }
 
-    swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+    swapchainCreateInfo.preTransform = transform;
     swapchainCreateInfo.compositeAlpha = composite_alpha;
     swapchainCreateInfo.presentMode = mode;
     swapchainCreateInfo.clipped = true;
@@ -831,6 +899,8 @@
         return false;
     }
 
+    surface->mTransform = transform;
+
     // destroy the old swapchain
     if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
         mDeviceWaitIdle(mDevice);
@@ -840,7 +910,7 @@
         mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
     }
 
-    createBuffers(surface, surfaceFormat, extent);
+    createBuffers(surface, surfaceFormat, swapExtent);
 
     // The window content is not updated (frozen) until a buffer of the window size is received.
     // This prevents temporary stretching of the window after it is resized, but before the first
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 9eb942c..b06eb82 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -45,6 +45,14 @@
 
     sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
 
+    // The width and height are are the logical width and height for when submitting draws to the
+    // surface. In reality if the window is rotated the underlying VkImage may have the width and
+    // height swapped.
+    int windowWidth() const { return mWindowWidth; }
+    int windowHeight() const { return mWindowHeight; }
+
+    SkMatrix& preTransform() { return mPreTransform; }
+
 private:
     friend class VulkanManager;
     struct BackbufferInfo {
@@ -84,6 +92,8 @@
     sk_sp<SkColorSpace> mColorSpace;
     SkColorSpace::Gamut mColorGamut;
     SkColorType mColorType;
+    VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+    SkMatrix mPreTransform;
 };
 
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -132,6 +142,9 @@
     // Creates a fence that is signaled, when all the pending Vulkan commands are flushed.
     status_t createReleaseFence(sp<Fence>& nativeFence);
 
+    // Returned pointers are owned by VulkanManager.
+    VkFunctorInitParams getVkFunctorInitParams() const;
+
 private:
     friend class RenderThread;
 
@@ -234,6 +247,12 @@
 
     VkCommandBuffer mDummyCB = VK_NULL_HANDLE;
 
+    // Variables saved to populate VkFunctorInitParams.
+    uint32_t mInstanceVersion = 0u;
+    std::vector<const char*> mInstanceExtensions;
+    std::vector<const char*> mDeviceExtensions;
+    VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures2{};
+
     enum class SwapBehavior {
         Discard,
         BufferAge,
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 3d50d2d..8a16b20 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -48,7 +48,7 @@
 
 class FileDescriptor {
 public:
-    FileDescriptor(int fd) : mFd(fd) {}
+    explicit FileDescriptor(int fd) : mFd(fd) {}
     ~FileDescriptor() {
         if (mFd != -1) {
             close(mFd);
@@ -56,7 +56,7 @@
         }
     }
     bool valid() { return mFd != -1; }
-    operator int() { return mFd; }
+    operator int() { return mFd; } // NOLINT(google-explicit-constructor)
 
 private:
     int mFd;
@@ -64,7 +64,7 @@
 
 class FileOutputStreamLite : public io::ZeroCopyOutputStream {
 public:
-    FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
+    explicit FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
     virtual ~FileOutputStreamLite() {}
 
     int GetErrno() { return mCopyAdapter.mErrno; }
@@ -82,7 +82,7 @@
         int mFd;
         int mErrno = 0;
 
-        FDAdapter(int fd) : mFd(fd) {}
+        explicit FDAdapter(int fd) : mFd(fd) {}
         virtual ~FDAdapter() {}
 
         virtual bool Write(const void* buffer, int size) override {
@@ -139,6 +139,7 @@
     uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
     if (file_version != sCurrentFileVersion) {
         ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
+        munmap(addr, sb.st_size);
         return false;
     }
 
@@ -150,6 +151,7 @@
         ALOGW("Parse failed on '%s' error='%s'", path.c_str(),
               output->InitializationErrorString().c_str());
     }
+    munmap(addr, sb.st_size);
     return success;
 }
 
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 7aa9b82..16a27598 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -82,7 +82,7 @@
                                  float y) {
     auto utf16 = asciiToUtf16(text);
     uint32_t length = strlen(text);
-    SkPaint glyphPaint(paint);
+    Paint glyphPaint(paint);
     glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawText(utf16.get(), length,  // text buffer
                      0, length,            // draw range
@@ -93,7 +93,7 @@
 void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
                                  const SkPath& path) {
     auto utf16 = asciiToUtf16(text);
-    SkPaint glyphPaint(paint);
+    Paint glyphPaint(paint);
     glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
                            nullptr);
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 1d3d607..5af7d43 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -31,7 +31,7 @@
 
 class BitmapFillrate : public TestScene {
 public:
-    BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     void createContent(int width, int height, Canvas& canvas) override {
@@ -70,4 +70,4 @@
 
     BitmapAllocationTestUtils::BitmapAllocator mAllocator;
     std::vector<sp<RenderNode> > mNodes;
-};
\ No newline at end of file
+};
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index ad11a1d..5107660 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -26,7 +26,7 @@
 
 class BitmapShaders : public TestScene {
 public:
-    BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     sp<RenderNode> card;
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index a64e844..286f5f1 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -48,7 +48,7 @@
 
 class TvApp : public TestScene {
 public:
-    TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     sp<RenderNode> mBg;
@@ -232,7 +232,7 @@
 
 class TvAppNoRoundedCorner : public TvApp {
 public:
-    TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+    explicit TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
 
 private:
     virtual float roundedCornerRadius() override { return dp(0); }
@@ -240,7 +240,7 @@
 
 class TvAppColorFilter : public TvApp {
 public:
-    TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+    explicit TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
 
 private:
     virtual bool useOverlay() override { return false; }
@@ -248,7 +248,7 @@
 
 class TvAppNoRoundedCornerColorFilter : public TvApp {
 public:
-    TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TvApp(allocator) {}
 
 private:
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 524dfb0..174a140 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -19,6 +19,8 @@
 
 #include "Properties.h"
 #include "hwui/Typeface.h"
+#include "HardwareBitmapUploader.h"
+#include "renderthread/RenderProxy.h"
 
 #include <benchmark/benchmark.h>
 #include <getopt.h>
@@ -353,6 +355,9 @@
         gBenchmarkReporter->Finalize();
     }
 
+    renderthread::RenderProxy::trimMemory(100);
+    HardwareBitmapUploader::terminate();
+
     LeakChecker::checkForLeaks();
     return 0;
 }
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index f3a7648..f6178af 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -44,8 +44,8 @@
 }
 
 TEST(SkiaCanvas, colorSpaceXform) {
-    sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                      SkColorSpace::kAdobeRGB_Gamut);
+    sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
+                                                      SkNamedGamut::kAdobeRGB);
 
     SkImageInfo adobeInfo = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType, adobe);
     sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo);
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 3c06dab..e86cf42 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -51,7 +51,8 @@
     auto surface = SkSurface::MakeRasterN32Premul(1, 1);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
@@ -83,7 +84,8 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
 
     // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
@@ -104,10 +106,12 @@
     auto surface = SkSurface::MakeRasterN32Premul(2, 2);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
 }
@@ -126,7 +130,8 @@
     auto surface = SkSurface::MakeRasterN32Premul(2, 2);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
@@ -198,32 +203,38 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
 
     // Single draw, should be white.
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
 
     // 1 Overdraw, should be blue blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
 
     // 2 Overdraw, should be green blended onto white
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
 
     // 3 Overdraw, should be pink blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
 
     // 4 Overdraw, should be red blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
 
     // 5 Overdraw, should be red blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+            SkMatrix::I());
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
 }
 
@@ -308,7 +319,8 @@
     SkRect dirty = SkRect::MakeWH(800, 600);
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
     sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>());
-    pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface,
+            SkMatrix::I());
     EXPECT_EQ(4, surface->canvas()->mDrawCounter);
 }
 
@@ -339,7 +351,43 @@
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
     sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>());
     pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
-                          SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
+                          SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface, SkMatrix::I());
+    EXPECT_EQ(1, surface->canvas()->mDrawCounter);
+}
+
+// Test renderFrame with a dirty clip and a pre-transform matrix.
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
+    static const int CANVAS_WIDTH = 200;
+    static const int CANVAS_HEIGHT = 100;
+    static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1);
+    static const SkRect dirty = SkRect::MakeLTRB(10, 20, 20, 40);
+    class ClippedTestCanvas : public SkCanvas {
+    public:
+        ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+            EXPECT_EQ(0, mDrawCounter++);
+            // Expect clip to be rotated.
+            EXPECT_EQ(SkRect::MakeLTRB(CANVAS_HEIGHT - dirty.fTop - dirty.height(), dirty.fLeft,
+                    CANVAS_HEIGHT - dirty.fTop, dirty.fLeft + dirty.width()),
+                    TestUtils::getClipBounds(this));
+            EXPECT_EQ(rotateMatrix, getTotalMatrix());
+        }
+        int mDrawCounter = 0;
+    };
+
+    std::vector<sp<RenderNode>> nodes;
+    nodes.push_back(TestUtils::createSkiaNode(
+            0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+                sk_sp<Bitmap> bitmap(TestUtils::createBitmap(CANVAS_WIDTH, CANVAS_HEIGHT));
+                canvas.drawBitmap(*bitmap, 0, 0, nullptr);
+            }));
+
+    LayerUpdateQueue layerUpdateQueue;
+    auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+    sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>());
+    pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
+                          SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface, rotateMatrix);
     EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
 
@@ -369,7 +417,7 @@
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
     sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>());
     pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
-                          SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
+                          SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface, SkMatrix::I());
     EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
 
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index 479c462..635429d 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -44,7 +44,7 @@
     static const int CANVAS_HEIGHT = 100;
     class PropertyTestCanvas : public TestCanvasBase {
     public:
-        PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
+        explicit PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
                 : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT), mCallback(callback) {}
         void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
             EXPECT_EQ(mDrawCounter++, 0);
diff --git a/libs/hwui/tests/unit/ThreadBaseTests.cpp b/libs/hwui/tests/unit/ThreadBaseTests.cpp
index 1168ff2..817c1f3 100644
--- a/libs/hwui/tests/unit/ThreadBaseTests.cpp
+++ b/libs/hwui/tests/unit/ThreadBaseTests.cpp
@@ -95,7 +95,7 @@
     };
 
     struct Counter {
-        Counter(EventCount* count) : mCount(count) { mCount->construct++; }
+        explicit Counter(EventCount* count) : mCount(count) { mCount->construct++; }
 
         Counter(const Counter& other) : mCount(other.mCount) {
             if (mCount) mCount->copy++;
@@ -148,4 +148,4 @@
     ASSERT_EQ(1, dummyObject->getStrongCount());
     ASSERT_EQ(2, lifecycleTestHelper(dummyObject));
     ASSERT_EQ(1, dummyObject->getStrongCount());
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index f9de8a5..8cdcc46 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -27,7 +27,7 @@
 
 namespace android::uirenderer {
 
-class ThreadBase : protected Thread {
+class ThreadBase : public Thread {
     PREVENT_COPY_AND_ASSIGN(ThreadBase);
 
 public:
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index dc347f6..4415a59 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -25,38 +25,6 @@
 namespace android {
 namespace uirenderer {
 
-static inline bool almostEqual(float a, float b) {
-    return std::abs(a - b) < 1e-2f;
-}
-
-bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) {
-    if (colorSpace == nullptr) return true;
-    if (colorSpace->isSRGB()) return true;
-
-    SkColorSpaceTransferFn transferFunction;
-    if (colorSpace->isNumericalTransferFn(&transferFunction)) {
-        // sRGB transfer function params:
-        const float sRGBParamA = 1 / 1.055f;
-        const float sRGBParamB = 0.055f / 1.055f;
-        const float sRGBParamC = 1 / 12.92f;
-        const float sRGBParamD = 0.04045f;
-        const float sRGBParamE = 0.0f;
-        const float sRGBParamF = 0.0f;
-        const float sRGBParamG = 2.4f;
-
-        // This comparison will catch Display P3
-        return almostEqual(sRGBParamA, transferFunction.fA) &&
-               almostEqual(sRGBParamB, transferFunction.fB) &&
-               almostEqual(sRGBParamC, transferFunction.fC) &&
-               almostEqual(sRGBParamD, transferFunction.fD) &&
-               almostEqual(sRGBParamE, transferFunction.fE) &&
-               almostEqual(sRGBParamF, transferFunction.fF) &&
-               almostEqual(sRGBParamG, transferFunction.fG);
-    }
-
-    return false;
-}
-
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
     switch (colorType) {
         case kRGBA_8888_SkColorType:
@@ -79,19 +47,19 @@
 
 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
 
-    SkColorSpace::Gamut gamut;
+    skcms_Matrix3x3 gamut;
     switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
         case HAL_DATASPACE_STANDARD_BT709:
-            gamut = SkColorSpace::kSRGB_Gamut;
+            gamut = SkNamedGamut::kSRGB;
             break;
         case HAL_DATASPACE_STANDARD_BT2020:
-            gamut = SkColorSpace::kRec2020_Gamut;
+            gamut = SkNamedGamut::kRec2020;
             break;
         case HAL_DATASPACE_STANDARD_DCI_P3:
-            gamut = SkColorSpace::kDCIP3_D65_Gamut;
+            gamut = SkNamedGamut::kDCIP3;
             break;
         case HAL_DATASPACE_STANDARD_ADOBE_RGB:
-            gamut = SkColorSpace::kAdobeRGB_Gamut;
+            gamut = SkNamedGamut::kAdobeRGB;
             break;
         case HAL_DATASPACE_STANDARD_UNSPECIFIED:
             return nullptr;
@@ -109,9 +77,9 @@
 
     switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
         case HAL_DATASPACE_TRANSFER_LINEAR:
-            return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
         case HAL_DATASPACE_TRANSFER_SRGB:
-            return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
         case HAL_DATASPACE_TRANSFER_GAMMA2_2:
             return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
         case HAL_DATASPACE_TRANSFER_GAMMA2_6:
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 4473ce6..3880252 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -111,11 +111,6 @@
 #endif
 }
 
-// Returns whether the specified color space's transfer function can be
-// approximated with the native sRGB transfer function. This method
-// returns true for sRGB, gamma 2.2 and Display P3 for instance
-bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
-
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
 
 ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index b401fcf..9c4a1be 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -168,7 +168,7 @@
     };
     // enable allocators to be constructed from other templated types
     template <class U>
-    LinearStdAllocator(const LinearStdAllocator<U>& other)  // NOLINT(implicit)
+    LinearStdAllocator(const LinearStdAllocator<U>& other)  // NOLINT(google-explicit-constructor)
             : linearAllocator(other.linearAllocator) {}
 
     T* allocate(size_t num, const void* = 0) {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0a90f85..d742cc3 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -39,7 +39,7 @@
     virtual ~WeakLooperCallback() { }
 
 public:
-    WeakLooperCallback(const wp<LooperCallback>& callback) :
+    explicit WeakLooperCallback(const wp<LooperCallback>& callback) :
         mCallback(callback) {
     }
 
@@ -89,10 +89,6 @@
 
     mLocked.animationPending = false;
 
-    mLocked.displayWidth = -1;
-    mLocked.displayHeight = -1;
-    mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
-
     mLocked.presentation = PRESENTATION_POINTER;
     mLocked.presentationChanged = false;
 
@@ -110,15 +106,6 @@
     mLocked.lastFrameUpdatedTime = 0;
 
     mLocked.buttonState = 0;
-
-    mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-
-    loadResources();
-
-    if (mLocked.pointerIcon.isValid()) {
-        mLocked.pointerIconChanged = true;
-        updatePointerLocked();
-    }
 }
 
 PointerController::~PointerController() {
@@ -144,23 +131,15 @@
 
 bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
         float* outMaxX, float* outMaxY) const {
-    if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
+
+    if (!mLocked.viewport.isValid()) {
         return false;
     }
 
-    *outMinX = 0;
-    *outMinY = 0;
-    switch (mLocked.displayOrientation) {
-    case DISPLAY_ORIENTATION_90:
-    case DISPLAY_ORIENTATION_270:
-        *outMaxX = mLocked.displayHeight - 1;
-        *outMaxY = mLocked.displayWidth - 1;
-        break;
-    default:
-        *outMaxX = mLocked.displayWidth - 1;
-        *outMaxY = mLocked.displayHeight - 1;
-        break;
-    }
+    *outMinX = mLocked.viewport.logicalLeft;
+    *outMinY = mLocked.viewport.logicalTop;
+    *outMaxX = mLocked.viewport.logicalRight - 1;
+    *outMaxY = mLocked.viewport.logicalBottom - 1;
     return true;
 }
 
@@ -231,6 +210,12 @@
     *outY = mLocked.pointerY;
 }
 
+int32_t PointerController::getDisplayId() const {
+    AutoMutex _l(mLock);
+
+    return mLocked.viewport.displayId;
+}
+
 void PointerController::fade(Transition transition) {
     AutoMutex _l(mLock);
 
@@ -270,7 +255,7 @@
 
     if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
         mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                                              &mLocked.animationResources);
+                &mLocked.animationResources, mLocked.viewport.displayId);
     }
 
     if (mLocked.presentation != presentation) {
@@ -355,48 +340,56 @@
 void PointerController::reloadPointerResources() {
     AutoMutex _l(mLock);
 
-    loadResources();
-
-    if (mLocked.presentation == PRESENTATION_POINTER) {
-        mLocked.additionalMouseResources.clear();
-        mLocked.animationResources.clear();
-        mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                                              &mLocked.animationResources);
-    }
-
-    mLocked.presentationChanged = true;
+    loadResourcesLocked();
     updatePointerLocked();
 }
 
-void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
-    AutoMutex _l(mLock);
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+    width = viewport.deviceWidth;
+    height = viewport.deviceHeight;
 
-    // Adjust to use the display's unrotated coordinate frame.
-    if (orientation == DISPLAY_ORIENTATION_90
-            || orientation == DISPLAY_ORIENTATION_270) {
-        int32_t temp = height;
-        height = width;
-        width = temp;
+    if (viewport.orientation == DISPLAY_ORIENTATION_90
+            || viewport.orientation == DISPLAY_ORIENTATION_270) {
+        std::swap(width, height);
+    }
+}
+
+void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+    AutoMutex _l(mLock);
+    if (viewport == mLocked.viewport) {
+        return;
     }
 
-    if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
-        mLocked.displayWidth = width;
-        mLocked.displayHeight = height;
+    const DisplayViewport oldViewport = mLocked.viewport;
+    mLocked.viewport = viewport;
+
+    int32_t oldDisplayWidth, oldDisplayHeight;
+    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+    int32_t newDisplayWidth, newDisplayHeight;
+    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+    // Reset cursor position to center if size or display changed.
+    if (oldViewport.displayId != viewport.displayId
+            || oldDisplayWidth != newDisplayWidth
+            || oldDisplayHeight != newDisplayHeight) {
 
         float minX, minY, maxX, maxY;
         if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
             mLocked.pointerX = (minX + maxX) * 0.5f;
             mLocked.pointerY = (minY + maxY) * 0.5f;
+            // Reload icon resources for density may be changed.
+            loadResourcesLocked();
         } else {
             mLocked.pointerX = 0;
             mLocked.pointerY = 0;
         }
 
         fadeOutAndReleaseAllSpotsLocked();
-    }
-
-    if (mLocked.displayOrientation != orientation) {
+    } else if (oldViewport.orientation != viewport.orientation) {
         // Apply offsets to convert from the pixel top-left corner position to the pixel center.
         // This creates an invariant frame of reference that we can easily rotate when
         // taking into account that the pointer may be located at fractional pixel offsets.
@@ -405,37 +398,37 @@
         float temp;
 
         // Undo the previous rotation.
-        switch (mLocked.displayOrientation) {
+        switch (oldViewport.orientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
-            x = mLocked.displayWidth - y;
+            x =  oldViewport.deviceHeight - y;
             y = temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = mLocked.displayWidth - x;
-            y = mLocked.displayHeight - y;
+            x = oldViewport.deviceWidth - x;
+            y = oldViewport.deviceHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
             x = y;
-            y = mLocked.displayHeight - temp;
+            y = oldViewport.deviceWidth - temp;
             break;
         }
 
         // Perform the new rotation.
-        switch (orientation) {
+        switch (viewport.orientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
             x = y;
-            y = mLocked.displayWidth - temp;
+            y = viewport.deviceHeight - temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = mLocked.displayWidth - x;
-            y = mLocked.displayHeight - y;
+            x = viewport.deviceWidth - x;
+            y = viewport.deviceHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
-            x = mLocked.displayHeight - y;
+            x = viewport.deviceWidth - y;
             y = temp;
             break;
         }
@@ -444,7 +437,6 @@
         // and save the results.
         mLocked.pointerX = x - 0.5f;
         mLocked.pointerY = y - 0.5f;
-        mLocked.displayOrientation = orientation;
     }
 
     updatePointerLocked();
@@ -614,11 +606,16 @@
     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
 }
 
-void PointerController::updatePointerLocked() {
+void PointerController::updatePointerLocked() REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
     mSpriteController->openTransaction();
 
     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
     mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
 
     if (mLocked.pointerAlpha > 0) {
         mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -729,8 +726,18 @@
     }
 }
 
-void PointerController::loadResources() {
-    mPolicy->loadPointerResources(&mResources);
+void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+    mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
+
+    if (mLocked.presentation == PRESENTATION_POINTER) {
+        mLocked.additionalMouseResources.clear();
+        mLocked.animationResources.clear();
+        mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
+        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                &mLocked.animationResources, mLocked.viewport.displayId);
+    }
+
+    mLocked.pointerIconChanged = true;
 }
 
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 7f4e5a5..be05786 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include <ui/DisplayInfo.h>
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <PointerControllerInterface.h>
 #include <utils/BitSet.h>
@@ -61,10 +62,10 @@
     virtual ~PointerControllerPolicyInterface() { }
 
 public:
-    virtual void loadPointerIcon(SpriteIcon* icon) = 0;
-    virtual void loadPointerResources(PointerResources* outResources) = 0;
+    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0;
+    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0;
     virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
-            std::map<int32_t, PointerAnimation>* outAnimationResources) = 0;
+            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
     virtual int32_t getDefaultPointerIconId() = 0;
     virtual int32_t getCustomPointerIconId() = 0;
 };
@@ -96,6 +97,7 @@
     virtual int32_t getButtonState() const;
     virtual void setPosition(float x, float y);
     virtual void getPosition(float* outX, float* outY) const;
+    virtual int32_t getDisplayId() const;
     virtual void fade(Transition transition);
     virtual void unfade(Transition transition);
 
@@ -106,7 +108,7 @@
 
     void updatePointerIcon(int32_t iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
-    void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
+    void setDisplayViewport(const DisplayViewport& viewport);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void reloadPointerResources();
 
@@ -156,9 +158,7 @@
         size_t animationFrameIndex;
         nsecs_t lastFrameUpdatedTime;
 
-        int32_t displayWidth;
-        int32_t displayHeight;
-        int32_t displayOrientation;
+        DisplayViewport viewport;
 
         InactivityTimeout inactivityTimeout;
 
@@ -182,7 +182,7 @@
 
         Vector<Spot*> spots;
         Vector<sp<Sprite> > recycledSprites;
-    } mLocked;
+    } mLocked GUARDED_BY(mLock);
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
     void fadeOutAndReleaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseAllSpotsLocked();
 
-    void loadResources();
+    void loadResourcesLocked();
 };
 
 } // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index eb2bc98..c1868d3 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,13 +144,16 @@
         }
     }
 
-    // Resize sprites if needed.
+    // Resize and/or reparent sprites if needed.
     SurfaceComposerClient::Transaction t;
     bool needApplyTransaction = false;
     for (size_t i = 0; i < numSprites; i++) {
         SpriteUpdate& update = updates.editItemAt(i);
+        if (update.state.surfaceControl == nullptr) {
+            continue;
+        }
 
-        if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+        if (update.state.wantSurfaceVisible()) {
             int32_t desiredWidth = update.state.icon.bitmap.width();
             int32_t desiredHeight = update.state.icon.bitmap.height();
             if (update.state.surfaceWidth < desiredWidth
@@ -170,6 +173,12 @@
                 }
             }
         }
+
+        // If surface is a new one, we have to set right layer stack.
+        if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
+            t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+            needApplyTransaction = true;
+        }
     }
     if (needApplyTransaction) {
         t.apply();
@@ -236,7 +245,7 @@
         if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
-                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -445,6 +454,15 @@
     }
 }
 
+void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
+    AutoMutex _l(mController->mLock);
+
+    if (mLocked.state.displayId != displayId) {
+        mLocked.state.displayId = displayId;
+        invalidateLocked(DIRTY_DISPLAY_ID);
+    }
+}
+
 void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
     bool wasDirty = mLocked.state.dirty;
     mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 31e43e9..5b216f5 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,6 +125,9 @@
 
     /* Sets the sprite transformation matrix. */
     virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+    /* Sets the id of the display where the sprite should be shown. */
+    virtual void setDisplayId(int32_t displayId) = 0;
 };
 
 /*
@@ -170,6 +173,7 @@
         DIRTY_LAYER = 1 << 4,
         DIRTY_VISIBILITY = 1 << 5,
         DIRTY_HOTSPOT = 1 << 6,
+        DIRTY_DISPLAY_ID = 1 << 7,
     };
 
     /* Describes the state of a sprite.
@@ -180,7 +184,7 @@
     struct SpriteState {
         inline SpriteState() :
                 dirty(0), visible(false),
-                positionX(0), positionY(0), layer(0), alpha(1.0f),
+                positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
                 surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
         }
 
@@ -193,6 +197,7 @@
         int32_t layer;
         float alpha;
         SpriteTransformationMatrix transformationMatrix;
+        int32_t displayId;
 
         sp<SurfaceControl> surfaceControl;
         int32_t surfaceWidth;
@@ -225,6 +230,7 @@
         virtual void setLayer(int32_t layer);
         virtual void setAlpha(float alpha);
         virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+        virtual void setDisplayId(int32_t displayId);
 
         inline const SpriteState& getStateLocked() const {
             return mLocked.state;
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
index c84de4c..0b7f6e46 100644
--- a/libs/protoutil/include/android/util/EncodedBuffer.h
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -38,13 +38,13 @@
 {
 public:
     EncodedBuffer();
-    EncodedBuffer(size_t chunkSize);
+    explicit EncodedBuffer(size_t chunkSize);
     ~EncodedBuffer();
 
     class Pointer {
     public:
         Pointer();
-        Pointer(size_t chunkSize);
+        explicit Pointer(size_t chunkSize);
 
         size_t pos() const;
         size_t index() const;
@@ -161,7 +161,7 @@
     friend class iterator;
     class iterator {
     public:
-        iterator(const EncodedBuffer& buffer);
+        explicit iterator(const EncodedBuffer& buffer);
 
         /**
          * Returns the number of bytes written in the buffer
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
index 75b26c6..0747243 100644
--- a/libs/services/include/android/os/DropBoxManager.h
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -62,7 +62,7 @@
     // file descriptor.
     Status addFile(const String16& tag, int fd, int flags);
 
-    class Entry : public virtual RefBase, public Parcelable {
+    class Entry : public Parcelable {
     public:
         Entry();
         virtual ~Entry();
@@ -89,9 +89,6 @@
         friend class DropBoxManager;
     };
 
-    // Get the next entry from the drop box after the specified time.
-    Status getNextEntry(const String16& tag, long msec, Entry* entry);
-
 private:
     enum {
         HAS_BYTE_ARRAY = 8
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index 8282518..681d5f7 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -228,15 +228,4 @@
     return service->add(entry);
 }
 
-Status
-DropBoxManager::getNextEntry(const String16& tag, long msec, Entry* entry)
-{
-    sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>(
-        defaultServiceManager()->getService(android::String16("dropbox")));
-    if (service == NULL) {
-        return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
-    }
-    return service->getNextEntry(tag, msec, android::String16("android"), entry);
-}
-
 }} // namespace android::os
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index f179bc3..602cc3e 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -48,6 +48,7 @@
     private int mMultipathIndicator;
     private double mSnrInDb;
     private double mAutomaticGainControlLevelInDb;
+    private int mCodeType;
 
     // The following enumerations must be in sync with the values declared in gps.h
 
@@ -58,6 +59,7 @@
     private static final int HAS_CARRIER_PHASE = (1<<11);
     private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
     private static final int HAS_AUTOMATIC_GAIN_CONTROL = (1<<13);
+    private static final int HAS_CODE_TYPE = (1 << 14);
 
     /**
      * The status of the multipath indicator.
@@ -202,6 +204,104 @@
     public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
 
     /**
+     * GNSS measurement code type.
+     * @hide
+     */
+    @IntDef(prefix = { "CODE_TYPE_" }, value = {
+            CODE_TYPE_UNKNOWN, CODE_TYPE_A, CODE_TYPE_B, CODE_TYPE_C, CODE_TYPE_I, CODE_TYPE_L,
+            CODE_TYPE_M, CODE_TYPE_P, CODE_TYPE_Q, CODE_TYPE_S, CODE_TYPE_W, CODE_TYPE_X,
+            CODE_TYPE_Y, CODE_TYPE_Z, CODE_TYPE_CODELESS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CodeType {}
+
+    /** The GNSS Measurement's code type is unknown. */
+    public static final int CODE_TYPE_UNKNOWN = -1;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GALILEO E1A, GALILEO E6A, IRNSS
+     * L5A, IRNSS SA.
+     */
+    public static final int CODE_TYPE_A = 0;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GALILEO E1B, GALILEO E6B, IRNSS
+     * L5B, IRNSS SB.
+     */
+    public static final int CODE_TYPE_B = 1;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1 C/A,  GPS L2 C/A, GLONASS G1
+     * C/A, GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C.
+     */
+    public static final int CODE_TYPE_C = 2;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L5 I, GLONASS G3 I, GALILEO E5a
+     * I, GALILEO E5b I, GALILEO E5a+b I, SBAS L5 I, QZSS L5 I, BDS B1 I, BDS B2 I, BDS B3 I.
+     */
+    public static final int CODE_TYPE_I = 3;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1C (P), GPS L2C (L), QZSS L1C
+     * (P), QZSS L2C (L), LEX(6) L.
+     */
+    public static final int CODE_TYPE_L = 4;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1M, GPS L2M.
+     */
+    public static final int CODE_TYPE_M = 5;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1P, GPS L2P, GLONASS G1P,
+     * GLONASS G2P.
+     */
+    public static final int CODE_TYPE_P = 6;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L5 Q, GLONASS G3 Q, GALILEO E5a
+     * Q, GALILEO E5b Q, GALILEO E5a+b Q, SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
+     */
+    public static final int CODE_TYPE_Q = 7;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1C (D), GPS L2C (M), QZSS L1C
+     * (D), QZSS L2C (M), LEX(6) S.
+     */
+    public static final int CODE_TYPE_S = 8;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1 Z-tracking, GPS L2
+     * Z-tracking.
+     */
+    public static final int CODE_TYPE_W = 9;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1C (D+P), GPS L2C (M+L), GPS
+     * L5 (I+Q), GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO
+     * E5a+b(I+Q), GALILEO E6 (B+C), SBAS L5 (I+Q), QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q),
+     * LEX(6) (S+L), BDS B1 (I+Q), BDS B2 (I+Q), BDS B3 (I+Q), IRNSS L5 (B+C).
+     */
+    public static final int CODE_TYPE_X = 10;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1Y, GPS L2Y.
+     */
+    public static final int CODE_TYPE_Y = 11;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GALILEO E1 (A+B+C), GALILEO E6
+     * (A+B+C), QZSS L1-SAIF.
+     */
+    public static final int CODE_TYPE_Z = 12;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1 codeless, GPS L2 codeless.
+     */
+    public static final int CODE_TYPE_CODELESS = 13;
+
+    /**
      * All the 'Accumulated Delta Range' flags.
      * @hide
      */
@@ -248,6 +348,7 @@
         mMultipathIndicator = measurement.mMultipathIndicator;
         mSnrInDb = measurement.mSnrInDb;
         mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb;
+        mCodeType = measurement.mCodeType;
     }
 
     /**
@@ -967,7 +1068,7 @@
      * <p>For internal and logging use only.
      */
     private String getMultipathIndicatorString() {
-        switch(mMultipathIndicator) {
+        switch (mMultipathIndicator) {
             case MULTIPATH_INDICATOR_UNKNOWN:
                 return "Unknown";
             case MULTIPATH_INDICATOR_DETECTED:
@@ -1063,6 +1164,89 @@
         mAutomaticGainControlLevelInDb = Double.NaN;
     }
 
+    /**
+     * Returns {@code true} if {@link #getCodeType()} is available,
+     * {@code false} otherwise.
+     */
+    public boolean hasCodeType() {
+        return isFlagSet(HAS_CODE_TYPE);
+    }
+
+    /**
+     * Gets the GNSS measurement's code type.
+     *
+     * <p>Similar to the Attribute field described in Rinex 3.03, e.g., in Tables 4-10, and Table
+     * A2 at the Rinex 3.03 Update 1 Document.
+     */
+    @CodeType
+    public int getCodeType() {
+        return mCodeType;
+    }
+
+    /**
+     * Sets the GNSS measurement's code type.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setCodeType(@CodeType int codeType) {
+        setFlag(HAS_CODE_TYPE);
+        mCodeType = codeType;
+    }
+
+    /**
+     * Resets the GNSS measurement's code type.
+     *
+     * @hide
+     */
+    @TestApi
+    public void resetCodeType() {
+        resetFlag(HAS_CODE_TYPE);
+        mCodeType = CODE_TYPE_UNKNOWN;
+    }
+
+    /**
+     * Gets a string representation of the 'code type'.
+     *
+     * <p>For internal and logging use only.
+     */
+    private String getCodeTypeString() {
+        switch (mCodeType) {
+            case CODE_TYPE_UNKNOWN:
+                return "CODE_TYPE_UNKNOWN";
+            case CODE_TYPE_A:
+                return "CODE_TYPE_A";
+            case CODE_TYPE_B:
+                return "CODE_TYPE_B";
+            case CODE_TYPE_C:
+                return "CODE_TYPE_C";
+            case CODE_TYPE_I:
+                return "CODE_TYPE_I";
+            case CODE_TYPE_L:
+                return "CODE_TYPE_L";
+            case CODE_TYPE_M:
+                return "CODE_TYPE_M";
+            case CODE_TYPE_P:
+                return "CODE_TYPE_P";
+            case CODE_TYPE_Q:
+                return "CODE_TYPE_Q";
+            case CODE_TYPE_S:
+                return "CODE_TYPE_S";
+            case CODE_TYPE_W:
+                return "CODE_TYPE_W";
+            case CODE_TYPE_X:
+                return "CODE_TYPE_X";
+            case CODE_TYPE_Y:
+                return "CODE_TYPE_Y";
+            case CODE_TYPE_Z:
+                return "CODE_TYPE_Z";
+            case CODE_TYPE_CODELESS:
+                return "CODE_TYPE_CODELESS";
+            default:
+                return "<Invalid: " + mCodeType + ">";
+        }
+    }
+
     public static final Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
         @Override
         public GnssMeasurement createFromParcel(Parcel parcel) {
@@ -1088,6 +1272,7 @@
             gnssMeasurement.mMultipathIndicator = parcel.readInt();
             gnssMeasurement.mSnrInDb = parcel.readDouble();
             gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
+            gnssMeasurement.mCodeType = parcel.readInt();
 
             return gnssMeasurement;
         }
@@ -1120,6 +1305,7 @@
         parcel.writeInt(mMultipathIndicator);
         parcel.writeDouble(mSnrInDb);
         parcel.writeDouble(mAutomaticGainControlLevelInDb);
+        parcel.writeInt(mCodeType);
     }
 
     @Override
@@ -1191,9 +1377,13 @@
                 "SnrInDb",
                 hasSnrInDb() ? mSnrInDb : null));
         builder.append(String.format(
-            format,
-            "AgcLevelDb",
-            hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
+                format,
+                "AgcLevelDb",
+                hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
+        builder.append(String.format(
+                format,
+                "CodeType",
+                hasCodeType() ? getCodeTypeString() : null));
 
         return builder.toString();
     }
@@ -1218,6 +1408,7 @@
         setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
         resetSnrInDb();
         resetAutomaticGainControlLevel();
+        resetCodeType();
     }
 
     private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java
index 6c757f9..3922d2f 100644
--- a/location/java/android/location/GnssSingleSatCorrection.java
+++ b/location/java/android/location/GnssSingleSatCorrection.java
@@ -16,11 +16,14 @@
 
 package android.location;
 
+import android.annotation.FloatRange;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * A container with measurement corrections for a single visible satellite
  *
@@ -31,9 +34,9 @@
 
     /**
      * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
-     * #mSatIsLos}.
+     * #mProbSatIsLos}.
      */
-    public static final int HAS_SAT_IS_LOS_MASK = 1 << 0;
+    public static final int HAS_PROB_SAT_IS_LOS_MASK = 1 << 0;
 
     /**
      * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
@@ -78,9 +81,11 @@
     private float mCarrierFrequencyHz;
 
     /**
-     * True if the satellite is estimated to be in Line-of-Sight condition at the given location.
+     * The probability that the satellite is estimated to be in Line-of-Sight condition at the given
+     * location.
      */
-    private boolean mSatIsLos;
+    @FloatRange(from = 0f, to = 1f)
+    private float mProbSatIsLos;
 
     /**
      * Excess path length to be subtracted from pseudorange before using it in calculating location.
@@ -103,7 +108,7 @@
         mSatId = builder.mSatId;
         mConstellationType = builder.mConstellationType;
         mCarrierFrequencyHz = builder.mCarrierFrequencyHz;
-        mSatIsLos = builder.mSatIsLos;
+        mProbSatIsLos = builder.mProbSatIsLos;
         mExcessPathLengthMeters = builder.mExcessPathLengthMeters;
         mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters;
         mReflectingPlane = builder.mReflectingPlane;
@@ -152,9 +157,13 @@
         return mCarrierFrequencyHz;
     }
 
-    /** True if the satellite is line-of-sight */
-    public boolean isSatelliteLineOfSight() {
-        return mSatIsLos;
+    /**
+     * Returns the probability that the satellite is in line-of-sight condition at the given
+     * location.
+     */
+    @FloatRange(from = 0f, to = 1f)
+    public float getProbSatIsLos() {
+        return mProbSatIsLos;
     }
 
     /**
@@ -180,9 +189,9 @@
         return mReflectingPlane;
     }
 
-    /** Returns {@code true} if {@link #isSatelliteLineOfSight()} is valid. */
+    /** Returns {@code true} if {@link #getProbSatIsLos()} is valid. */
     public boolean hasSatelliteLineOfSight() {
-        return (mSingleSatCorrectionFlags & HAS_SAT_IS_LOS_MASK) != 0;
+        return (mSingleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0;
     }
 
     /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
@@ -215,7 +224,7 @@
                                     .setConstellationType(parcel.readInt())
                                     .setSatId(parcel.readInt())
                                     .setCarrierFrequencyHz(parcel.readFloat())
-                                    .setSatIsLos(parcel.readBoolean())
+                                    .setProbSatIsLos(parcel.readFloat())
                                     .setExcessPathLengthMeters(parcel.readFloat())
                                     .setExcessPathLengthUncertaintyMeters(parcel.readFloat())
                                     .setReflectingPlane(
@@ -239,7 +248,7 @@
         builder.append(String.format(format, "ConstellationType = ", mConstellationType));
         builder.append(String.format(format, "SatId = ", mSatId));
         builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz));
-        builder.append(String.format(format, "SatIsLos = ", mSatIsLos));
+        builder.append(String.format(format, "ProbSatIsLos = ", mProbSatIsLos));
         builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters));
         builder.append(
                 String.format(
@@ -256,7 +265,7 @@
         parcel.writeInt(mConstellationType);
         parcel.writeInt(mSatId);
         parcel.writeFloat(mCarrierFrequencyHz);
-        parcel.writeBoolean(mSatIsLos);
+        parcel.writeFloat(mProbSatIsLos);
         parcel.writeFloat(mExcessPathLengthMeters);
         parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
         mReflectingPlane.writeToParcel(parcel, flags);
@@ -274,7 +283,7 @@
         private int mConstellationType;
         private int mSatId;
         private float mCarrierFrequencyHz;
-        private boolean mSatIsLos;
+        private float mProbSatIsLos;
         private float mExcessPathLengthMeters;
         private float mExcessPathLengthUncertaintyMeters;
         private GnssReflectingPlane mReflectingPlane;
@@ -303,10 +312,16 @@
             return this;
         }
 
-        /** Sets the line=of-sight state of the satellite */
-        public Builder setSatIsLos(boolean satIsLos) {
-            mSatIsLos = satIsLos;
-            mSingleSatCorrectionFlags = (byte) (mSingleSatCorrectionFlags | HAS_SAT_IS_LOS_MASK);
+        /**
+         * Sets the line-of-sight probability of the satellite at the given location in the range
+         * between 0 and 1.
+         */
+        public Builder setProbSatIsLos(@FloatRange(from = 0f, to = 1f) float probSatIsLos) {
+            Preconditions.checkArgumentInRange(probSatIsLos, 0, 1,
+                    "probSatIsLos should be between 0 and 1.");
+            mProbSatIsLos = probSatIsLos;
+            mSingleSatCorrectionFlags =
+                    (byte) (mSingleSatCorrectionFlags | HAS_PROB_SAT_IS_LOS_MASK);
             return this;
         }
 
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index bdc84da..e795b81 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -89,7 +89,6 @@
     List<String> getAllProviders();
     List<String> getProviders(in Criteria criteria, boolean enabledOnly);
     String getBestProvider(in Criteria criteria, boolean enabledOnly);
-    boolean providerMeetsCriteria(String provider, in Criteria criteria);
     ProviderProperties getProviderProperties(String provider);
     String getNetworkProviderPackage();
     void setLocationControllerExtraPackage(String packageName);
@@ -98,9 +97,7 @@
     boolean isLocationControllerExtraPackageEnabled();
 
     boolean isProviderEnabledForUser(String provider, int userId);
-    boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
     boolean isLocationEnabledForUser(int userId);
-    void setLocationEnabledForUser(boolean enabled, int userId);
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
     void setTestProviderLocation(String provider, in Location loc, String opPackageName);
@@ -114,10 +111,6 @@
 
     // --- internal ---
 
-    // --- deprecated ---
-    void reportLocation(in Location location, boolean passive);
-    void reportLocationBatch(in List<Location> locations);
-
     // for reporting callback completion
     void locationCallbackFinished(ILocationListener listener);
 
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d96597b..59c6a0a 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -43,6 +43,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 
 import com.android.internal.location.ProviderProperties;
@@ -1282,11 +1283,13 @@
     @SystemApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
-        try {
-            mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.LOCATION_MODE,
+                enabled
+                        ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
+                        : Settings.Secure.LOCATION_MODE_OFF,
+                userHandle.getIdentifier());
     }
 
     /**
@@ -1372,20 +1375,22 @@
      * @return true if the value was set, false on database errors
      *
      * @throws IllegalArgumentException if provider is null
+     * @deprecated Do not manipulate providers individually, use
+     * {@link #setLocationEnabledForUser(boolean, UserHandle)} instead.
      * @hide
      */
+    @Deprecated
     @SystemApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public boolean setProviderEnabledForUser(
             String provider, boolean enabled, UserHandle userHandle) {
         checkProvider(provider);
 
-        try {
-            return mService.setProviderEnabledForUser(
-                    provider, enabled, userHandle.getIdentifier());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return Settings.Secure.setLocationProviderEnabledForUser(
+                mContext.getContentResolver(),
+                provider,
+                enabled,
+                userHandle.getIdentifier());
     }
 
     /**
@@ -1578,7 +1583,7 @@
      */
     @Deprecated
     public void clearTestProviderEnabled(String provider) {
-        setTestProviderEnabled(provider, true);
+        setTestProviderEnabled(provider, false);
     }
 
     /**
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index b531325..866634e 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -16,9 +16,6 @@
 
 package com.android.internal.location;
 
-import java.io.UnsupportedEncodingException;
-import java.util.concurrent.TimeUnit;
-
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -26,20 +23,23 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.location.LocationManager;
 import android.location.INetInitiatedListener;
+import android.location.LocationManager;
+import android.os.RemoteException;
 import android.os.SystemClock;
-import android.telephony.TelephonyManager;
+import android.os.UserHandle;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
-import android.os.RemoteException;
-import android.os.UserHandle;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
-import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.telephony.GsmAlphabet;
 
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
+
 /**
  * A GPS Network-initiated Handler class used by LocationManager.
  *
@@ -92,9 +92,6 @@
     public static final int GPS_ENC_SUPL_UCS2 = 3;
     public static final int GPS_ENC_UNKNOWN = -1;
 
-    // Limit on SUPL NI emergency mode time extension after emergency sessions ends
-    private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300;  // 5 minute maximum
-
     private final Context mContext;
     private final TelephonyManager mTelephonyManager;
     private final PhoneStateListener mPhoneStateListener;
@@ -252,19 +249,9 @@
     }
 
     public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
-        if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
-            Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
-                    + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
-            emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
-        } else if (emergencyExtensionSeconds < 0) {
-            Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
-                    + " is negative, reset to zero.");
-            emergencyExtensionSeconds = 0;
-        }
         mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
     }
 
-
     // Handles NI events from HAL
     public void handleNiNotification(GpsNiNotification notif) {
         if (DEBUG) Log.d(TAG, "in handleNiNotification () :"
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index b09335c..35f2877 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -18,5 +18,7 @@
     name: "com.android.location.provider",
     srcs: ["java/**/*.java"],
     api_packages: ["com.android.location.provider"],
-    metalava_enabled: false,
+    srcs_lib: "framework",
+    srcs_lib_whitelist_dirs: ["location/java"],
+    srcs_lib_whitelist_pkgs: ["com.android.internal.location"],
 }
diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
index c18d58f..d6227bb 100644
--- a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
+++ b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
@@ -59,7 +59,7 @@
         assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType());
         assertEquals(11, singleSatCorrection.getSatId());
         assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz());
-        assertEquals(false, singleSatCorrection.isSatelliteLineOfSight());
+        assertEquals(0.9f, singleSatCorrection.getProbSatIsLos());
         assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters());
         assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
         GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
@@ -88,7 +88,7 @@
                 .setConstellationType(GnssStatus.CONSTELLATION_GPS)
                 .setSatId(11)
                 .setCarrierFrequencyHz(1575430000f)
-                .setSatIsLos(false)
+                .setProbSatIsLos(0.9f)
                 .setExcessPathLengthMeters(50.0f)
                 .setExcessPathLengthUncertaintyMeters(55.0f)
                 .setReflectingPlane(generateTestReflectingPlane());
diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
index 2e54ae4..f358806 100644
--- a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
+++ b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
@@ -44,7 +44,7 @@
         assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType());
         assertEquals(12, singleSatCorrection.getSatId());
         assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz());
-        assertEquals(true, singleSatCorrection.isSatelliteLineOfSight());
+        assertEquals(0.1f, singleSatCorrection.getProbSatIsLos());
         assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters());
         assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
         GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
@@ -58,7 +58,7 @@
                 .setConstellationType(singleSatCorr.getConstellationType())
                 .setSatId(singleSatCorr.getSatId())
                 .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz())
-                .setSatIsLos(singleSatCorr.isSatelliteLineOfSight())
+                .setProbSatIsLos(singleSatCorr.getProbSatIsLos())
                 .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters())
                 .setExcessPathLengthUncertaintyMeters(
                         singleSatCorr.getExcessPathLengthUncertaintyMeters())
@@ -72,7 +72,7 @@
                         .setConstellationType(GnssStatus.CONSTELLATION_GALILEO)
                         .setSatId(12)
                         .setCarrierFrequencyHz(1575420000f)
-                        .setSatIsLos(true)
+                        .setProbSatIsLos(0.1f)
                         .setExcessPathLengthMeters(10.0f)
                         .setExcessPathLengthUncertaintyMeters(5.0f)
                         .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane());
diff --git a/media/Android.bp b/media/Android.bp
index d5da6f2..8ebc91a 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,4 +1,21 @@
 java_library {
+    name: "media1",
+
+    srcs: [
+        ":media1-srcs",
+    ],
+
+    sdk_version: "system_current",
+}
+
+filegroup {
+    name: "media1-srcs",
+    srcs: [
+        "java/android/media/session/MediaSessionProviderService.java",
+    ],
+}
+
+java_library {
     // TODO: include media2.jar in the media apex and add it to the bootclasspath.
     name: "media2",
 
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 4f23cca..f5a6f86 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -736,8 +736,9 @@
          * @param preset one of {@link MediaRecorder.AudioSource#DEFAULT},
          *     {@link MediaRecorder.AudioSource#MIC}, {@link MediaRecorder.AudioSource#CAMCORDER},
          *     {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
-         *     {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION} or
-         *     {@link MediaRecorder.AudioSource#UNPROCESSED}
+         *     {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
+         *     {@link MediaRecorder.AudioSource#UNPROCESSED} or
+         *     {@link MediaRecorder.AudioSource#VOICE_PERFORMANCE}
          * @return the same Builder instance.
          */
         @SystemApi
@@ -749,6 +750,7 @@
                 case MediaRecorder.AudioSource.VOICE_RECOGNITION:
                 case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
                 case MediaRecorder.AudioSource.UNPROCESSED:
+                case MediaRecorder.AudioSource.VOICE_PERFORMANCE:
                     mSource = preset;
                     break;
                 default:
@@ -760,7 +762,7 @@
         /**
          * @hide
          * Same as {@link #setCapturePreset(int)} but authorizes the use of HOTWORD,
-         * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK and VOICE_CALL.
+         * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK, VOICE_CALL and ECHO_REFERENCE.
          * @param preset
          * @return the same Builder instance.
          */
@@ -771,7 +773,8 @@
                     || (preset == MediaRecorder.AudioSource.RADIO_TUNER)
                     || (preset == MediaRecorder.AudioSource.VOICE_DOWNLINK)
                     || (preset == MediaRecorder.AudioSource.VOICE_UPLINK)
-                    || (preset == MediaRecorder.AudioSource.VOICE_CALL)) {
+                    || (preset == MediaRecorder.AudioSource.VOICE_CALL)
+                    || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)) {
                 mSource = preset;
             } else {
                 setCapturePreset(preset);
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 793aa27..5516086 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -816,7 +817,7 @@
      *
      * @return The audio frame size in bytes corresponding to the encoding and the channel mask.
      */
-    public int getFrameSizeInBytes() {
+    public @IntRange(from = 1) int getFrameSizeInBytes() {
         return mFrameSizeInBytes;
     }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 30b5480..202a3cf 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3802,6 +3802,12 @@
     public static final int DEVICE_IN_HDMI =
                                     AudioSystem.DEVICE_IN_HDMI;
     /** @hide
+     * The audio input device code for HDMI ARC
+     */
+    public static final int DEVICE_IN_HDMI_ARC =
+                                    AudioSystem.DEVICE_IN_HDMI_ARC;
+
+    /** @hide
      * The audio input device code for telephony voice RX path
      */
     public static final int DEVICE_IN_TELEPHONY_RX =
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 33f81f1..92afe7e 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -61,7 +61,8 @@
  * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
  * the total recording buffer size.
  */
-public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRecordingMonitorClient
+public class AudioRecord implements AudioRouting, MicrophoneDirection,
+        AudioRecordingMonitor, AudioRecordingMonitorClient
 {
     //---------------------------------------------------------
     // Constants
@@ -1657,7 +1658,6 @@
         return activeMicrophones;
     }
 
-
     //--------------------------------------------------------------------------
     // Implementation of AudioRecordingMonitor interface
     //--------------------
@@ -1707,6 +1707,33 @@
         return native_getPortId();
     }
 
+    //--------------------------------------------------------------------------
+    // MicrophoneDirection
+    //--------------------
+    /**
+     * Specifies the logical microphone (for processing).
+     *
+     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
+     * @return retval OK if the call is successful, an error code otherwise.
+     * @hide
+     */
+    public int setMicrophoneDirection(int direction) {
+        return native_set_microphone_direction(direction);
+    }
+
+    /**
+     * Specifies the zoom factor (i.e. the field dimension) for the selected microphone
+     * (for processing). The selected microphone is determined by the use-case for the stream.
+     *
+     * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
+     * though 0 (no zoom) to 1 (maximum zoom).
+     * @return retval OK if the call is successful, an error code otherwise.
+     * @hide
+     */
+    public int setMicrophoneFieldDimension(float zoom) {
+        return native_set_microphone_field_dimension(zoom);
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
@@ -1860,6 +1887,9 @@
 
     private native int native_getPortId();
 
+    private native int native_set_microphone_direction(int direction);
+    private native int native_set_microphone_field_dimension(float zoom);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index de76aef..52771e4 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -172,7 +172,8 @@
         MediaRecorder.AudioSource.CAMCORDER,
         MediaRecorder.AudioSource.VOICE_RECOGNITION,
         MediaRecorder.AudioSource.VOICE_COMMUNICATION,
-        MediaRecorder.AudioSource.UNPROCESSED
+        MediaRecorder.AudioSource.UNPROCESSED,
+        MediaRecorder.AudioSource.VOICE_PERFORMANCE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioSource {}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 45cde0f..6b74479 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -541,6 +541,9 @@
     public static final int DEVICE_IN_BUS = DEVICE_BIT_IN | 0x100000;
     public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x1000000;
     public static final int DEVICE_IN_USB_HEADSET = DEVICE_BIT_IN | 0x2000000;
+    public static final int DEVICE_IN_BLUETOOTH_BLE = DEVICE_BIT_IN | 0x4000000;
+    public static final int DEVICE_IN_HDMI_ARC = DEVICE_BIT_IN | 0x8000000;
+    public static final int DEVICE_IN_ECHO_REFERENCE = DEVICE_BIT_IN | 0x10000000;
     @UnsupportedAppUsage
     public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
 
@@ -567,6 +570,9 @@
                                              DEVICE_IN_BUS |
                                              DEVICE_IN_PROXY |
                                              DEVICE_IN_USB_HEADSET |
+                                             DEVICE_IN_BLUETOOTH_BLE |
+                                             DEVICE_IN_HDMI_ARC |
+                                             DEVICE_IN_ECHO_REFERENCE |
                                              DEVICE_IN_DEFAULT);
     public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET;
     public static final int DEVICE_IN_ALL_USB = (DEVICE_IN_USB_ACCESSORY |
@@ -641,6 +647,9 @@
     public static final String DEVICE_IN_BUS_NAME = "bus";
     public static final String DEVICE_IN_PROXY_NAME = "proxy";
     public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset";
+    public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble";
+    public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference";
+    public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc";
 
     @UnsupportedAppUsage
     public static String getOutputDeviceName(int device)
@@ -757,6 +766,12 @@
             return DEVICE_IN_PROXY_NAME;
         case DEVICE_IN_USB_HEADSET:
             return DEVICE_IN_USB_HEADSET_NAME;
+        case DEVICE_IN_BLUETOOTH_BLE:
+            return DEVICE_IN_BLUETOOTH_BLE_NAME;
+        case DEVICE_IN_ECHO_REFERENCE:
+            return DEVICE_IN_ECHO_REFERENCE_NAME;
+        case DEVICE_IN_HDMI_ARC:
+            return DEVICE_IN_HDMI_ARC_NAME;
         case DEVICE_IN_DEFAULT:
         default:
             return Integer.toString(device);
diff --git a/media/java/android/media/Controller2Link.java b/media/java/android/media/Controller2Link.java
index a62db5f..d11f776 100644
--- a/media/java/android/media/Controller2Link.java
+++ b/media/java/android/media/Controller2Link.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -102,6 +103,15 @@
         }
     }
 
+    /** Interface method for IMediaController2.notifyPlaybackActiveChanged */
+    public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
+        try {
+            mIController.notifyPlaybackActiveChanged(seq, playbackActive);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /** Interface method for IMediaController2.sendSessionCommand */
     public void sendSessionCommand(int seq, Session2Command command, Bundle args,
             ResultReceiver resultReceiver) {
@@ -135,6 +145,11 @@
         mController.onDisconnected(seq);
     }
 
+    /** Stub implementation for IMediaController2.notifyPlaybackActiveChanged */
+    public void onPlaybackActiveChanged(int seq, boolean playbackActive) {
+        mController.onPlaybackActiveChanged(seq, playbackActive);
+    }
+
     /** Stub implementation for IMediaController2.sendSessionCommand */
     public void onSessionCommand(int seq, Session2Command command, Bundle args,
             ResultReceiver resultReceiver) {
@@ -149,23 +164,53 @@
     private class Controller2Stub extends IMediaController2.Stub {
         @Override
         public void notifyConnected(int seq, Bundle connectionResult) {
-            Controller2Link.this.onConnected(seq, connectionResult);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Controller2Link.this.onConnected(seq, connectionResult);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void notifyDisconnected(int seq) {
-            Controller2Link.this.onDisconnected(seq);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Controller2Link.this.onDisconnected(seq);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Controller2Link.this.onPlaybackActiveChanged(seq, playbackActive);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void sendSessionCommand(int seq, Session2Command command, Bundle args,
                 ResultReceiver resultReceiver) {
-            Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void cancelSessionCommand(int seq) {
-            Controller2Link.this.onCancelCommand(seq);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Controller2Link.this.onCancelCommand(seq);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 }
diff --git a/media/java/android/media/IMediaController2.aidl b/media/java/android/media/IMediaController2.aidl
index ca5394f..42c6e70 100644
--- a/media/java/android/media/IMediaController2.aidl
+++ b/media/java/android/media/IMediaController2.aidl
@@ -31,8 +31,9 @@
 oneway interface IMediaController2 {
     void notifyConnected(int seq, in Bundle connectionResult) = 0;
     void notifyDisconnected(int seq) = 1;
+    void notifyPlaybackActiveChanged(int seq, boolean playbackActive) = 2;
     void sendSessionCommand(int seq, in Session2Command command, in Bundle args,
-            in ResultReceiver resultReceiver) = 2;
-    void cancelSessionCommand(int seq) = 3;
-    // Next Id : 4
+            in ResultReceiver resultReceiver) = 3;
+    void cancelSessionCommand(int seq) = 4;
+    // Next Id : 5
 }
diff --git a/media/java/android/media/IMediaSession2Service.aidl b/media/java/android/media/IMediaSession2Service.aidl
new file mode 100644
index 0000000..10ac1be
--- /dev/null
+++ b/media/java/android/media/IMediaSession2Service.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 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.os.Bundle;
+import android.media.Controller2Link;
+
+/**
+ * Interface from MediaController2 to MediaSession2Service.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from controller to make controller owner(s) frozen.
+ * @hide
+ */
+oneway interface IMediaSession2Service {
+    void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0;
+    // Next Id : 1
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0375de3..f756658 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1829,9 +1829,14 @@
 
         mBufferLock = new Object();
 
+        // save name used at creation
+        mNameAtCreation = nameIsType ? null : name;
+
         native_setup(name, nameIsType, encoder);
     }
 
+    private String mNameAtCreation;
+
     @Override
     protected void finalize() {
         native_finalize();
@@ -2289,6 +2294,30 @@
          */
         public static final int ERROR_UNSUPPORTED_OPERATION = 6;
 
+        /**
+         * This indicates that the security level of the device is not
+         * sufficient to meet the requirements set by the content owner
+         * in the license policy.
+         */
+        public static final int ERROR_INSUFFICIENT_SECURITY = 7;
+
+        /**
+         * This indicates that the video frame being decrypted exceeds
+         * the size of the device's protected output buffers. When
+         * encountering this error the app should try playing content
+         * of a lower resolution.
+         */
+        public static final int ERROR_FRAME_TOO_LARGE = 8;
+
+        /**
+         * This error indicates that session state has been
+         * invalidated. It can occur on devices that are not capable
+         * of retaining crypto session state across device
+         * suspend/resume. The session must be closed and a new
+         * session opened to resume operation.
+         */
+        public static final int ERROR_LOST_STATE = 9;
+
         /** @hide */
         @IntDef({
             ERROR_NO_KEY,
@@ -2296,7 +2325,10 @@
             ERROR_RESOURCE_BUSY,
             ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
             ERROR_SESSION_NOT_OPENED,
-            ERROR_UNSUPPORTED_OPERATION
+            ERROR_UNSUPPORTED_OPERATION,
+            ERROR_INSUFFICIENT_SECURITY,
+            ERROR_FRAME_TOO_LARGE,
+            ERROR_LOST_STATE
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface CryptoErrorCode {}
@@ -3290,12 +3322,36 @@
     private native void native_setAudioPresentation(int presentationId, int programId);
 
     /**
-     * Get the component name. If the codec was created by createDecoderByType
-     * or createEncoderByType, what component is chosen is not known beforehand.
+     * Retrieve the codec name.
+     *
+     * If the codec was created by createDecoderByType or createEncoderByType, what component is
+     * chosen is not known beforehand. This method returns the name of the codec that was
+     * selected by the platform.
+     *
+     * <strong>Note:</strong> Implementations may provide multiple aliases (codec
+     * names) for the same underlying codec, any of which can be used to instantiate the same
+     * underlying codec in {@link MediaCodec#createByCodecName}. This method returns the
+     * name used to create the codec in this case.
+     *
      * @throws IllegalStateException if in the Released state.
      */
     @NonNull
-    public native final String getName();
+    public final String getName() {
+        // get canonical name to handle exception
+        String canonicalName = getCanonicalName();
+        return mNameAtCreation != null ? mNameAtCreation : canonicalName;
+    }
+
+    /**
+     * Retrieve the underlying codec name.
+     *
+     * This method is similar to {@link #getName}, except that it returns the underlying component
+     * name even if an alias was used to create this MediaCodec object by name,
+     *
+     * @throws IllegalStateException if in the Released state.
+     */
+    @NonNull
+    public native final String getCanonicalName();
 
     /**
      *  Return Metrics data about the current codec instance.
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 10a1e3a..751d57b 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -32,8 +32,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Vector;
 
 /**
  * Provides information about a given media codec available on the device. You can
@@ -61,15 +63,25 @@
  *
  */
 public final class MediaCodecInfo {
-    private boolean mIsEncoder;
+    private static final String TAG = "MediaCodecInfo";
+
+    private static final int FLAG_IS_ENCODER = (1 << 0);
+    private static final int FLAG_IS_VENDOR = (1 << 1);
+    private static final int FLAG_IS_SOFTWARE_ONLY = (1 << 2);
+    private static final int FLAG_IS_HARDWARE_ACCELERATED = (1 << 3);
+
+    private int mFlags;
     private String mName;
+    private String mCanonicalName;
     private Map<String, CodecCapabilities> mCaps;
 
     /* package private */ MediaCodecInfo(
-            String name, boolean isEncoder, CodecCapabilities[] caps) {
+            String name, String canonicalName, int flags, CodecCapabilities[] caps) {
         mName = name;
-        mIsEncoder = isEncoder;
+        mCanonicalName = canonicalName;
+        mFlags = flags;
         mCaps = new HashMap<String, CodecCapabilities>();
+
         for (CodecCapabilities c: caps) {
             mCaps.put(c.getMimeType(), c);
         }
@@ -77,16 +89,69 @@
 
     /**
      * Retrieve the codec name.
+     *
+     * <strong>Note:</strong> Implementations may provide multiple aliases (codec
+     * names) for the same underlying codec, any of which can be used to instantiate the same
+     * underlying codec in {@link MediaCodec#createByCodecName}.
+     *
+     * Applications targeting SDK < {@link android.os.Build.VERSION_CODES#Q}, cannot determine if
+     * the multiple codec names listed in MediaCodecList are in-fact for the same codec.
      */
+    @NonNull
     public final String getName() {
         return mName;
     }
 
     /**
+     * Retrieve the underlying codec name.
+     *
+     * Device implementations may provide multiple aliases (codec names) for the same underlying
+     * codec to maintain backward app compatibility. This method returns the name of the underlying
+     * codec name, which must not be another alias. For non-aliases this is always the name of the
+     * codec.
+     */
+    @NonNull
+    public final String getCanonicalName() {
+        return mCanonicalName;
+    }
+
+    /**
+     * Query if the codec is an alias for another underlying codec.
+     */
+    public final boolean isAlias() {
+        return !mName.equals(mCanonicalName);
+    }
+
+    /**
      * Query if the codec is an encoder.
      */
     public final boolean isEncoder() {
-        return mIsEncoder;
+        return (mFlags & FLAG_IS_ENCODER) != 0;
+    }
+
+    /**
+     * Query if the codec is provided by the Android platform (false) or the device manufacturer
+     * (true).
+     */
+    public final boolean isVendor() {
+        return (mFlags & FLAG_IS_VENDOR) != 0;
+    }
+
+    /**
+     * Query if the codec is software only. Software-only codecs are more secure as they run in
+     * a tighter security sandbox. On the other hand, software-only codecs do not provide any
+     * performance guarantees.
+     */
+    public final boolean isSoftwareOnly() {
+        return (mFlags & FLAG_IS_SOFTWARE_ONLY) != 0;
+    }
+
+    /**
+     * Query if the codec is hardware accelerated. This attribute is provided by the device
+     * manufacturer. Note that it cannot be tested for correctness.
+     */
+    public final boolean isHardwareAccelerated() {
+        return (mFlags & FLAG_IS_HARDWARE_ACCELERATED) != 0;
     }
 
     /**
@@ -163,7 +228,7 @@
         // such as B-frame support, arithmetic coding...
         public CodecProfileLevel[] profileLevels;  // NOTE this array is modifiable by user
 
-        // from OMX_COLOR_FORMATTYPE
+        // from MediaCodecConstants
         /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */
         public static final int COLOR_FormatMonochrome              = 1;
         /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */
@@ -344,7 +409,7 @@
         /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
         public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
         // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference.
-        // In OMX this is called OMX_COLOR_FormatAndroidOpaque.
+        // Note: in OMX this is called OMX_COLOR_FormatAndroidOpaque.
         public static final int COLOR_FormatSurface                   = 0x7F000789;
 
         /**
@@ -435,8 +500,7 @@
         public static final int COLOR_QCOM_FormatYUV420SemiPlanar     = 0x7fa30c00;
 
         /**
-         * Defined in the OpenMAX IL specs, color format values are drawn from
-         * OMX_COLOR_FORMATTYPE.
+         * The color format for the media. This is one of the color constants defined in this class.
          */
         public int[] colorFormats; // NOTE this array is modifiable by user
 
@@ -462,6 +526,26 @@
         public static final String FEATURE_TunneledPlayback       = "tunneled-playback";
 
         /**
+         * If true, the timestamp of each output buffer is derived from the timestamp of the input
+         * buffer that produced the output. If false, the timestamp of each output buffer is
+         * derived from the timestamp of the first input buffer.
+         */
+        public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
+
+        /**
+         * <b>decoder only</b>If true, the codec supports partial (including multiple) access units
+         * per input buffer.
+         */
+        public static final String FEATURE_FrameParsing = "frame-parsing";
+
+        /**
+         * If true, the codec supports multiple access units (for decoding, or to output for
+         * encoders). If false, the codec only supports single access units. Producing multiple
+         * access units for output is an optional feature.
+         */
+        public static final String FEATURE_MultipleFrames = "multiple-frames";
+
+        /**
          * <b>video decoder only</b>: codec supports queuing partial frames.
          */
         public static final String FEATURE_PartialFrame = "partial-frame";
@@ -497,10 +581,15 @@
             new Feature(FEATURE_SecurePlayback,   (1 << 1), false),
             new Feature(FEATURE_TunneledPlayback, (1 << 2), false),
             new Feature(FEATURE_PartialFrame,     (1 << 3), false),
+            new Feature(FEATURE_FrameParsing,     (1 << 4), false),
+            new Feature(FEATURE_MultipleFrames,   (1 << 5), false),
+            new Feature(FEATURE_DynamicTimestamp, (1 << 6), false),
         };
 
         private static final Feature[] encoderFeatures = {
             new Feature(FEATURE_IntraRefresh, (1 << 0), false),
+            new Feature(FEATURE_MultipleFrames, (1 << 1), false),
+            new Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
         };
 
         /** @hide */
@@ -869,7 +958,7 @@
 
             CodecCapabilities ret = new CodecCapabilities(
                 new CodecProfileLevel[] { pl }, new int[0], true /* encoder */,
-                0 /* flags */, defaultFormat, new MediaFormat() /* info */);
+                defaultFormat, new MediaFormat() /* info */);
             if (ret.mError != 0) {
                 return null;
             }
@@ -878,10 +967,10 @@
 
         /* package private */ CodecCapabilities(
                 CodecProfileLevel[] profLevs, int[] colFmts,
-                boolean encoder, int flags,
+                boolean encoder,
                 Map<String, Object>defaultFormatMap,
                 Map<String, Object>capabilitiesMap) {
-            this(profLevs, colFmts, encoder, flags,
+            this(profLevs, colFmts, encoder,
                     new MediaFormat(defaultFormatMap),
                     new MediaFormat(capabilitiesMap));
         }
@@ -889,11 +978,11 @@
         private MediaFormat mCapabilitiesInfo;
 
         /* package private */ CodecCapabilities(
-                CodecProfileLevel[] profLevs, int[] colFmts, boolean encoder, int flags,
+                CodecProfileLevel[] profLevs, int[] colFmts, boolean encoder,
                 MediaFormat defaultFormat, MediaFormat info) {
             final Map<String, Object> map = info.getMap();
             colorFormats = colFmts;
-            mFlagsVerified = flags;
+            mFlagsVerified = 0; // TODO: remove as it is unused
             mDefaultFormat = defaultFormat;
             mCapabilitiesInfo = info;
             mMime = mDefaultFormat.getString(MediaFormat.KEY_MIME);
@@ -1243,6 +1332,7 @@
         private Range<Rational> mBlockAspectRatioRange;
         private Range<Long> mBlocksPerSecondRange;
         private Map<Size, Range<Long>> mMeasuredFrameRates;
+        private Vector<PerformancePoint> mPerformancePoints;
         private Range<Integer> mFrameRateRange;
 
         private int mBlockWidth;
@@ -1524,6 +1614,158 @@
         }
 
         /**
+         * Video performance points are a set of standard performance points defined by pixel rate.
+         */
+        public static final class PerformancePoint {
+            /**
+             * Frame width in pixels.
+             */
+            public final int width;
+
+            /**
+             * Frame height in pixels.
+             */
+            public final int height;
+
+            /**
+             * Frame rate in frames per second.
+             */
+            public final int frameRate;
+
+            /* package private */
+            PerformancePoint(int width_, int height_, int frameRate_) {
+                width = width_;
+                height = height_;
+                frameRate = frameRate_;
+            }
+
+            /**
+             * Checks whether the performance point covers a media format.
+             *
+             * @param format Stream format considered
+             *
+             * @return {@code true} if the performance point covers the format.
+             */
+            public boolean covers(@NonNull MediaFormat format) {
+                // for simplicity, this code assumes a 16x16 block size.
+                long macroBlocks = ((width + 15) / 16) * (long)((height + 15) / 16);
+                long mbps = macroBlocks * frameRate;
+
+                long formatMacroBlocks =
+                    (long)((format.getInteger(MediaFormat.KEY_WIDTH, 0) + 15) / 16)
+                            * ((format.getInteger(MediaFormat.KEY_HEIGHT, 0) + 15) / 16);
+                double formatMbps =
+                    Math.ceil(formatMacroBlocks
+                              * format.getNumber(MediaFormat.KEY_FRAME_RATE, 0).doubleValue());
+                return formatMacroBlocks > 0 && formatMacroBlocks <= macroBlocks
+                        && formatMbps <= mbps;
+            }
+
+            @Override
+            public boolean equals(Object o) {
+                if (o instanceof PerformancePoint) {
+                    PerformancePoint other = (PerformancePoint)o;
+                    return ((long)width * height) == ((long)other.width * other.height)
+                            && frameRate == other.frameRate;
+                }
+                return false;
+            }
+
+            /** 480p 24fps */
+            public static final PerformancePoint SD_24 = new PerformancePoint(720, 480, 24);
+            /** 576p 25fps */
+            public static final PerformancePoint SD_25 = new PerformancePoint(720, 576, 25);
+            /** 480p 30fps */
+            public static final PerformancePoint SD_30 = new PerformancePoint(720, 480, 30);
+            /** 480p 48fps */
+            public static final PerformancePoint SD_48 = new PerformancePoint(720, 480, 48);
+            /** 576p 50fps */
+            public static final PerformancePoint SD_50 = new PerformancePoint(720, 576, 50);
+            /** 480p 60fps */
+            public static final PerformancePoint SD_60 = new PerformancePoint(720, 480, 60);
+
+            /** 720p 24fps */
+            public static final PerformancePoint HD_24 = new PerformancePoint(1280, 720, 24);
+            /** 720p 25fps */
+            public static final PerformancePoint HD_25 = new PerformancePoint(1280, 720, 25);
+            /** 720p 30fps */
+            public static final PerformancePoint HD_30 = new PerformancePoint(1280, 720, 30);
+            /** 720p 50fps */
+            public static final PerformancePoint HD_50 = new PerformancePoint(1280, 720, 50);
+            /** 720p 60fps */
+            public static final PerformancePoint HD_60 = new PerformancePoint(1280, 720, 60);
+            /** 720p 100fps */
+            public static final PerformancePoint HD_100 = new PerformancePoint(1280, 720, 100);
+            /** 720p 120fps */
+            public static final PerformancePoint HD_120 = new PerformancePoint(1280, 720, 120);
+            /** 720p 200fps */
+            public static final PerformancePoint HD_200 = new PerformancePoint(1280, 720, 200);
+            /** 720p 240fps */
+            public static final PerformancePoint HD_240 = new PerformancePoint(1280, 720, 240);
+
+            /** 1080p 24fps */
+            public static final PerformancePoint FHD_24 = new PerformancePoint(1920, 1080, 24);
+            /** 1080p 25fps */
+            public static final PerformancePoint FHD_25 = new PerformancePoint(1920, 1080, 25);
+            /** 1080p 30fps */
+            public static final PerformancePoint FHD_30 = new PerformancePoint(1920, 1080, 30);
+            /** 1080p 50fps */
+            public static final PerformancePoint FHD_50 = new PerformancePoint(1920, 1080, 50);
+            /** 1080p 60fps */
+            public static final PerformancePoint FHD_60 = new PerformancePoint(1920, 1080, 60);
+            /** 1080p 100fps */
+            public static final PerformancePoint FHD_100 = new PerformancePoint(1920, 1080, 100);
+            /** 1080p 120fps */
+            public static final PerformancePoint FHD_120 = new PerformancePoint(1920, 1080, 120);
+            /** 1080p 200fps */
+            public static final PerformancePoint FHD_200 = new PerformancePoint(1920, 1080, 200);
+            /** 1080p 240fps */
+            public static final PerformancePoint FHD_240 = new PerformancePoint(1920, 1080, 240);
+
+            /** 2160p 24fps */
+            public static final PerformancePoint UHD_24 = new PerformancePoint(3840, 2160, 24);
+            /** 2160p 25fps */
+            public static final PerformancePoint UHD_25 = new PerformancePoint(3840, 2160, 25);
+            /** 2160p 30fps */
+            public static final PerformancePoint UHD_30 = new PerformancePoint(3840, 2160, 30);
+            /** 2160p 50fps */
+            public static final PerformancePoint UHD_50 = new PerformancePoint(3840, 2160, 50);
+            /** 2160p 60fps */
+            public static final PerformancePoint UHD_60 = new PerformancePoint(3840, 2160, 60);
+            /** 2160p 100fps */
+            public static final PerformancePoint UHD_100 = new PerformancePoint(3840, 2160, 100);
+            /** 2160p 120fps */
+            public static final PerformancePoint UHD_120 = new PerformancePoint(3840, 2160, 120);
+            /** 2160p 200fps */
+            public static final PerformancePoint UHD_200 = new PerformancePoint(3840, 2160, 200);
+            /** 2160p 240fps */
+            public static final PerformancePoint UHD_240 = new PerformancePoint(3840, 2160, 240);
+        }
+
+        /**
+         * Returns the supported performance points. May return {@code null} if the codec did not
+         * publish any performance point information (e.g. the vendor codecs have not been updated
+         * to the latest android release). May return an empty list if the codec published that
+         * if does not guarantee any performance points.
+         * <p>
+         * This is a performance guarantee provided by the device manufacturer for hardware codecs
+         * based on hardware capabilities of the device.
+         * <p>
+         * The returned list is sorted first by decreasing number of pixels, then by decreasing
+         * width, and finally by decreasing frame rate.
+         * Performance points assume a single active codec. For use cases where multiple
+         * codecs are active, should use that highest pixel count, and add the frame rates of
+         * each individual codec.
+         */
+        @Nullable
+        public List<PerformancePoint> getSupportedPerformancePoints() {
+            if (mPerformancePoints == null) {
+                return null;
+            }
+            return new ArrayList<PerformancePoint>(mPerformancePoints);
+        }
+
+        /**
          * Returns whether a given video size ({@code width} and
          * {@code height}) and {@code frameRate} combination is supported.
          */
@@ -1659,6 +1901,50 @@
             mSmallerDimensionUpperLimit = SIZE_RANGE.getUpper();
         }
 
+        private @Nullable Vector<PerformancePoint> getPerformancePoints(Map<String, Object> map) {
+            Vector<PerformancePoint> ret = new Vector<>();
+            final String prefix = "performance-point-";
+            Set<String> keys = map.keySet();
+            for (String key : keys) {
+                // looking for: performance-point-WIDTHxHEIGHT-range
+                if (!key.startsWith(prefix)) {
+                    continue;
+                }
+                String subKey = key.substring(prefix.length());
+                if (subKey.equals("none") && ret.size() == 0) {
+                    // This means that component knowingly did not publish performance points.
+                    // This is different from when the component forgot to publish performance
+                    // points.
+                    return ret;
+                }
+                String[] temp = key.split("-");
+                if (temp.length != 4) {
+                    continue;
+                }
+                String sizeStr = temp[2];
+                Size size = Utils.parseSize(sizeStr, null);
+                if (size == null || size.getWidth() * size.getHeight() <= 0) {
+                    continue;
+                }
+                Range<Long> range = Utils.parseLongRange(map.get(key), null);
+                if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
+                    continue;
+                }
+                ret.add(new PerformancePoint(
+                        size.getWidth(), size.getHeight(), range.getLower().intValue()));
+            }
+            // check if the component specified no performance point indication
+            if (ret.size() == 0) {
+                return null;
+            }
+
+            // sort reversed by area first, then by frame rate
+            ret.sort((a, b) -> (a.width * a.height != b.width * b.height ?
+                                    (b.width * b.height - a.width * a.height) :
+                                    (b.frameRate - a.frameRate)));
+            return ret;
+        }
+
         private Map<Size, Range<Long>> getMeasuredFrameRates(Map<String, Object> map) {
             Map<Size, Range<Long>> ret = new HashMap<Size, Range<Long>>();
             final String prefix = "measured-frame-rate-";
@@ -1770,6 +2056,7 @@
             blockRates =
                 Utils.parseLongRange(map.get("blocks-per-second-range"), null);
             mMeasuredFrameRates = getMeasuredFrameRates(map);
+            mPerformancePoints = getPerformancePoints(map);
             Pair<Range<Integer>, Range<Integer>> sizeRanges =
                 parseWidthHeightRanges(map.get("size-range"));
             if (sizeRanges != null) {
@@ -2879,7 +3166,9 @@
      * {@link MediaCodecInfo.CodecCapabilities#profileLevels} field.
      */
     public static final class CodecProfileLevel {
-        // from OMX_VIDEO_AVCPROFILETYPE
+        // These constants were originally in-line with OMX values, but this
+        // correspondence is no longer maintained.
+
         public static final int AVCProfileBaseline = 0x01;
         public static final int AVCProfileMain     = 0x02;
         public static final int AVCProfileExtended = 0x04;
@@ -2890,7 +3179,6 @@
         public static final int AVCProfileConstrainedBaseline = 0x10000;
         public static final int AVCProfileConstrainedHigh     = 0x80000;
 
-        // from OMX_VIDEO_AVCLEVELTYPE
         public static final int AVCLevel1       = 0x01;
         public static final int AVCLevel1b      = 0x02;
         public static final int AVCLevel11      = 0x04;
@@ -2908,8 +3196,10 @@
         public static final int AVCLevel5       = 0x4000;
         public static final int AVCLevel51      = 0x8000;
         public static final int AVCLevel52      = 0x10000;
+        public static final int AVCLevel6       = 0x20000;
+        public static final int AVCLevel61      = 0x40000;
+        public static final int AVCLevel62      = 0x80000;
 
-        // from OMX_VIDEO_H263PROFILETYPE
         public static final int H263ProfileBaseline             = 0x01;
         public static final int H263ProfileH320Coding           = 0x02;
         public static final int H263ProfileBackwardCompatible   = 0x04;
@@ -2920,7 +3210,6 @@
         public static final int H263ProfileInterlace            = 0x80;
         public static final int H263ProfileHighLatency          = 0x100;
 
-        // from OMX_VIDEO_H263LEVELTYPE
         public static final int H263Level10      = 0x01;
         public static final int H263Level20      = 0x02;
         public static final int H263Level30      = 0x04;
@@ -2930,7 +3219,6 @@
         public static final int H263Level60      = 0x40;
         public static final int H263Level70      = 0x80;
 
-        // from OMX_VIDEO_MPEG4PROFILETYPE
         public static final int MPEG4ProfileSimple              = 0x01;
         public static final int MPEG4ProfileSimpleScalable      = 0x02;
         public static final int MPEG4ProfileCore                = 0x04;
@@ -2948,7 +3236,6 @@
         public static final int MPEG4ProfileAdvancedScalable    = 0x4000;
         public static final int MPEG4ProfileAdvancedSimple      = 0x8000;
 
-        // from OMX_VIDEO_MPEG4LEVELTYPE
         public static final int MPEG4Level0      = 0x01;
         public static final int MPEG4Level0b     = 0x02;
         public static final int MPEG4Level1      = 0x04;
@@ -2960,7 +3247,6 @@
         public static final int MPEG4Level5      = 0x80;
         public static final int MPEG4Level6      = 0x100;
 
-        // from OMX_VIDEO_MPEG2PROFILETYPE
         public static final int MPEG2ProfileSimple              = 0x00;
         public static final int MPEG2ProfileMain                = 0x01;
         public static final int MPEG2Profile422                 = 0x02;
@@ -2968,14 +3254,12 @@
         public static final int MPEG2ProfileSpatial             = 0x04;
         public static final int MPEG2ProfileHigh                = 0x05;
 
-        // from OMX_VIDEO_MPEG2LEVELTYPE
         public static final int MPEG2LevelLL     = 0x00;
         public static final int MPEG2LevelML     = 0x01;
         public static final int MPEG2LevelH14    = 0x02;
         public static final int MPEG2LevelHL     = 0x03;
         public static final int MPEG2LevelHP     = 0x04;
 
-        // from OMX_AUDIO_AACPROFILETYPE
         public static final int AACObjectMain       = 1;
         public static final int AACObjectLC         = 2;
         public static final int AACObjectSSR        = 3;
@@ -2990,16 +3274,13 @@
         /** xHE-AAC (includes USAC) */
         public static final int AACObjectXHE        = 42;
 
-        // from OMX_VIDEO_VP8LEVELTYPE
         public static final int VP8Level_Version0 = 0x01;
         public static final int VP8Level_Version1 = 0x02;
         public static final int VP8Level_Version2 = 0x04;
         public static final int VP8Level_Version3 = 0x08;
 
-        // from OMX_VIDEO_VP8PROFILETYPE
         public static final int VP8ProfileMain = 0x01;
 
-        // from OMX_VIDEO_VP9PROFILETYPE
         public static final int VP9Profile0 = 0x01;
         public static final int VP9Profile1 = 0x02;
         public static final int VP9Profile2 = 0x04;
@@ -3010,7 +3291,6 @@
         public static final int VP9Profile2HDR10Plus = 0x4000;
         public static final int VP9Profile3HDR10Plus = 0x8000;
 
-        // from OMX_VIDEO_VP9LEVELTYPE
         public static final int VP9Level1  = 0x1;
         public static final int VP9Level11 = 0x2;
         public static final int VP9Level2  = 0x4;
@@ -3026,14 +3306,12 @@
         public static final int VP9Level61 = 0x1000;
         public static final int VP9Level62 = 0x2000;
 
-        // from OMX_VIDEO_HEVCPROFILETYPE
         public static final int HEVCProfileMain        = 0x01;
         public static final int HEVCProfileMain10      = 0x02;
         public static final int HEVCProfileMainStill   = 0x04;
         public static final int HEVCProfileMain10HDR10 = 0x1000;
         public static final int HEVCProfileMain10HDR10Plus = 0x2000;
 
-        // from OMX_VIDEO_HEVCLEVELTYPE
         public static final int HEVCMainTierLevel1  = 0x1;
         public static final int HEVCHighTierLevel1  = 0x2;
         public static final int HEVCMainTierLevel2  = 0x4;
@@ -3067,7 +3345,6 @@
             HEVCHighTierLevel51 | HEVCHighTierLevel52 | HEVCHighTierLevel6 | HEVCHighTierLevel61 |
             HEVCHighTierLevel62;
 
-        // from OMX_VIDEO_DOLBYVISIONPROFILETYPE
         public static final int DolbyVisionProfileDvavPer = 0x1;
         public static final int DolbyVisionProfileDvavPen = 0x2;
         public static final int DolbyVisionProfileDvheDer = 0x4;
@@ -3079,7 +3356,6 @@
         public static final int DolbyVisionProfileDvheSt = 0x100;
         public static final int DolbyVisionProfileDvavSe = 0x200;
 
-        // from OMX_VIDEO_DOLBYVISIONLEVELTYPE
         public static final int DolbyVisionLevelHd24    = 0x1;
         public static final int DolbyVisionLevelHd30    = 0x2;
         public static final int DolbyVisionLevelFhd24   = 0x4;
@@ -3090,17 +3366,44 @@
         public static final int DolbyVisionLevelUhd48   = 0x80;
         public static final int DolbyVisionLevelUhd60   = 0x100;
 
+        public static final int AV1Profile0     = 0x1;
+        public static final int AV1Profile1     = 0x2;
+        public static final int AV1Profile2     = 0x4;
+
+        public static final int AV1Level2       = 0x1;
+        public static final int AV1Level21      = 0x2;
+        public static final int AV1Level22      = 0x4;
+        public static final int AV1Level23      = 0x8;
+        public static final int AV1Level3       = 0x10;
+        public static final int AV1Level31      = 0x20;
+        public static final int AV1Level32      = 0x40;
+        public static final int AV1Level33      = 0x80;
+        public static final int AV1Level4       = 0x100;
+        public static final int AV1Level41      = 0x200;
+        public static final int AV1Level42      = 0x400;
+        public static final int AV1Level43      = 0x800;
+        public static final int AV1Level5       = 0x1000;
+        public static final int AV1Level51      = 0x2000;
+        public static final int AV1Level52      = 0x4000;
+        public static final int AV1Level53      = 0x8000;
+        public static final int AV1Level6       = 0x10000;
+        public static final int AV1Level61      = 0x20000;
+        public static final int AV1Level62      = 0x40000;
+        public static final int AV1Level63      = 0x80000;
+        public static final int AV1Level7       = 0x100000;
+        public static final int AV1Level71      = 0x200000;
+        public static final int AV1Level72      = 0x400000;
+        public static final int AV1Level73      = 0x800000;
+
         /**
-         * Defined in the OpenMAX IL specs, depending on the type of media
-         * this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE,
-         * OMX_VIDEO_MPEG4PROFILETYPE, OMX_VIDEO_VP8PROFILETYPE or OMX_VIDEO_VP9PROFILETYPE.
+         * The profile of the media content. Depending on the type of media this can be
+         * one of the profile values defined in this class.
          */
         public int profile;
 
         /**
-         * Defined in the OpenMAX IL specs, depending on the type of media
-         * this can be OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE
-         * OMX_VIDEO_MPEG4LEVELTYPE, OMX_VIDEO_VP8LEVELTYPE or OMX_VIDEO_VP9LEVELTYPE.
+         * The level of the media content. Depending on the type of media this can be
+         * one of the level values defined in this class.
          *
          * Note that VP9 decoder on platforms before {@link android.os.Build.VERSION_CODES#N} may
          * not advertise a profile level support. For those VP9 decoders, please use
@@ -3157,7 +3460,7 @@
         }
 
         return new MediaCodecInfo(
-                mName, mIsEncoder,
+                mName, mCanonicalName, mFlags,
                 caps.toArray(new CodecCapabilities[caps.size()]));
     }
 }
diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java
index 2e47865..a460954 100644
--- a/media/java/android/media/MediaCodecList.java
+++ b/media/java/android/media/MediaCodecList.java
@@ -111,12 +111,14 @@
             caps[typeIx++] = getCodecCapabilities(index, type);
         }
         return new MediaCodecInfo(
-                getCodecName(index), isEncoder(index), caps);
+                getCodecName(index), getCanonicalName(index), getAttributes(index), caps);
     }
 
     /* package private */ static native final String getCodecName(int index);
 
-    /* package private */ static native final boolean isEncoder(int index);
+    /* package private */ static native final String getCanonicalName(int index);
+
+    /* package private */ static native final int getAttributes(int index);
 
     /* package private */ static native final String[] getSupportedTypes(int index);
 
diff --git a/media/java/android/media/MediaConstants.java b/media/java/android/media/MediaConstants.java
index 275b0ac..65b6f55 100644
--- a/media/java/android/media/MediaConstants.java
+++ b/media/java/android/media/MediaConstants.java
@@ -24,8 +24,9 @@
     static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
 
     // Bundle key for Parcelable
-    static final String KEY_SESSION2_STUB = "android.media.key.SESSION2_STUB";
+    static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
     static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
+    static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE";
 
     private MediaConstants() {
     }
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index b8381a7..7c5335b 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -19,19 +19,23 @@
 import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
 import static android.media.Session2Token.TYPE_SESSION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
-import android.os.Binder;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -41,15 +45,15 @@
 
 /**
  * Allows an app to interact with an active {@link MediaSession2} or a
- * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
+ * MediaSession2Service which would provide {@link MediaSession2}. Media buttons and other
  * commands can be sent to the session.
  * <p>
  * This API is not generally intended for third party application developers.
  * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
- * @hide
  */
+// TODO: use @link for MediaSession2Service
 public class MediaController2 implements AutoCloseable {
     static final String TAG = "MediaController2";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -63,6 +67,7 @@
     private final Executor mCallbackExecutor;
     private final Controller2Link mControllerStub;
     private final Handler mResultHandler;
+    private final SessionServiceConnection mServiceConnection;
 
     private final Object mLock = new Object();
     //@GuardedBy("mLock")
@@ -77,6 +82,8 @@
     private ArrayMap<ResultReceiver, Integer> mPendingCommands;
     //@GuardedBy("mLock")
     private ArraySet<Integer> mRequestedCommandSeqNumbers;
+    //@GuardedBy("mLock")
+    private boolean mPlaybackActive;
 
     /**
      * Create a {@link MediaController2} from the {@link Session2Token}.
@@ -118,20 +125,29 @@
         mPendingCommands = new ArrayMap<>();
         mRequestedCommandSeqNumbers = new ArraySet<>();
 
+        boolean connectRequested;
         if (token.getType() == TYPE_SESSION) {
-            connectToSession();
+            mServiceConnection = null;
+            connectRequested = requestConnectToSession();
         } else {
-            // TODO: Handle connect to session service.
+            mServiceConnection = new SessionServiceConnection();
+            connectRequested = requestConnectToService();
+        }
+        if (!connectRequested) {
+            close();
         }
     }
 
     @Override
     public void close() {
         synchronized (mLock) {
+            if (mServiceConnection != null) {
+                mContext.unbindService(mServiceConnection);
+            }
             if (mSessionBinder != null) {
                 try {
-                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
                     mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
+                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
                 } catch (RuntimeException e) {
                     // No-op
                 }
@@ -146,6 +162,18 @@
     }
 
     /**
+     * Returns whether the session's playback is active.
+     *
+     * @return {@code true} if playback active. {@code false} otherwise.
+     * @see ControllerCallback#onPlaybackActiveChanged(MediaController2, boolean)
+     */
+    public boolean isPlaybackActive() {
+        synchronized (mLock) {
+            return mPlaybackActive;
+        }
+    }
+
+    /**
      * Sends a session command to the session
      * <p>
      * @param command the session command
@@ -153,6 +181,7 @@
      * @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
      *        when its result is received.
      */
+    @NonNull
     public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
         if (command == null) {
             throw new IllegalArgumentException("command shouldn't be null");
@@ -206,89 +235,82 @@
 
     // Called by Controller2Link.onConnected
     void onConnected(int seq, Bundle connectionResult) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2_STUB);
-            Session2CommandGroup allowedCommands =
-                    connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
-            if (DEBUG) {
-                Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
-                        + ", allowedCommands=" + allowedCommands);
-            }
-            if (sessionBinder == null || allowedCommands == null) {
-                // Connection rejected.
-                close();
-                return;
-            }
-            synchronized (mLock) {
-                mSessionBinder = sessionBinder;
-                mAllowedCommands = allowedCommands;
-                // Implementation for the local binder is no-op,
-                // so can be used without worrying about deadlock.
-                sessionBinder.linkToDeath(mDeathRecipient, 0);
-                mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
-                        mSessionToken.getPackageName(), sessionBinder);
-            }
-            mCallbackExecutor.execute(() -> {
-                mCallback.onConnected(MediaController2.this, allowedCommands);
-            });
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
+        Session2CommandGroup allowedCommands =
+                connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
+        boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE);
+        if (DEBUG) {
+            Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
+                    + ", allowedCommands=" + allowedCommands);
         }
+        if (sessionBinder == null || allowedCommands == null) {
+            // Connection rejected.
+            close();
+            return;
+        }
+        synchronized (mLock) {
+            mSessionBinder = sessionBinder;
+            mAllowedCommands = allowedCommands;
+            mPlaybackActive = playbackActive;
+
+            // Implementation for the local binder is no-op,
+            // so can be used without worrying about deadlock.
+            sessionBinder.linkToDeath(mDeathRecipient, 0);
+            mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
+                    mSessionToken.getPackageName(), sessionBinder);
+        }
+        mCallbackExecutor.execute(() -> {
+            mCallback.onConnected(MediaController2.this, allowedCommands);
+        });
     }
 
     // Called by Controller2Link.onDisconnected
     void onDisconnected(int seq) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            // close() will call mCallback.onDisconnected
-            close();
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        // close() will call mCallback.onDisconnected
+        close();
+    }
+
+    // Called by Controller2Link.onPlaybackActiveChanged
+    void onPlaybackActiveChanged(int seq, boolean playbackActive) {
+        synchronized (mLock) {
+            mPlaybackActive = playbackActive;
         }
+        mCallbackExecutor.execute(() -> {
+            mCallback.onPlaybackActiveChanged(MediaController2.this, playbackActive);
+        });
     }
 
     // Called by Controller2Link.onSessionCommand
     void onSessionCommand(int seq, Session2Command command, Bundle args,
             @Nullable ResultReceiver resultReceiver) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                mRequestedCommandSeqNumbers.add(seq);
-            }
-            mCallbackExecutor.execute(() -> {
-                boolean isCanceled;
-                synchronized (mLock) {
-                    isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
-                }
-                if (isCanceled) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                    return;
-                }
-                Session2Command.Result result = mCallback.onSessionCommand(
-                        MediaController2.this, command, args);
-                if (resultReceiver != null) {
-                    if (result == null) {
-                        throw new RuntimeException("onSessionCommand shouldn't return null");
-                    } else {
-                        resultReceiver.send(result.getResultCode(), result.getResultData());
-                    }
-                }
-            });
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        synchronized (mLock) {
+            mRequestedCommandSeqNumbers.add(seq);
         }
+        mCallbackExecutor.execute(() -> {
+            boolean isCanceled;
+            synchronized (mLock) {
+                isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
+            }
+            if (isCanceled) {
+                resultReceiver.send(RESULT_INFO_SKIPPED, null);
+                return;
+            }
+            Session2Command.Result result = mCallback.onSessionCommand(
+                    MediaController2.this, command, args);
+            if (resultReceiver != null) {
+                if (result == null) {
+                    throw new RuntimeException("onSessionCommand shouldn't return null");
+                } else {
+                    resultReceiver.send(result.getResultCode(), result.getResultData());
+                }
+            }
+        });
     }
 
     // Called by Controller2Link.onSessionCommand
     void onCancelCommand(int seq) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                mRequestedCommandSeqNumbers.remove(seq);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        synchronized (mLock) {
+            mRequestedCommandSeqNumbers.remove(seq);
         }
     }
 
@@ -298,18 +320,55 @@
         }
     }
 
-    private void connectToSession() {
-        Session2Link sessionBinder = mSessionToken.getSessionLink();
+    private Bundle createConnectionRequest() {
         Bundle connectionRequest = new Bundle();
         connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName());
         connectionRequest.putInt(KEY_PID, Process.myPid());
+        return connectionRequest;
+    }
 
+    private boolean requestConnectToSession() {
+        Session2Link sessionBinder = mSessionToken.getSessionLink();
+        Bundle connectionRequest = createConnectionRequest();
         try {
             sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
         } catch (RuntimeException e) {
-            Log.w(TAG, "Failed to call connection request. Framework will retry"
-                    + " automatically");
+            Log.w(TAG, "Failed to call connection request", e);
+            return false;
         }
+        return true;
+    }
+
+    private boolean requestConnectToService() {
+        // Service. Needs to get fresh binder whenever connection is needed.
+        final Intent intent = new Intent(MediaSession2Service.SERVICE_INTERFACE);
+        intent.setClassName(mSessionToken.getPackageName(), mSessionToken.getServiceName());
+
+        // Use bindService() instead of startForegroundService() to start session service for three
+        // reasons.
+        // 1. Prevent session service owner's stopSelf() from destroying service.
+        //    With the startForegroundService(), service's call of stopSelf() will trigger immediate
+        //    onDestroy() calls on the main thread even when onConnect() is running in another
+        //    thread.
+        // 2. Minimize APIs for developers to take care about.
+        //    With bindService(), developers only need to take care about Service.onBind()
+        //    but Service.onStartCommand() should be also taken care about with the
+        //    startForegroundService().
+        // 3. Future support for UI-less playback
+        //    If a service wants to keep running, it should be either foreground service or
+        //    bound service. But there had been request for the feature for system apps
+        //    and using bindService() will be better fit with it.
+        synchronized (mLock) {
+            boolean result = mContext.bindService(
+                    intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+            if (!result) {
+                Log.w(TAG, "bind to " + mSessionToken + " failed");
+                return false;
+            } else if (DEBUG) {
+                Log.d(TAG, "bind to " + mSessionToken + " succeeded");
+            }
+        }
+        return true;
     }
 
     /**
@@ -341,6 +400,17 @@
         public void onDisconnected(@NonNull MediaController2 controller) {}
 
         /**
+         * Called when the playback of the session's playback activeness is changed.
+         *
+         * @param controller the controller for this event
+         * @param playbackActive {@code true} if the session's playback is active.
+         *                       {@code false} otherwise.
+         * @see MediaController2#isPlaybackActive()
+         */
+        public void onPlaybackActiveChanged(@NonNull MediaController2 controller,
+                boolean playbackActive) {}
+
+        /**
          * Called when the connected session sent a session command.
          *
          * @param controller the controller for this event
@@ -349,7 +419,7 @@
          * @return the result for the session command. A runtime exception will be thrown if null
          *         is returned.
          */
-        @NonNull
+        @Nullable
         public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
                 @NonNull Session2Command command, @Nullable Bundle args) {
             return null;
@@ -366,4 +436,59 @@
         public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
                 @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
     }
+
+    // This will be called on the main thread.
+    private class SessionServiceConnection implements ServiceConnection {
+        SessionServiceConnection() {
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            // Note that it's always main-thread.
+            boolean connectRequested = false;
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "onServiceConnected " + name + " " + this);
+                }
+                // Sanity check
+                if (!mSessionToken.getPackageName().equals(name.getPackageName())) {
+                    Log.wtf(TAG, "Expected connection to " + mSessionToken.getPackageName()
+                            + " but is connected to " + name);
+                    return;
+                }
+                IMediaSession2Service iService = IMediaSession2Service.Stub.asInterface(service);
+                if (iService == null) {
+                    Log.wtf(TAG, "Service interface is missing.");
+                    return;
+                }
+                Bundle connectionRequest = createConnectionRequest();
+                iService.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
+                connectRequested = true;
+            } catch (RemoteException e) {
+                Log.w(TAG, "Service " + name + " has died prematurely", e);
+            } finally {
+                if (!connectRequested) {
+                    close();
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // Temporal lose of the binding because of the service crash. System will automatically
+            // rebind, so just no-op.
+            if (DEBUG) {
+                Log.w(TAG, "Session service " + name + " is disconnected.");
+            }
+            close();
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            // Permanent lose of the binding because of the service package update or removed.
+            // This SessionServiceRecord will be removed accordingly, but forget session binder here
+            // for sure.
+            close();
+        }
+    }
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index cdbc7b44..75b3915 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -132,11 +132,19 @@
     private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
 
     private EventHandler mEventHandler;
-    private EventHandler mOnKeyStatusChangeEventHandler;
-    private EventHandler mOnExpirationUpdateEventHandler;
+    private EventHandler mKeyStatusChangeHandler;
+    private EventHandler mExpirationUpdateHandler;
+    private EventHandler mSessionLostStateHandler;
+
     private OnEventListener mOnEventListener;
     private OnKeyStatusChangeListener mOnKeyStatusChangeListener;
     private OnExpirationUpdateListener mOnExpirationUpdateListener;
+    private OnSessionLostStateListener mOnSessionLostStateListener;
+
+    private final Object mEventLock = new Object();
+    private final Object mKeyStatusChangeLock = new Object();
+    private final Object mExpirationUpdateLock = new Object();
+    private final Object mSessionLostStateLock = new Object();
 
     private long mNativeContext;
 
@@ -200,6 +208,35 @@
     private static final native boolean isCryptoSchemeSupportedNative(
             @NonNull byte[] uuid, @Nullable String mimeType);
 
+    private EventHandler createHandler() {
+        Looper looper;
+        EventHandler handler;
+        if ((looper = Looper.myLooper()) != null) {
+            handler = new EventHandler(this, looper);
+        } else if ((looper = Looper.getMainLooper()) != null) {
+            handler = new EventHandler(this, looper);
+        } else {
+            handler = null;
+        }
+        return handler;
+    }
+
+    private EventHandler updateHandler(Handler handler) {
+        Looper looper;
+        EventHandler newHandler = null;
+        if (handler != null) {
+            looper = handler.getLooper();
+        } else {
+            looper = Looper.myLooper();
+        }
+        if (looper != null) {
+            if (handler == null || handler.getLooper() != looper) {
+                newHandler = new EventHandler(this, looper);
+            }
+        }
+        return newHandler;
+    }
+
     /**
      * Instantiate a MediaDrm object
      *
@@ -209,14 +246,10 @@
      * specified scheme UUID
      */
     public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException {
-        Looper looper;
-        if ((looper = Looper.myLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else if ((looper = Looper.getMainLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else {
-            mEventHandler = null;
-        }
+        mEventHandler = createHandler();
+        mKeyStatusChangeHandler = createHandler();
+        mExpirationUpdateHandler = createHandler();
+        mSessionLostStateHandler = createHandler();
 
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
@@ -272,6 +305,40 @@
     }
 
     /**
+     * Thrown when an error occurs in any method that has a session context.
+     */
+    public static final class SessionException extends RuntimeException {
+        public SessionException(int errorCode, @Nullable String detailMessage) {
+            super(detailMessage);
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * This indicates that apps using MediaDrm sessions are
+         * temporarily exceeding the capacity of available crypto
+         * resources. The app should retry the operation later.
+         */
+        public static final int ERROR_RESOURCE_CONTENTION = 1;
+
+        /** @hide */
+        @IntDef({
+            ERROR_RESOURCE_CONTENTION,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface SessionErrorCode {}
+
+        /**
+         * Retrieve the error code associated with the SessionException
+         */
+        @SessionErrorCode
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+
+        private final int mErrorCode;
+    }
+
+    /**
      * Register a callback to be invoked when a session expiration update
      * occurs.  The app's OnExpirationUpdateListener will be notified
      * when the expiration time of the keys in the session have changed.
@@ -282,15 +349,12 @@
      */
     public void setOnExpirationUpdateListener(
             @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) {
-        if (listener != null) {
-            Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
-            if (looper != null) {
-                if (mEventHandler == null || mEventHandler.getLooper() != looper) {
-                    mEventHandler = new EventHandler(this, looper);
-                }
+        synchronized(mExpirationUpdateLock) {
+            if (listener != null) {
+                mExpirationUpdateHandler = updateHandler(handler);
             }
+            mOnExpirationUpdateListener = listener;
         }
-        mOnExpirationUpdateListener = listener;
     }
 
     /**
@@ -324,15 +388,12 @@
      */
     public void setOnKeyStatusChangeListener(
             @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) {
-        if (listener != null) {
-            Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
-            if (looper != null) {
-                if (mEventHandler == null || mEventHandler.getLooper() != looper) {
-                    mEventHandler = new EventHandler(this, looper);
-                }
+        synchronized(mKeyStatusChangeLock) {
+            if (listener != null) {
+                mKeyStatusChangeHandler = updateHandler(handler);
             }
+            mOnKeyStatusChangeListener = listener;
         }
-        mOnKeyStatusChangeListener = listener;
     }
 
     /**
@@ -360,6 +421,46 @@
     }
 
     /**
+     * Register a callback to be invoked when session state has been
+     * lost. This event can occur on devices that are not capable of
+     * retaining crypto session state across device suspend/resume
+     * cycles.  When this event occurs, the session must be closed and
+     * a new session opened to resume operation.
+     *
+     * @param listener the callback that will be run, or {@code null} to unregister the
+     *     previously registered callback.
+     * @param handler the handler on which the listener should be invoked, or
+     *     {@code null} if the listener should be invoked on the calling thread's looper.
+     */
+    public void setOnSessionLostStateListener(
+            @Nullable OnSessionLostStateListener listener, @Nullable Handler handler) {
+        synchronized(mSessionLostStateLock) {
+            if (listener != null) {
+                mSessionLostStateHandler = updateHandler(handler);
+            }
+            mOnSessionLostStateListener = listener;
+        }
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the
+     * session state has been lost and is now invalid
+     */
+    public interface OnSessionLostStateListener
+    {
+        /**
+         * Called when session state has lost state, to inform the app
+         * about the condition so it can close the session and open a new
+         * one to resume operation.
+         *
+         * @param md the MediaDrm object on which the event occurred
+         * @param sessionId the DRM session ID on which the event occurred
+         */
+        void onSessionLostState(
+                @NonNull MediaDrm md, @NonNull byte[] sessionId);
+    }
+
+    /**
      * Defines the status of a key.
      * A KeyStatus for each key in a session is provided to the
      * {@link OnKeyStatusChangeListener#onKeyStatusChange}
@@ -437,7 +538,9 @@
      */
     public void setOnEventListener(@Nullable OnEventListener listener)
     {
-        mOnEventListener = listener;
+        synchronized(mEventLock) {
+            mOnEventListener = listener;
+        }
     }
 
     /**
@@ -513,6 +616,7 @@
     private static final int DRM_EVENT = 200;
     private static final int EXPIRATION_UPDATE = 201;
     private static final int KEY_STATUS_CHANGE = 202;
+    private static final int SESSION_LOST_STATE = 203;
 
     private class EventHandler extends Handler
     {
@@ -532,52 +636,72 @@
             switch(msg.what) {
 
             case DRM_EVENT:
-                if (mOnEventListener != null) {
-                    if (msg.obj != null && msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        byte[] sessionId = parcel.createByteArray();
-                        if (sessionId.length == 0) {
-                            sessionId = null;
-                        }
-                        byte[] data = parcel.createByteArray();
-                        if (data.length == 0) {
-                            data = null;
-                        }
+                synchronized(mEventLock) {
+                    if (mOnEventListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            if (sessionId.length == 0) {
+                                sessionId = null;
+                            }
+                            byte[] data = parcel.createByteArray();
+                            if (data.length == 0) {
+                                data = null;
+                            }
 
-                        Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
-                        mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+                            Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
+                            mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+                        }
                     }
                 }
                 return;
 
             case KEY_STATUS_CHANGE:
-                if (mOnKeyStatusChangeListener != null) {
-                    if (msg.obj != null && msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        byte[] sessionId = parcel.createByteArray();
-                        if (sessionId.length > 0) {
-                            List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
-                            boolean hasNewUsableKey = (parcel.readInt() != 0);
+                synchronized(mKeyStatusChangeLock) {
+                    if (mOnKeyStatusChangeListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            if (sessionId.length > 0) {
+                                List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
+                                boolean hasNewUsableKey = (parcel.readInt() != 0);
 
-                            Log.i(TAG, "Drm key status changed");
-                            mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
-                                    keyStatusList, hasNewUsableKey);
+                                Log.i(TAG, "Drm key status changed");
+                                mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
+                                        keyStatusList, hasNewUsableKey);
+                            }
                         }
                     }
                 }
                 return;
 
             case EXPIRATION_UPDATE:
-                if (mOnExpirationUpdateListener != null) {
-                    if (msg.obj != null && msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        byte[] sessionId = parcel.createByteArray();
-                        if (sessionId.length > 0) {
-                            long expirationTime = parcel.readLong();
+                synchronized(mExpirationUpdateLock) {
+                    if (mOnExpirationUpdateListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            if (sessionId.length > 0) {
+                                long expirationTime = parcel.readLong();
 
-                            Log.i(TAG, "Drm key expiration update: " + expirationTime);
-                            mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
-                                    expirationTime);
+                                Log.i(TAG, "Drm key expiration update: " + expirationTime);
+                                mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
+                                        expirationTime);
+                            }
+                        }
+                    }
+                }
+                return;
+
+            case SESSION_LOST_STATE:
+                synchronized(mSessionLostStateLock) {
+                    if (mOnSessionLostStateListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            Log.i(TAG, "Drm session lost state event: ");
+                            mOnSessionLostStateListener.onSessionLostState(mMediaDrm,
+                                    sessionId);
                         }
                     }
                 }
@@ -619,9 +743,42 @@
         if (md == null) {
             return;
         }
-        if (md.mEventHandler != null) {
-            Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
-            md.mEventHandler.sendMessage(m);
+        switch (what) {
+            case DRM_EVENT:
+                synchronized(md.mEventLock) {
+                    if (md.mEventHandler != null) {
+                        Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
+                        md.mEventHandler.sendMessage(m);
+                    }
+                }
+                break;
+            case EXPIRATION_UPDATE:
+                synchronized(md.mExpirationUpdateLock) {
+                    if (md.mExpirationUpdateHandler != null) {
+                        Message m = md.mExpirationUpdateHandler.obtainMessage(what, obj);
+                        md.mExpirationUpdateHandler.sendMessage(m);
+                    }
+                }
+                break;
+            case KEY_STATUS_CHANGE:
+                synchronized(md.mKeyStatusChangeLock) {
+                    if (md.mKeyStatusChangeHandler != null) {
+                        Message m = md.mKeyStatusChangeHandler.obtainMessage(what, obj);
+                        md.mKeyStatusChangeHandler.sendMessage(m);
+                    }
+                }
+                break;
+            case SESSION_LOST_STATE:
+                synchronized(md.mSessionLostStateLock) {
+                    if (md.mSessionLostStateHandler != null) {
+                        Message m = md.mSessionLostStateHandler.obtainMessage(what, obj);
+                        md.mSessionLostStateHandler.sendMessage(m);
+                    }
+                }
+                break;
+            default:
+                Log.e(TAG, "Unknown message type " + what);
+                break;
         }
     }
 
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 594a224..c82b5f6 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -124,6 +124,7 @@
 public final class MediaFormat {
     public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
     public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+    public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
     public static final String MIMETYPE_VIDEO_AVC = "video/avc";
     public static final String MIMETYPE_VIDEO_HEVC = "video/hevc";
     public static final String MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 235325e..c496cf7 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -40,9 +40,8 @@
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
  * <p>
- * @hide
  */
-public class MediaItem2 implements Parcelable {
+public final class MediaItem2 implements Parcelable {
     private static final String TAG = "MediaItem2";
 
     // intentionally less than long.MAX_VALUE.
@@ -69,7 +68,6 @@
                 }
             };
 
-    // TODO: Use SessionPlayer2.UNKNOWN_TIME instead
     private static final long UNKNOWN_TIME = -1;
 
     private final long mStartPositionMs;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 0057875..fb18c3b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -25,8 +25,10 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.graphics.SurfaceTexture;
+import android.media.SubtitleController.Anchor;
+import android.media.SubtitleTrack.RenderingWidget;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -35,30 +37,19 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
-import android.util.ArrayMap;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.widget.VideoView;
-import android.graphics.SurfaceTexture;
-import android.media.AudioManager;
-import android.media.MediaDrm;
-import android.media.MediaFormat;
-import android.media.MediaTimeProvider;
-import android.media.PlaybackParams;
-import android.media.SubtitleController;
-import android.media.SubtitleController.Anchor;
-import android.media.SubtitleData;
-import android.media.SubtitleTrack.RenderingWidget;
-import android.media.SyncParams;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -72,7 +63,6 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.Runnable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -2105,9 +2095,11 @@
         mOnInfoListener = null;
         mOnVideoSizeChangedListener = null;
         mOnTimedTextListener = null;
-        if (mTimeProvider != null) {
-            mTimeProvider.close();
-            mTimeProvider = null;
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider != null) {
+                mTimeProvider.close();
+                mTimeProvider = null;
+            }
         }
         synchronized(this) {
             mSubtitleDataListenerDisabled = false;
@@ -2147,9 +2139,11 @@
         if (mSubtitleController != null) {
             mSubtitleController.reset();
         }
-        if (mTimeProvider != null) {
-            mTimeProvider.close();
-            mTimeProvider = null;
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider != null) {
+                mTimeProvider.close();
+                mTimeProvider = null;
+            }
         }
 
         stayAwake(false);
@@ -2790,12 +2784,17 @@
                 synchronized (mIndexTrackPairs) {
                     mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
                 }
-                Handler h = mTimeProvider.mEventHandler;
-                int what = TimeProvider.NOTIFY;
-                int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
-                Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, contents.getBytes());
-                Message m = h.obtainMessage(what, arg1, 0, trackData);
-                h.sendMessage(m);
+                synchronized (mTimeProviderLock) {
+                    if (mTimeProvider != null) {
+                        Handler h = mTimeProvider.mEventHandler;
+                        int what = TimeProvider.NOTIFY;
+                        int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
+                        Pair<SubtitleTrack, byte[]> trackData =
+                                Pair.create(track, contents.getBytes());
+                        Message m = h.obtainMessage(what, arg1, 0, trackData);
+                        h.sendMessage(m);
+                    }
+                }
                 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
             }
 
@@ -3020,12 +3019,17 @@
                             total += bytes;
                         }
                     }
-                    Handler h = mTimeProvider.mEventHandler;
-                    int what = TimeProvider.NOTIFY;
-                    int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
-                    Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, bos.toByteArray());
-                    Message m = h.obtainMessage(what, arg1, 0, trackData);
-                    h.sendMessage(m);
+                    synchronized (mTimeProviderLock) {
+                        if (mTimeProvider != null) {
+                            Handler h = mTimeProvider.mEventHandler;
+                            int what = TimeProvider.NOTIFY;
+                            int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
+                            Pair<SubtitleTrack, byte[]> trackData =
+                                    Pair.create(track, bos.toByteArray());
+                            Message m = h.obtainMessage(what, arg1, 0, trackData);
+                            h.sendMessage(m);
+                        }
+                    }
                     return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
                 } catch (Exception e) {
                     Log.e(TAG, e.getMessage(), e);
@@ -3308,14 +3312,17 @@
     private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
 
     private TimeProvider mTimeProvider;
+    private final Object mTimeProviderLock = new Object();
 
     /** @hide */
     @UnsupportedAppUsage
     public MediaTimeProvider getMediaTimeProvider() {
-        if (mTimeProvider == null) {
-            mTimeProvider = new TimeProvider(this);
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider == null) {
+                mTimeProvider = new TimeProvider(this);
+            }
+            return mTimeProvider;
         }
-        return mTimeProvider;
     }
 
     private class EventHandler extends Handler
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index cef6614..aa79c41 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -60,6 +60,7 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -67,6 +68,7 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -399,8 +401,7 @@
         clearSourceInfos();
 
         // Modular DRM clean up
-        mOnDrmConfigHelper = null;
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             mDrmEventCallbackRecords.clear();
         }
 
@@ -537,6 +538,19 @@
     public native long getCurrentPosition();
 
     /**
+     * Gets the duration of the current data source.
+     * Same as {@link #getDuration(DataSourceDesc)} with
+     * {@code dsd = getCurrentDataSource()}.
+     *
+     * @return the duration in milliseconds, if no duration is available
+     *         (for example, if streaming live content), -1 is returned.
+     * @throws NullPointerException if current data source is null
+     */
+    public long getDuration() {
+        return getDuration(getCurrentDataSource());
+    }
+
+    /**
      * Gets the duration of the dsd.
      *
      * @param dsd the descriptor of data source of which you want to get duration
@@ -559,6 +573,18 @@
     private native long native_getDuration(long srcId);
 
     /**
+     * Gets the buffered media source position of current data source.
+     * Same as {@link #getBufferedPosition(DataSourceDesc)} with
+     * {@code dsd = getCurrentDataSource()}.
+     *
+     * @return the current buffered media source position in milliseconds
+     * @throws NullPointerException if current data source is null
+     */
+    public long getBufferedPosition() {
+        return getBufferedPosition(getCurrentDataSource());
+    }
+
+    /**
      * Gets the buffered media source position of given dsd.
      * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
      * has already been played indicates that the next 3000 milliseconds of the
@@ -750,7 +776,7 @@
                 }
                 boolean hasError = false;
                 for (DataSourceDesc dsd : dsds) {
-                    if (dsd != null) {
+                    if (dsd == null) {
                         hasError = true;
                         continue;
                     }
@@ -1309,11 +1335,11 @@
         return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) {
             @Override
             void process() {
-                boolean washeld = false;
+                boolean wasHeld = false;
 
                 if (mWakeLock != null) {
                     if (mWakeLock.isHeld()) {
-                        washeld = true;
+                        wasHeld = true;
                         mWakeLock.release();
                     }
                 }
@@ -1321,7 +1347,7 @@
                 mWakeLock = wakeLock;
                 if (mWakeLock != null) {
                     mWakeLock.setReferenceCounted(false);
-                    if (washeld) {
+                    if (wasHeld) {
                         mWakeLock.acquire();
                     }
                 }
@@ -2015,6 +2041,21 @@
     };
 
     /**
+     * Returns a List of track information of current data source.
+     * Same as {@link #getTrackInfo(DataSourceDesc)} with
+     * {@code dsd = getCurrentDataSource()}.
+     *
+     * @return List of track info. The total number of tracks is the array length.
+     * Must be called again if an external timed text source has been added after
+     * addTimedTextSource method is called.
+     * @throws IllegalStateException if it is called in an invalid state.
+     * @throws NullPointerException if current data source is null
+     */
+    public @NonNull List<TrackInfo> getTrackInfo() {
+        return getTrackInfo(getCurrentDataSource());
+    }
+
+    /**
      * Returns a List of track information.
      *
      * @param dsd the descriptor of data source of which you want to get track info
@@ -2024,7 +2065,6 @@
      * @throws IllegalStateException if it is called in an invalid state.
      * @throws NullPointerException if dsd is null
      */
-
     public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) {
         if (dsd == null) {
             throw new NullPointerException("non-null dsd is expected");
@@ -2060,9 +2100,34 @@
     }
 
     /**
-     * Returns the index of the audio, video, or subtitle track currently selected for playback,
+     * Returns the index of the audio, video, or subtitle track currently selected for playback.
      * The return value is an index into the array returned by {@link #getTrackInfo}, and can
-     * be used in calls to {@link #selectTrack} or {@link #deselectTrack}.
+     * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
+     * Same as {@link #getSelectedTrack(DataSourceDesc, int)} with
+     * {@code dsd = getCurrentDataSource()}.
+     *
+     * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
+     * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
+     * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
+     * @return index of the audio, video, or subtitle track currently selected for playback;
+     * a negative integer is returned when there is no selected track for {@code trackType} or
+     * when {@code trackType} is not one of audio, video, or subtitle.
+     * @throws IllegalStateException if called after {@link #close()}
+     * @throws NullPointerException if current data source is null
+     *
+     * @see #getTrackInfo()
+     * @see #selectTrack(int)
+     * @see #deselectTrack(int)
+     */
+    public int getSelectedTrack(int trackType) {
+        return getSelectedTrack(getCurrentDataSource(), trackType);
+    }
+
+    /**
+     * Returns the index of the audio, video, or subtitle track currently selected for playback.
+     * The return value is an index into the array returned by {@link #getTrackInfo}, and can
+     * be used in calls to {@link #selectTrack(DataSourceDesc, int)} or
+     * {@link #deselectTrack(DataSourceDesc, int)}.
      *
      * @param dsd the descriptor of data source of which you want to get selected track
      * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
@@ -2074,9 +2139,9 @@
      * @throws IllegalStateException if called after {@link #close()}
      * @throws NullPointerException if dsd is null
      *
-     * @see #getTrackInfo
-     * @see #selectTrack
-     * @see #deselectTrack
+     * @see #getTrackInfo(DataSourceDesc)
+     * @see #selectTrack(DataSourceDesc, int)
+     * @see #deselectTrack(DataSourceDesc, int)
      */
     public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) {
         if (dsd == null) {
@@ -2100,6 +2165,23 @@
     }
 
     /**
+     * Selects a track of current data source.
+     * Same as {@link #selectTrack(DataSourceDesc, int)} with
+     * {@code dsd = getCurrentDataSource()}.
+     *
+     * @param index the index of the track to be selected. The valid range of the index
+     * is 0..total number of track - 1. The total number of tracks as well as the type of
+     * each individual track can be found by calling {@link #getTrackInfo()} method.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
+     *
+     * @see MediaPlayer2#getTrackInfo()
+     */
+    // This is an asynchronous call.
+    public Object selectTrack(int index) {
+        return selectTrack(getCurrentDataSource(), index);
+    }
+
+    /**
      * Selects a track.
      * <p>
      * If a MediaPlayer2 is in invalid state, it throws an IllegalStateException exception.
@@ -2123,10 +2205,10 @@
      * @param dsd the descriptor of data source of which you want to select track
      * @param index the index of the track to be selected. The valid range of the index
      * is 0..total number of track - 1. The total number of tracks as well as the type of
-     * each individual track can be found by calling {@link #getTrackInfo} method.
+     * each individual track can be found by calling {@link #getTrackInfo(DataSourceDesc)} method.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
-     * @see MediaPlayer2#getTrackInfo
+     * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
      */
     // This is an asynchronous call.
     public Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
@@ -2139,6 +2221,23 @@
     }
 
     /**
+     * Deselect a track of current data source.
+     * Same as {@link #deselectTrack(DataSourceDesc, int)} with
+     * {@code dsd = getCurrentDataSource()}.
+     *
+     * @param index the index of the track to be deselected. The valid range of the index
+     * is 0..total number of tracks - 1. The total number of tracks as well as the type of
+     * each individual track can be found by calling {@link #getTrackInfo()} method.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
+     *
+     * @see MediaPlayer2#getTrackInfo()
+     */
+    // This is an asynchronous call.
+    public Object deselectTrack(int index) {
+        return deselectTrack(getCurrentDataSource(), index);
+    }
+
+    /**
      * Deselect a track.
      * <p>
      * Currently, the track must be a timed text track and no audio or video tracks can be
@@ -2151,7 +2250,7 @@
      * each individual track can be found by calling {@link #getTrackInfo} method.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
-     * @see MediaPlayer2#getTrackInfo
+     * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
      */
     // This is an asynchronous call.
     public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
@@ -2791,7 +2890,7 @@
     }
 
     private void sendDrmEvent(final DrmEventNotifier notifier) {
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             try {
                 for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
                     cb.first.execute(() -> notifier.notify(cb.second));
@@ -2803,12 +2902,28 @@
         }
     }
 
+    private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier)
+            throws InterruptedException, ExecutionException {
+        synchronized (mDrmEventCallbackLock) {
+            mDrmEventCallbackRecords.get(0);
+            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                CompletableFuture<T> ret = new CompletableFuture<>();
+                cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second)));
+                return ret.get();
+            }
+        }
+        return null;
+    }
+
     private interface EventNotifier {
         void notify(EventCallback callback);
     }
 
-    private interface DrmEventNotifier {
-        void notify(DrmEventCallback callback);
+    private interface DrmEventNotifier<T> {
+        default void notify(DrmEventCallback callback) { }
+        default T notifyWait(DrmEventCallback callback) {
+            return null;
+        }
     }
 
     /* Do not change these values without updating their counterparts
@@ -3253,73 +3368,209 @@
     // Modular DRM begin
 
     /**
-     * Interface definition of a callback to be invoked when the app
-     * can do DRM configuration (get/set properties) before the session
-     * is opened. This facilitates configuration of the properties, like
-     * 'securityLevel', which has to be set after DRM scheme creation but
-     * before the DRM session is opened.
+     * An immutable structure per {@link DataSourceDesc} with settings required to initiate a DRM
+     * protected playback session.
      *
-     * The only allowed DRM calls in this listener are
-     * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)}
-     * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}.
-     * @hide
+     * @see DrmPreparationInfo.Builder
      */
-    public interface OnDrmConfigHelper {
+    public static final class DrmPreparationInfo {
+
         /**
-         * Called to give the app the opportunity to configure DRM before the session is created
-         *
-         * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
+         * Mutable builder to create a {@link MediaPlayer2.DrmPreparationInfo} object.
          */
-        public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd);
-    }
+        public static final class Builder {
 
-    /**
-     * Register a callback to be invoked for configuration of the DRM object before
-     * the session is created.
-     * The callback will be invoked synchronously during the execution
-     * of {@link #prepareDrm}.
-     *
-     * @param listener the callback that will be run
-     * @hide
-     */
-    // This is a synchronous call.
-    public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
-        mOnDrmConfigHelper = listener;
-    }
+            private UUID mUUID;
+            private byte[] mKeySetId;
+            private byte[] mInitData;
+            private String mMimeType;
+            private int mKeyType;
+            private Map<String, String> mOptionalParameters;
 
-    private OnDrmConfigHelper mOnDrmConfigHelper;
+            /**
+             * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved from
+             * the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}.
+             *
+             * @param uuid of selected crypto scheme
+             * @return this
+             */
+            public Builder setUuid(@NonNull UUID uuid) {
+                this.mUUID = uuid;
+                return this;
+            }
+
+            /**
+             * Set identifier of a persisted offline key obtained from
+             * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared(MediaPlayer2, DataSourceDesc, int, byte[])}.
+             *
+             * A {@code keySetId} can be used to restore persisted offline keys into a new playback
+             * session of a DRM protected data source. When {@code keySetId} is set, {@code initData},
+             * {@code mimeType}, {@code keyType}, {@code optionalParameters} are ignored.
+             *
+             * @param keySetId identifier of a persisted offline key
+             * @return this
+             */
+            public Builder setKeySetId(@Nullable byte[] keySetId) {
+                this.mKeySetId = keySetId;
+                return this;
+            }
+
+            /**
+             * Set container-specific DRM initialization data. Its meaning is interpreted based on
+             * {@code mimeType}. For example, it could contain the content ID, key ID or other data
+             * obtained from the content metadata that is required to generate a
+             * {@link MediaDrm.KeyRequest}.
+             *
+             * @param initData container-specific DRM initialization data
+             * @return this
+             */
+            public Builder setInitData(@Nullable byte[] initData) {
+                this.mInitData = initData;
+                return this;
+            }
+
+            /**
+             * Set mime type of the content
+             *
+             * @param mimeType mime type to the content
+             * @return this
+             */
+            public Builder setMimeType(@Nullable String mimeType) {
+                this.mMimeType = mimeType;
+                return this;
+            }
+
+            /**
+             * Set type of the key request. The request may be to acquire keys
+             * for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content,
+             * {@link MediaDrm#KEY_TYPE_OFFLINE}. Releasing previously acquired keys
+             * ({@link MediaDrm#KEY_TYPE_RELEASE}) is not allowed.
+             *
+             * @param keyType type of the key request
+             * @return this
+             */
+            public Builder setKeyType(@MediaPlayer2.MediaDrmKeyType int keyType) {
+                this.mKeyType = keyType;
+                return this;
+            }
+
+            /**
+             * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent to
+             * the license server.
+             *
+             * @param optionalParameters optional parameters to be included in a key request
+             * @return this
+             */
+            public Builder setOptionalParameters(
+                    @Nullable Map<String, String> optionalParameters) {
+                this.mOptionalParameters = optionalParameters;
+                return this;
+            }
+
+            /**
+             * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the settings of this builder
+             */
+            public MediaPlayer2.DrmPreparationInfo build() {
+                return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType, mKeyType,
+                        mOptionalParameters);
+            }
+
+        }
+
+        private final UUID mUUID;
+        private final byte[] mKeySetId;
+        private final byte[] mInitData;
+        private final String mMimeType;
+        private final int mKeyType;
+        private final Map<String, String> mOptionalParameters;
+
+        private DrmPreparationInfo(UUID mUUID, byte[] mKeySetId, byte[] mInitData, String mMimeType,
+                int mKeyType, Map<String, String> optionalParameters) {
+            this.mUUID = mUUID;
+            this.mKeySetId = mKeySetId;
+            this.mInitData = mInitData;
+            this.mMimeType = mMimeType;
+            this.mKeyType = mKeyType;
+            this.mOptionalParameters = optionalParameters;
+        }
+
+    }
 
     /**
      * Interface definition for callbacks to be invoked when the player has the corresponding
      * DRM events.
-     * @hide
      */
     public static class DrmEventCallback {
         /**
-         * Called to indicate DRM info is available
+         * Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that
+         * bundles DRM initialization parameters.
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
-         * @param drmInfo DRM info of the source including PSSH, and subset
-         *                of crypto schemes supported by this device
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param drmInfo DRM info of the source including PSSH, and subset of crypto schemes
+         *        supported by this device
+         * @return a {@link DrmPreparationInfo} object to initialize DRM playback, or null to skip
+         *         DRM initialization
          */
-        public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { }
+        public DrmPreparationInfo onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+            return null;
+        }
 
         /**
-         * Called to notify the client that {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}
-         * is finished and ready for key request/response.
+         * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source
+         * {@code dsd}
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
+         * @param dsd the {@link DataSourceDesc} of this data source
          * @param status the result of DRM preparation.
+         * @param keySetId optional identifier that can be used to restore DRM playback initiated
+         *        with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request.
+         *
+         * @see DrmPreparationInfo.Builder#setKeySetId(byte[])
          */
-        public void onDrmPrepared(
-                MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
+        public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { }
+
+        /**
+         * Called to give the app the opportunity to configure DRM before the session is created.
+         *
+         * This facilitates configuration of the properties, like 'securityLevel', which
+         * has to be set after DRM scheme creation but before the DRM session is opened.
+         *
+         * The only allowed DRM calls in this listener are
+         * {@link MediaDrm#getPropertyString(String)},
+         * {@link MediaDrm#getPropertyByteArray(String)},
+         * {@link MediaDrm#setPropertyString(String, String)},
+         * {@link MediaDrm#setPropertyByteArray(String, byte[])},
+         * {@link MediaDrm#setOnExpirationUpdateListener},
+         * and {@link MediaDrm#setOnKeyStatusChangeListener}.
+         *
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param drm handle to get/set DRM properties and listeners for this data source
+         */
+        public void onDrmConfig(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull MediaDrm drm) { }
+
+        /**
+         * Called to indicate the DRM session for {@code dsd} is ready for key request/response
+         *
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param request a {@link MediaDrm.KeyRequest} prepared using the
+         *        {@link DrmPreparationInfo} returned from
+         *        {@link #onDrmInfo(MediaPlayer2, DataSourceDesc, DrmInfo)}
+         * @return the response to {@code request} (from license server)
+         */
+        public byte[] onDrmKeyRequest(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull MediaDrm.KeyRequest request) {
+            return null;
+        }
+
     }
 
-    private final Object mDrmEventCbLock = new Object();
-    private ArrayList<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
+    private final Object mDrmEventCallbackLock = new Object();
+    private List<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
             new ArrayList<Pair<Executor, DrmEventCallback>>();
 
     /**
@@ -3327,10 +3578,9 @@
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
-     * @hide
      */
     // This is a synchronous call.
-    public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback) {
         if (eventCallback == null) {
             throw new IllegalArgumentException("Illegal null EventCallback");
@@ -3339,8 +3589,9 @@
             throw new IllegalArgumentException(
                     "Illegal null Executor for the EventCallback");
         }
-        synchronized (mDrmEventCbLock) {
-            mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
+        synchronized (mDrmEventCallbackLock) {
+            mDrmEventCallbackRecords = Collections.singletonList(
+                    new Pair<Executor, DrmEventCallback>(executor, eventCallback));
         }
     }
 
@@ -3352,7 +3603,7 @@
      */
     // This is a synchronous call.
     public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
                 if (cb.second == eventCallback) {
                     mDrmEventCallbackRecords.remove(cb);
@@ -3466,7 +3717,7 @@
     /**
      * Prepares the DRM for the given data source
      * <p>
-     * If {@link OnDrmConfigHelper} is registered, it will be called during
+     * If {@link DrmEventCallback} is registered, it will be called during
      * preparation to allow configuration of the DRM properties before opening the
      * DRM session. It should be used only for a series of
      * {@link #getDrmPropertyString(DataSourceDesc, String)} and
@@ -3489,8 +3740,7 @@
      * @param dsd The DRM protected data source
      *
      * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
-     * from the source through {@link #getDrmInfo(DataSourceDesc)} or registering a
-     * {@link DrmEventCallback#onDrmInfo}.
+     * from the source listening to {@link DrmEventCallback#onDrmInfo}.
      *
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      * @hide
@@ -3563,8 +3813,8 @@
                     sendDrmEvent(new DrmEventNotifier() {
                         @Override
                         public void notify(DrmEventCallback callback) {
-                            callback.onDrmPrepared(
-                                    MediaPlayer2.this, dsd, prepareDrmStatus);
+                            callback.onDrmPrepared(MediaPlayer2.this, dsd, prepareDrmStatus,
+                                    /* TODO: keySetId */ null);
                         }
                     });
 
@@ -3778,7 +4028,6 @@
 
     /**
      * Encapsulates the DRM properties of the source.
-     * @hide
      */
     public static final class DrmInfo {
         private Map<UUID, byte[]> mMapPssh;
@@ -3915,10 +4164,8 @@
     };  // DrmInfo
 
     /**
-     * Thrown when a DRM method is called before preparing a DRM scheme through
-     * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}.
+     * Thrown when a DRM method is called when there is no active DRM session.
      * Extends MediaDrm.MediaDrmException
-     * @hide
      */
     public static final class NoDrmSchemeException extends MediaDrmException {
         public NoDrmSchemeException(String detailMessage) {
@@ -4193,9 +4440,9 @@
         }
 
         void prepare(UUID uuid) throws UnsupportedSchemeException,
-                ResourceBusyException, NotProvisionedException {
-            final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
-            Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+                ResourceBusyException, NotProvisionedException, InterruptedException,
+                ExecutionException {
+            Log.v(TAG, "prepareDrm: uuid: " + uuid);
 
             synchronized (this) {
                 if (mActiveDrmUUID != null) {
@@ -4236,9 +4483,13 @@
             }  // synchronized
 
             // call the callback outside the lock
-            if (onDrmConfigHelper != null)  {
-                onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
-            }
+            sendDrmEventWait(new DrmEventNotifier<Void>() {
+                @Override
+                public Void notifyWait(DrmEventCallback callback) {
+                    callback.onDrmConfig(MediaPlayer2.this, mDSD, mDrmObj);
+                    return null;
+                }
+            });
 
             synchronized (this) {
                 mDrmConfigAllowed = false;
@@ -4408,7 +4659,7 @@
                 @Override
                 public void notify(DrmEventCallback callback) {
                     callback.onDrmPrepared(
-                            MediaPlayer2.this, mDSD, finalStatus);
+                            MediaPlayer2.this, mDSD, finalStatus, /* TODO: keySetId */ null);
                 }
             });
 
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index b160029..1304afe 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -302,6 +302,34 @@
          *  {@link #DEFAULT} otherwise. */
         public static final int UNPROCESSED = 9;
 
+
+        /**
+         * Source for capturing audio meant to be processed in real time and played back for live
+         * performance (e.g karaoke).
+         * <p>
+         * The capture path will minimize latency and coupling with
+         * playback path.
+         * </p>
+         */
+        public static final int VOICE_PERFORMANCE = 10;
+
+        /**
+         * Source for an echo canceller to capture the reference signal to be cancelled.
+         * <p>
+         * The echo reference signal will be captured as close as possible to the DAC in order
+         * to include all post processing applied to the playback path.
+         * </p><p>
+         * Capturing the echo reference requires the
+         * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
+         * This permission is reserved for use by system components and is not available to
+         * third-party applications.
+         * </p>
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)
+        public static final int ECHO_REFERENCE = 1997;
+
         /**
          * Audio source for capturing broadcast radio tuner output.
          * @hide
@@ -343,6 +371,7 @@
         case AudioSource.VOICE_COMMUNICATION:
         //case REMOTE_SUBMIX:  considered "system" as it requires system permissions
         case AudioSource.UNPROCESSED:
+        case AudioSource.VOICE_PERFORMANCE:
             return false;
         default:
             return true;
@@ -372,6 +401,10 @@
             return "REMOTE_SUBMIX";
         case AudioSource.UNPROCESSED:
             return "UNPROCESSED";
+        case AudioSource.ECHO_REFERENCE:
+            return "ECHO_REFERENCE";
+        case AudioSource.VOICE_PERFORMANCE:
+            return "VOICE_PERFORMANCE";
         case AudioSource.RADIO_TUNER:
             return "RADIO_TUNER";
         case AudioSource.HOTWORD:
@@ -456,6 +489,9 @@
         /** VP8/VORBIS data in a WEBM container */
         public static final int WEBM = 9;
 
+        /** @hide HEIC data in a HEIF container */
+        public static final int HEIF = 10;
+
         /** Opus data in a Ogg container */
         public static final int OGG = 11;
     };
@@ -521,7 +557,7 @@
      * @see android.media.MediaRecorder.AudioSource
      */
     public static final int getAudioSourceMax() {
-        return AudioSource.UNPROCESSED;
+        return AudioSource.VOICE_PERFORMANCE;
     }
 
     /**
@@ -699,6 +735,12 @@
      * is no guarantee that the recorder will have stopped by the time the
      * listener is notified.
      *
+     * <p>When using MPEG-4 container ({@link #setOutputFormat(int)} with
+     * {@link OutputFormat#MPEG_4}), it is recommended to set maximum duration that fits the use
+     * case. Setting a larger than required duration may result in a larger than needed output file
+     * because of space reserved for MOOV box expecting large movie data in this recording session.
+     *  Unused space of MOOV box is turned into FREE box in the output file.</p>
+     *
      * @param max_duration_ms the maximum duration in ms (if zero or negative, disables the duration limit)
      *
      */
@@ -714,6 +756,12 @@
      * is no guarantee that the recorder will have stopped by the time the
      * listener is notified.
      *
+     * <p>When using MPEG-4 container ({@link #setOutputFormat(int)} with
+     * {@link OutputFormat#MPEG_4}), it is recommended to set maximum filesize that fits the use
+     * case. Setting a larger than required filesize may result in a larger than needed output file
+     * because of space reserved for MOOV box expecting large movie data in this recording session.
+     * Unused space of MOOV box is turned into FREE box in the output file.</p>
+     *
      * @param max_filesize_bytes the maximum filesize in bytes (if zero or negative, disables the limit)
      *
      */
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 4712cdd..3adac72 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -19,7 +19,8 @@
 import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
 import static android.media.Session2Token.TYPE_SESSION;
@@ -31,7 +32,6 @@
 import android.content.Intent;
 import android.media.session.MediaSessionManager;
 import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Process;
@@ -41,7 +41,6 @@
 import android.util.Log;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -56,10 +55,9 @@
  * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
- * @hide
  */
 public class MediaSession2 implements AutoCloseable {
-    static final String TAG = "MediaSession";
+    static final String TAG = "MediaSession2";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // Note: This checks the uniqueness of a session ID only in a single process.
@@ -89,8 +87,9 @@
     private final Handler mResultHandler;
 
     //@GuardedBy("mLock")
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
     private boolean mClosed;
+    //@GuardedBy("mLock")
+    private boolean mPlaybackActive;
 
     MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
             @NonNull Executor callbackExecutor, @NonNull SessionCallback callback) {
@@ -113,20 +112,25 @@
                 Context.MEDIA_SESSION_SERVICE);
         // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
         mResultHandler = new Handler(context.getMainLooper());
+        mClosed = false;
     }
 
     @Override
     public void close() {
         try {
+            List<ControllerInfo> controllerInfos;
+            synchronized (mLock) {
+                if (mClosed) {
+                    return;
+                }
+                mClosed = true;
+                controllerInfos = getConnectedControllers();
+                mConnectedControllers.clear();
+                mCallback.onSessionClosed(this);
+            }
             synchronized (MediaSession2.class) {
                 SESSION_ID_LIST.remove(mSessionId);
             }
-            Collection<ControllerInfo> controllerInfos;
-            synchronized (mLock) {
-                controllerInfos = mConnectedControllers.values();
-                mConnectedControllers.clear();
-                mClosed = true;
-            }
             for (ControllerInfo info : controllerInfos) {
                 info.notifyDisconnected();
             }
@@ -161,10 +165,7 @@
         if (command == null) {
             throw new IllegalArgumentException("command shouldn't be null");
         }
-        Collection<ControllerInfo> controllerInfos;
-        synchronized (mLock) {
-            controllerInfos = mConnectedControllers.values();
-        }
+        List<ControllerInfo> controllerInfos = getConnectedControllers();
         for (ControllerInfo controller : controllerInfos) {
             controller.sendSessionCommand(command, args, null);
         }
@@ -179,6 +180,7 @@
      * @return a token which will be sent together in {@link SessionCallback#onCommandResult}
      *     when its result is received.
      */
+    @NonNull
     public Object sendSessionCommand(@NonNull ControllerInfo controller,
             @NonNull Session2Command command, @Nullable Bundle args) {
         if (controller == null) {
@@ -206,36 +208,71 @@
      * @param controller the controller to get the session command
      * @param token the token which is returned from {@link #sendSessionCommand}.
      */
-    public void cancelSessionCommand(ControllerInfo controller, Object token) {
+    public void cancelSessionCommand(@NonNull ControllerInfo controller, @NonNull Object token) {
+        if (controller == null) {
+            throw new IllegalArgumentException("controller shouldn't be null");
+        }
         if (token == null) {
             throw new IllegalArgumentException("token shouldn't be null");
         }
         controller.cancelSessionCommand(token);
     }
 
+    /**
+     * Sets whether the playback is active (i.e. playing something)
+     *
+     * @param playbackActive {@code true} if the playback active, {@code false} otherwise.
+     **/
+    public void setPlaybackActive(boolean playbackActive) {
+        synchronized (mLock) {
+            if (mPlaybackActive == playbackActive) {
+                return;
+            }
+            mPlaybackActive = playbackActive;
+        }
+        List<ControllerInfo> controllerInfos = getConnectedControllers();
+        for (ControllerInfo controller : controllerInfos) {
+            controller.notifyPlaybackActiveChanged(playbackActive);
+        }
+    }
+
+    /**
+     * Returns whehther the playback is active (i.e. playing something)
+     *
+     * @return {@code true} if the playback active, {@code false} otherwise.
+     */
+    public boolean isPlaybackActive() {
+        synchronized (mLock) {
+            return mPlaybackActive;
+        }
+    }
+
     boolean isClosed() {
         synchronized (mLock) {
             return mClosed;
         }
     }
 
-    // Called by Session2Link.onConnect
-    void onConnect(final Controller2Link controller, int seq, Bundle connectionRequest) {
-        if (controller == null || connectionRequest == null) {
-            return;
+    SessionCallback getCallback() {
+        return mCallback;
+    }
+
+    // Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect
+    void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq,
+            Bundle connectionRequest) {
+        if (callingPid == 0) {
+            // The pid here is from Binder.getCallingPid(), which can be 0 for an oneway call from
+            // the remote process. If it's the case, use PID from the connectionRequest.
+            callingPid = connectionRequest.getInt(KEY_PID);
         }
-        final int uid = Binder.getCallingUid();
-        final int callingPid = Binder.getCallingPid();
-        final long token = Binder.clearCallingIdentity();
-        // Binder.getCallingPid() can be 0 for an oneway call from the remote process.
-        // If it's the case, use PID from the ConnectionRequest.
-        final int pid = (callingPid != 0) ? callingPid : connectionRequest.getInt(KEY_PID);
-        final String pkg = connectionRequest.getString(KEY_PACKAGE_NAME);
-        try {
-            RemoteUserInfo remoteUserInfo = new RemoteUserInfo(pkg, pid, uid);
-            final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo,
-                    mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller);
-            mCallbackExecutor.execute(() -> {
+        String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME);
+
+        RemoteUserInfo remoteUserInfo = new RemoteUserInfo(callingPkg, callingPid, callingUid);
+        final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo,
+                mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller);
+        mCallbackExecutor.execute(() -> {
+            boolean connected = false;
+            try {
                 if (isClosed()) {
                     return;
                 }
@@ -244,76 +281,72 @@
                 // Don't reject connection for the request from trusted app.
                 // Otherwise server will fail to retrieve session's information to dispatch
                 // media keys to.
-                boolean accept =
-                        controllerInfo.mAllowedCommands != null || controllerInfo.isTrusted();
-                if (accept) {
-                    if (controllerInfo.mAllowedCommands == null) {
-                        // For trusted apps, send non-null allowed commands to keep
-                        // connection.
-                        controllerInfo.mAllowedCommands = new Session2CommandGroup();
+                if (controllerInfo.mAllowedCommands == null && !controllerInfo.isTrusted()) {
+                    return;
+                }
+                if (controllerInfo.mAllowedCommands == null) {
+                    // For trusted apps, send non-null allowed commands to keep
+                    // connection.
+                    controllerInfo.mAllowedCommands =
+                            new Session2CommandGroup.Builder().build();
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "Accepting connection: " + controllerInfo);
+                }
+                synchronized (mLock) {
+                    if (mConnectedControllers.containsKey(controller)) {
+                        Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
+                                + " request multiple times");
                     }
+                    mConnectedControllers.put(controller, controllerInfo);
+                }
+                // If connection is accepted, notify the current state to the controller.
+                // It's needed because we cannot call synchronous calls between
+                // session/controller.
+                Bundle connectionResult = new Bundle();
+                connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
+                connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
+                        controllerInfo.mAllowedCommands);
+                connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive());
+
+                // Double check if session is still there, because close() can be called in
+                // another thread.
+                if (isClosed()) {
+                    return;
+                }
+                controllerInfo.notifyConnected(connectionResult);
+                connected = true;
+            } finally {
+                if (!connected) {
                     if (DEBUG) {
-                        Log.d(TAG, "Accepting connection: " + controllerInfo);
+                        Log.d(TAG, "Rejecting connection or notifying that session is closed"
+                                + ", controllerInfo=" + controllerInfo);
                     }
                     synchronized (mLock) {
-                        if (mConnectedControllers.containsKey(controller)) {
-                            Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
-                                    + " request multiple times");
-                        }
-                        mConnectedControllers.put(controller, controllerInfo);
-                    }
-                    // If connection is accepted, notify the current state to the controller.
-                    // It's needed because we cannot call synchronous calls between
-                    // session/controller.
-                    Bundle connectionResult = new Bundle();
-                    connectionResult.putParcelable(KEY_SESSION2_STUB, mSessionStub);
-                    connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
-                            controllerInfo.mAllowedCommands);
-
-                    // Double check if session is still there, because close() can be called in
-                    // another thread.
-                    if (isClosed()) {
-                        return;
-                    }
-                    controllerInfo.notifyConnected(connectionResult);
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo);
+                        mConnectedControllers.remove(controller);
                     }
                     controllerInfo.notifyDisconnected();
                 }
-            });
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+            }
+        });
     }
 
     // Called by Session2Link.onDisconnect
-    void onDisconnect(final Controller2Link controller, int seq) {
-        if (controller == null) {
-            return;
-        }
+    void onDisconnect(@NonNull final Controller2Link controller, int seq) {
         final ControllerInfo controllerInfo;
         synchronized (mLock) {
-            controllerInfo = mConnectedControllers.get(controller);
+            controllerInfo = mConnectedControllers.remove(controller);
         }
         if (controllerInfo == null) {
             return;
         }
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mCallbackExecutor.execute(() -> {
-                mCallback.onDisconnected(MediaSession2.this, controllerInfo);
-            });
-            mConnectedControllers.remove(controller);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        mCallbackExecutor.execute(() -> {
+            mCallback.onDisconnected(MediaSession2.this, controllerInfo);
+        });
     }
 
     // Called by Session2Link.onSessionCommand
-    void onSessionCommand(final Controller2Link controller, final int seq,
+    void onSessionCommand(@NonNull final Controller2Link controller, final int seq,
             final Session2Command command, final Bundle args,
             @Nullable ResultReceiver resultReceiver) {
         if (controller == null) {
@@ -328,34 +361,28 @@
         }
 
         // TODO: check allowed commands.
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                controllerInfo.addRequestedCommandSeqNumber(seq);
-            }
-
-            mCallbackExecutor.execute(() -> {
-                if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                    return;
-                }
-                Session2Command.Result result = mCallback.onSessionCommand(
-                        MediaSession2.this, controllerInfo, command, args);
-                if (resultReceiver != null) {
-                    if (result == null) {
-                        throw new RuntimeException("onSessionCommand shouldn't return null");
-                    } else {
-                        resultReceiver.send(result.getResultCode(), result.getResultData());
-                    }
-                }
-            });
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        synchronized (mLock) {
+            controllerInfo.addRequestedCommandSeqNumber(seq);
         }
+        mCallbackExecutor.execute(() -> {
+            if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
+                resultReceiver.send(RESULT_INFO_SKIPPED, null);
+                return;
+            }
+            Session2Command.Result result = mCallback.onSessionCommand(
+                    MediaSession2.this, controllerInfo, command, args);
+            if (resultReceiver != null) {
+                if (result == null) {
+                    throw new RuntimeException("onSessionCommand shouldn't return null");
+                } else {
+                    resultReceiver.send(result.getResultCode(), result.getResultData());
+                }
+            }
+        });
     }
 
     // Called by Session2Link.onCancelCommand
-    void onCancelCommand(final Controller2Link controller, final int seq) {
+    void onCancelCommand(@NonNull final Controller2Link controller, final int seq) {
         final ControllerInfo controllerInfo;
         synchronized (mLock) {
             controllerInfo = mConnectedControllers.get(controller);
@@ -363,13 +390,15 @@
         if (controllerInfo == null) {
             return;
         }
+        controllerInfo.removeRequestedCommandSeqNumber(seq);
+    }
 
-        final long token = Binder.clearCallingIdentity();
-        try {
-            controllerInfo.removeRequestedCommandSeqNumber(seq);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+    private List<ControllerInfo> getConnectedControllers() {
+        List<ControllerInfo> controllers = new ArrayList<>();
+        synchronized (mLock) {
+            controllers.addAll(mConnectedControllers.values());
         }
+        return controllers;
     }
 
     /**
@@ -464,7 +493,21 @@
             if (mId == null) {
                 mId = "";
             }
-            return new MediaSession2(mContext, mId, mSessionActivity, mCallbackExecutor, mCallback);
+            MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity,
+                    mCallbackExecutor, mCallback);
+
+            // Notify framework about the newly create session after the constructor is finished.
+            // Otherwise, framework may access the session before the initialization is finished.
+            try {
+                MediaSessionManager manager = (MediaSessionManager) mContext.getSystemService(
+                        Context.MEDIA_SESSION_SERVICE);
+                manager.notifySession2Created(session2.getSessionToken());
+            } catch (Exception e) {
+                session2.close();
+                throw e;
+            }
+
+            return session2;
         }
     }
 
@@ -543,7 +586,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (!(obj instanceof ControllerInfo)) return false;
             if (this == obj) return true;
 
@@ -555,6 +598,7 @@
         }
 
         @Override
+        @NonNull
         public String toString() {
             return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid="
                     + mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})";
@@ -580,6 +624,16 @@
             }
         }
 
+        void notifyPlaybackActiveChanged(boolean playbackActive) {
+            if (mControllerBinder == null) return;
+
+            try {
+                mControllerBinder.notifyPlaybackActiveChanged(getNextSeqNumber(), playbackActive);
+            } catch (RuntimeException e) {
+                // Controller may be died prematurely.
+            }
+        }
+
         void sendSessionCommand(Session2Command command, Bundle args,
                 ResultReceiver resultReceiver) {
             if (mControllerBinder == null) return;
@@ -641,6 +695,8 @@
      * This API is not generally intended for third party application developers.
      */
     public abstract static class SessionCallback {
+        ForegroundServiceEventCallback mForegroundServiceEventCallback;
+
         /**
          * Called when a controller is created for this session. Return allowed commands for
          * controller. By default it returns {@code null}.
@@ -678,7 +734,7 @@
          * @return the result for the session command. A runtime exception will be thrown if null
          *         is returned.
          */
-        @NonNull
+        @Nullable
         public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Session2Command command,
                 @Nullable Bundle args) {
@@ -697,5 +753,19 @@
         public void onCommandResult(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Object token,
                 @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
+
+        final void onSessionClosed(MediaSession2 session) {
+            if (mForegroundServiceEventCallback != null) {
+                mForegroundServiceEventCallback.onSessionClosed(session);
+            }
+        }
+
+        void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
+            mForegroundServiceEventCallback = callback;
+        }
+
+        abstract static class ForegroundServiceEventCallback {
+            public void onSessionClosed(MediaSession2 session) {}
+        }
     }
 }
diff --git a/media/java/android/media/MediaSession2Service.java b/media/java/android/media/MediaSession2Service.java
new file mode 100644
index 0000000..8fb00fe
--- /dev/null
+++ b/media/java/android/media/MediaSession2Service.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2019 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.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Service containing {@link MediaSession2}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * @hide
+ */
+// TODO: Unhide
+// TODO: Add onUpdateNotification(), and calls it to get Notification for startForegroundService()
+//       when a session's player state becomes playing.
+public abstract class MediaSession2Service extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
+
+    private static final String TAG = "MediaSession2Service";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private Map<String, MediaSession2> mSessions = new ArrayMap<>();
+
+    private MediaSession2ServiceStub mStub;
+
+    /**
+     * Called by the system when the service is first created. Do not call this method directly.
+     * <p>
+     * Override this method if you need your own initialization. Derived classes MUST call through
+     * to the super class's implementation of this method.
+     */
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mStub = new MediaSession2ServiceStub(this);
+    }
+
+    @CallSuper
+    @Override
+    @Nullable
+    public IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mStub;
+        }
+        return null;
+    }
+
+    @CallSuper
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        // TODO: Dispatch media key events to the primary session.
+        return START_STICKY;
+    }
+
+    /**
+     * Called by the system to notify that it is no longer used and is being removed. Do not call
+     * this method directly.
+     * <p>
+     * Override this method if you need your own clean up. Derived classes MUST call through
+     * to the super class's implementation of this method.
+     */
+    @CallSuper
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        synchronized (mLock) {
+            for (MediaSession2 session : mSessions.values()) {
+                session.getCallback().setForegroundServiceEventCallback(null);
+            }
+            mSessions.clear();
+        }
+        mStub.close();
+    }
+
+    /**
+     * Called when a {@link MediaController2} is created with the this service's
+     * {@link Session2Token}. Return the primary session for telling the controller which session to
+     * connect.
+     * <p>
+     * Primary session is the highest priority session that this service manages. Here are some
+     * recommendations of the primary session.
+     * <ol>
+     * <li>When there's no {@link MediaSession2}, create and return a new session. Resume the
+     * playback that the app has the lastly played with the new session. The behavior is what
+     * framework expects when the framework sends key events to the service.</li>
+     * <li>When there's multiple {@link MediaSession2}s, pick the session that has the lastly
+     * started the playback. This is the same way as the framework prioritize sessions to receive
+     * media key events.</li>
+     * </ol>
+     * <p>
+     * Session returned here will be added to this service automatically. You don't need to call
+     * {@link #addSession(MediaSession2)} for that.
+     * <p>
+     * Session service will accept or reject the connection with the
+     * {@link MediaSession2.SessionCallback} in the session returned here.
+     * <p>
+     * This method is always called on the main thread.
+     *
+     * @return a new session
+     * @see MediaSession2.Builder
+     * @see #getSessions()
+     */
+    @NonNull
+    public abstract MediaSession2 onGetPrimarySession();
+
+    /**
+     * Adds a session to this service.
+     * <p>
+     * Added session will be removed automatically when it's closed, or removed when
+     * {@link #removeSession} is called.
+     *
+     * @param session a session to be added.
+     * @see #removeSession(MediaSession2)
+     */
+    public final void addSession(@NonNull MediaSession2 session) {
+        if (session == null) {
+            throw new IllegalArgumentException("session shouldn't be null");
+        }
+        if (session.isClosed()) {
+            throw new IllegalArgumentException("session is already closed");
+        }
+        synchronized (mLock) {
+            MediaSession2 previousSession = mSessions.get(session.getSessionId());
+            if (previousSession != session) {
+                if (previousSession != null) {
+                    Log.w(TAG, "Session ID should be unique, ID=" + session.getSessionId()
+                            + ", previous=" + previousSession + ", session=" + session);
+                }
+                return;
+            }
+            mSessions.put(session.getSessionId(), session);
+            session.getCallback().setForegroundServiceEventCallback(
+                    new MediaSession2.SessionCallback.ForegroundServiceEventCallback() {
+                        @Override
+                        public void onSessionClosed(MediaSession2 session) {
+                            removeSession(session);
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Removes a session from this service.
+     *
+     * @param session a session to be removed.
+     * @see #addSession(MediaSession2)
+     */
+    public final void removeSession(@NonNull MediaSession2 session) {
+        if (session == null) {
+            throw new IllegalArgumentException("session shouldn't be null");
+        }
+        synchronized (mLock) {
+            mSessions.remove(session.getSessionId());
+        }
+    }
+
+    /**
+     * Gets the list of {@link MediaSession2}s that you've added to this service.
+     *
+     * @return sessions
+     */
+    public final @NonNull List<MediaSession2> getSessions() {
+        List<MediaSession2> list = new ArrayList<>();
+        synchronized (mLock) {
+            list.addAll(mSessions.values());
+        }
+        return list;
+    }
+
+    private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub
+            implements AutoCloseable {
+        final WeakReference<MediaSession2Service> mService;
+        final Handler mHandler;
+
+        MediaSession2ServiceStub(MediaSession2Service service) {
+            mService = new WeakReference<>(service);
+            mHandler = new Handler(service.getMainLooper());
+        }
+
+        @Override
+        public void connect(Controller2Link caller, int seq, Bundle connectionRequest) {
+            if (mService.get() == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Service is already destroyed");
+                }
+                return;
+            }
+            if (caller == null || connectionRequest == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Ignoring calls with illegal arguments, caller=" + caller
+                            + ", connectionRequest=" + connectionRequest);
+                }
+                return;
+            }
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mHandler.post(() -> {
+                    boolean shouldNotifyDisconnected = true;
+                    try {
+                        final MediaSession2Service service = mService.get();
+                        if (service == null) {
+                            if (DEBUG) {
+                                Log.d(TAG, "Service isn't available");
+                            }
+                            return;
+                        }
+                        if (DEBUG) {
+                            Log.d(TAG, "Handling incoming connection request from the"
+                                    + " controller, controller=" + caller + ", uid=" + uid);
+                        }
+                        final MediaSession2 session;
+                        session = service.onGetPrimarySession();
+                        service.addSession(session);
+                        shouldNotifyDisconnected = false;
+                        session.onConnect(caller, pid, uid, seq, connectionRequest);
+                    } catch (Exception e) {
+                        // Don't propagate exception in service to the controller.
+                        Log.w(TAG, "Failed to add a session to session service", e);
+                    } finally {
+                        // Trick to call onDisconnected() in one place.
+                        if (shouldNotifyDisconnected) {
+                            if (DEBUG) {
+                                Log.d(TAG, "Service has destroyed prematurely."
+                                        + " Rejecting connection");
+                            }
+                            try {
+                                caller.notifyDisconnected(0);
+                            } catch (RuntimeException e) {
+                                // Controller may be died prematurely.
+                                // Not an issue because we'll ignore it anyway.
+                            }
+                        }
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void close() {
+            mHandler.removeCallbacksAndMessages(null);
+            mService.clear();
+        }
+    }
+}
diff --git a/media/java/android/media/MicrophoneDirection.java b/media/java/android/media/MicrophoneDirection.java
new file mode 100644
index 0000000..99201c0
--- /dev/null
+++ b/media/java/android/media/MicrophoneDirection.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+public interface MicrophoneDirection {
+    /**
+     * @hide
+     */
+    int MIC_DIRECTION_UNSPECIFIED = 0;
+
+    /**
+     * @hide
+     */
+    int MIC_DIRECTION_FRONT = 1;
+
+    /**
+     * @hide
+     */
+    int MIC_DIRECTION_BACK = 2;
+
+    /**
+     * @hide
+     */
+    int MIC_DIRECTION_EXTERNAL = 3;
+
+    /**
+     * Specifies the logical microphone (for processing).
+     *
+     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
+     * @return retval OK if the call is successful, an error code otherwise.
+     * @hide
+     */
+    int setMicrophoneDirection(int direction);
+
+    /**
+     * Specifies the zoom factor (i.e. the field dimension) for the selected microphone
+     * (for processing). The selected microphone is determined by the use-case for the stream.
+     *
+     * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
+     * though 0 (no zoom) to 1 (maximum zoom).
+     * @return retval OK if the call is successful, an error code otherwise.
+     * @hide
+     */
+    int setMicrophoneFieldDimension(float zoom);
+}
diff --git a/media/java/android/media/Session2Command.java b/media/java/android/media/Session2Command.java
index d2a5aae..8b285f2 100644
--- a/media/java/android/media/Session2Command.java
+++ b/media/java/android/media/Session2Command.java
@@ -36,8 +36,6 @@
  * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
- * </p>
- * @hide
  */
 public final class Session2Command implements Parcelable {
     /**
@@ -151,14 +149,17 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("parcel shouldn't be null");
+        }
         dest.writeInt(mCommandCode);
         dest.writeString(mCustomCommand);
         dest.writeBundle(mExtras);
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof Session2Command)) {
             return false;
         }
@@ -185,7 +186,7 @@
          * @param resultCode result code
          * @param resultData result data
          */
-        public Result(int resultCode, Bundle resultData) {
+        public Result(int resultCode, @Nullable Bundle resultData) {
             mResultCode = resultCode;
             mResultData = resultData;
         }
@@ -200,6 +201,7 @@
         /**
          * Returns the result data.
          */
+        @Nullable
         public Bundle getResultData() {
             return mResultData;
         }
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
index 122dfb1..a189c26 100644
--- a/media/java/android/media/Session2CommandGroup.java
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -35,7 +35,6 @@
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
  * </p>
- * @hide
  */
 public final class Session2CommandGroup implements Parcelable {
     private static final String TAG = "Session2CommandGroup";
@@ -56,16 +55,12 @@
     Set<Session2Command> mCommands = new HashSet<>();
 
     /**
-     * Default Constructor.
-     */
-    public Session2CommandGroup() {}
-
-    /**
      * Creates a new Session2CommandGroup with commands copied from another object.
      *
      * @param commands The collection of commands to copy.
      */
-    public Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
         if (commands != null) {
             mCommands.addAll(commands);
         }
@@ -129,8 +124,11 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelableArray((Session2Command[]) mCommands.toArray(), 0);
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("parcel shouldn't be null");
+        }
+        dest.writeParcelableArray(mCommands.toArray(new Session2Command[0]), 0);
     }
 
     /**
@@ -149,6 +147,9 @@
          * @param commandGroup
          */
         public Builder(@NonNull Session2CommandGroup commandGroup) {
+            if (commandGroup == null) {
+                throw new IllegalArgumentException("command group shouldn't be null");
+            }
             mCommands = commandGroup.getCommands();
         }
 
diff --git a/media/java/android/media/Session2Link.java b/media/java/android/media/Session2Link.java
index 5fe558d..08664aa 100644
--- a/media/java/android/media/Session2Link.java
+++ b/media/java/android/media/Session2Link.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -145,8 +146,9 @@
     }
 
     /** Stub implementation for IMediaSession2.connect */
-    public void onConnect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-        mSession.onConnect(caller, seq, connectionRequest);
+    public void onConnect(final Controller2Link caller, int pid, int uid, int seq,
+            Bundle connectionRequest) {
+        mSession.onConnect(caller, pid, uid, seq, connectionRequest);
     }
 
     /** Stub implementation for IMediaSession2.disconnect */
@@ -168,23 +170,57 @@
     private class Session2Stub extends IMediaSession2.Stub {
         @Override
         public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-            Session2Link.this.onConnect(caller, seq, connectionRequest);
+            if (caller == null || connectionRequest == null) {
+                return;
+            }
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onConnect(caller, pid, uid, seq, connectionRequest);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void disconnect(final Controller2Link caller, int seq) {
-            Session2Link.this.onDisconnect(caller, seq);
+            if (caller == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onDisconnect(caller, seq);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void sendSessionCommand(final Controller2Link caller, final int seq,
                 final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-            Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
+            if (caller == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void cancelSessionCommand(final Controller2Link caller, final int seq) {
-            Session2Link.this.onCancelCommand(caller, seq);
+            if (caller == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onCancelCommand(caller, seq);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 }
diff --git a/media/java/android/media/Session2Token.aidl b/media/java/android/media/Session2Token.aidl
new file mode 100644
index 0000000..c5980e9
--- /dev/null
+++ b/media/java/android/media/Session2Token.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 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;
+
+parcelable Session2Token;
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index 7642faa..c7b8911 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -34,7 +34,7 @@
 import java.util.Objects;
 
 /**
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
+ * Represents an ongoing {@link MediaSession2} or a MediaSession2Service.
  * If it's representing a session service, it may not be ongoing.
  * <p>
  * This API is not generally intended for third party application developers.
@@ -45,9 +45,8 @@
  * This may be passed to apps by the session owner to allow them to create a
  * {@link MediaController2} to communicate with the session.
  * <p>
- * It can be also obtained by {@link MediaSessionManager}.
+ * It can be also obtained by {@link android.media.session.MediaSessionManager}.
  *
- * @hide
  */
 // New version of MediaSession2.Token for following reasons
 //   - Stop implementing Parcelable for updatable support
@@ -56,6 +55,7 @@
 //     This helps controller apps to keep target of dispatching media key events in uniform way.
 //     For details about the reason, see following. (Android O+)
 //         android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged
+// TODO: use @link for MediaSession2Service
 public final class Session2Token implements Parcelable {
     private static final String TAG = "Session2Token";
 
@@ -75,7 +75,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
+    @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
     public @interface TokenType {
     }
 
@@ -85,15 +85,10 @@
     public static final int TYPE_SESSION = 0;
 
     /**
-     * Type for {@link MediaSession2Service}.
+     * Type for MediaSession2Service.
      */
     public static final int TYPE_SESSION_SERVICE = 1;
 
-    /**
-     * Type for {@link MediaLibrary2Service}.
-     */
-    public static final int TYPE_LIBRARY_SERVICE = 2;
-
     private final int mUid;
     private final @TokenType int mType;
     private final String mPackageName;
@@ -102,7 +97,7 @@
     private final ComponentName mComponentName;
 
     /**
-     * Constructor for the token.
+     * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
      *
      * @param context The context.
      * @param serviceComponent The component name of the service.
@@ -119,7 +114,7 @@
         final int uid = getUid(manager, serviceComponent.getPackageName());
 
         // TODO: Uncomment below to stop hardcode type.
-        final int type = TYPE_SESSION;
+        final int type = TYPE_SESSION_SERVICE;
 //        final int type;
 //        if (isInterfaceDeclared(manager, MediaLibraryService2.SERVICE_INTERFACE,
 //                serviceComponent)) {
@@ -157,9 +152,7 @@
         mType = in.readInt();
         mPackageName = in.readString();
         mServiceName = in.readString();
-        // TODO: Uncomment below and stop hardcode mSessionLink
-        mSessionLink = null;
-        //mSessionLink = ISession.Stub.asInterface(in.readStrongBinder());
+        mSessionLink = Session2Link.CREATOR.createFromParcel(in);
         mComponentName = ComponentName.unflattenFromString(in.readString());
     }
 
@@ -169,8 +162,7 @@
         dest.writeInt(mType);
         dest.writeString(mPackageName);
         dest.writeString(mServiceName);
-        // TODO: Uncomment below
-        //dest.writeStrongBinder(mSessionLink.asBinder());
+        mSessionLink.writeToParcel(dest, flags);
         dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
     }
 
@@ -238,7 +230,6 @@
      * @return type of the token
      * @see #TYPE_SESSION
      * @see #TYPE_SESSION_SERVICE
-     * @see #TYPE_LIBRARY_SERVICE
      */
     public @TokenType int getType() {
         return mType;
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/media/java/android/media/session/ControllerCallbackLink.aidl
new file mode 100644
index 0000000..8ee8c7d
--- /dev/null
+++ b/media/java/android/media/session/ControllerCallbackLink.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019 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.session;
+
+parcelable ControllerCallbackLink;
diff --git a/media/java/android/media/session/ControllerCallbackLink.java b/media/java/android/media/session/ControllerCallbackLink.java
new file mode 100644
index 0000000..a143c9b
--- /dev/null
+++ b/media/java/android/media/session/ControllerCallbackLink.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2019 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.session;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.media.MediaMetadata;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.QueueItem;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * Handles incoming commands to {@link MediaController.Callback}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ */
+public final class ControllerCallbackLink implements Parcelable {
+    final CallbackStub mCallbackStub;
+    final ISessionControllerCallback mIControllerCallback;
+
+    /**
+     * Constructor for stub (Callee)
+     * @hide
+     */
+    @SystemApi
+    public ControllerCallbackLink(@NonNull CallbackStub callbackStub) {
+        mCallbackStub = callbackStub;
+        mIControllerCallback = new CallbackStubProxy();
+    }
+
+    /**
+     * Constructor for interface (Caller)
+     */
+    ControllerCallbackLink(Parcel in) {
+        mCallbackStub = null;
+        mIControllerCallback = ISessionControllerCallback.Stub.asInterface(in.readStrongBinder());
+    }
+
+    /**
+     * Notify controller that the connected session is destroyed.
+     */
+    public void notifySessionDestroyed() {
+        try {
+            mIControllerCallback.notifySessionDestroyed();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify controller that the connected session sends an event.
+     *
+     * @param event the name of the event
+     * @param extras the extras included with the event
+     */
+    public void notifyEvent(@NonNull String event, @Nullable Bundle extras) {
+        try {
+            mIControllerCallback.notifyEvent(event, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify controller that the current playback state is changed.
+     *
+     * @param state the new playback state
+     */
+    public void notifyPlaybackStateChanged(@Nullable PlaybackState state) {
+        try {
+            mIControllerCallback.notifyPlaybackStateChanged(state);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify controller that the current metadata is changed.
+     *
+     * @param metadata the new metadata
+     */
+    public void notifyMetadataChanged(@Nullable MediaMetadata metadata) {
+        try {
+            mIControllerCallback.notifyMetadataChanged(metadata);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify controller that the current queue is changed.
+     *
+     * @param queue the new queue
+     */
+    public void notifyQueueChanged(@Nullable List<QueueItem> queue) {
+        try {
+            mIControllerCallback.notifyQueueChanged(queue);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify controller that the current queue title is changed.
+     *
+     * @param title the new queue title
+     */
+    public void notifyQueueTitleChanged(@Nullable CharSequence title) {
+        try {
+            mIControllerCallback.notifyQueueTitleChanged(title);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify controller that the extras are changed.
+     *
+     * @param extras the new extras
+     */
+    public void notifyExtrasChanged(@Nullable Bundle extras) {
+        try {
+            mIControllerCallback.notifyExtrasChanged(extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify controller that the playback info is changed.
+     *
+     * @param info the new playback info
+     */
+    public void notifyVolumeInfoChanged(@NonNull PlaybackInfo info) {
+        try {
+            mIControllerCallback.notifyVolumeInfoChanged(info);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Gets the binder */
+    @NonNull
+    public IBinder getBinder() {
+        return mIControllerCallback.asBinder();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mIControllerCallback.asBinder());
+    }
+
+    public static final Parcelable.Creator<ControllerCallbackLink> CREATOR =
+            new Parcelable.Creator<ControllerCallbackLink>() {
+        @Override
+        public ControllerCallbackLink createFromParcel(Parcel in) {
+            return new ControllerCallbackLink(in);
+        }
+
+        @Override
+        public ControllerCallbackLink[] newArray(int size) {
+            return new ControllerCallbackLink[size];
+        }
+    };
+
+    /**
+     * Class for Stub implementation
+     * @hide
+     */
+    @SystemApi
+    public abstract static class CallbackStub {
+        /** Stub method for ISessionControllerCallback.notifySessionDestroyed */
+        public void onSessionDestroyed() {
+        }
+
+        /** Stub method for ISessionControllerCallback.notifyEvent */
+        public void onEvent(@NonNull String event, @Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionControllerCallback.notifyPlaybackStateChanged */
+        public void onPlaybackStateChanged(@Nullable PlaybackState state) {
+        }
+
+        /** Stub method for ISessionControllerCallback.notifyMetadataChanged */
+        public void onMetadataChanged(@Nullable MediaMetadata metadata) {
+        }
+
+        /** Stub method for ISessionControllerCallback.notifyQueueChanged */
+        public void onQueueChanged(@Nullable List<QueueItem> queue) {
+        }
+
+        /** Stub method for ISessionControllerCallback.notifyQueueTitleChanged */
+        public void onQueueTitleChanged(@Nullable CharSequence title) {
+        }
+
+        /** Stub method for ISessionControllerCallback.notifyExtrasChanged */
+        public void onExtrasChanged(@Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionControllerCallback.notifyVolumeInfoChanged */
+        public void onVolumeInfoChanged(@NonNull PlaybackInfo info) {
+        }
+    }
+
+    private class CallbackStubProxy extends ISessionControllerCallback.Stub {
+        @Override
+        public void notifyEvent(String event, Bundle extras) {
+            mCallbackStub.onEvent(event, extras);
+        }
+
+        @Override
+        public void notifySessionDestroyed() {
+            mCallbackStub.onSessionDestroyed();
+        }
+
+        @Override
+        public void notifyPlaybackStateChanged(PlaybackState state) {
+            mCallbackStub.onPlaybackStateChanged(state);
+        }
+
+        @Override
+        public void notifyMetadataChanged(MediaMetadata metadata) {
+            mCallbackStub.onMetadataChanged(metadata);
+        }
+
+        @Override
+        public void notifyQueueChanged(List<QueueItem> queue) {
+            mCallbackStub.onQueueChanged(queue);
+        }
+
+        @Override
+        public void notifyQueueTitleChanged(CharSequence title) {
+            mCallbackStub.onQueueTitleChanged(title);
+        }
+
+        @Override
+        public void notifyExtrasChanged(Bundle extras) {
+            mCallbackStub.onExtrasChanged(extras);
+        }
+
+        @Override
+        public void notifyVolumeInfoChanged(PlaybackInfo info) {
+            mCallbackStub.onVolumeInfoChanged(info);
+        }
+    }
+}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index bfc05fa..1524ad9 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,7 +16,6 @@
 package android.media.session;
 
 import android.app.PendingIntent;
-import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.MediaMetadata;
 import android.media.session.ISessionController;
@@ -41,7 +40,8 @@
     // These commands are for the TransportPerformer
     void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription);
     void setPlaybackState(in PlaybackState state);
-    void setQueue(in ParceledListSlice queue);
+    // TODO(b/122432476): Replace List with MediaParceledListSlice
+    void setQueue(in List<MediaSession.QueueItem> queue);
     void setQueueTitle(CharSequence title);
     void setExtras(in Bundle extras);
     void setRatingType(int type);
diff --git a/media/java/android/media/session/ISession2TokensListener.aidl b/media/java/android/media/session/ISession2TokensListener.aidl
new file mode 100644
index 0000000..7d1a4aa
--- /dev/null
+++ b/media/java/android/media/session/ISession2TokensListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 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.session;
+
+import android.media.Session2Token;
+
+/**
+ * Listens for changes to the list of session2 tokens.
+ * @hide
+ */
+oneway interface ISession2TokensListener {
+    void onSession2TokensChanged(in List<Session2Token> tokens);
+}
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 626338d..9b86bfc 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -17,7 +17,7 @@
 
 import android.content.Intent;
 import android.media.Rating;
-import android.media.session.ISessionControllerCallback;
+import android.media.session.ControllerCallbackLink;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -26,46 +26,46 @@
  * @hide
  */
 oneway interface ISessionCallback {
-    void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller,
+    void notifyCommand(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             String command, in Bundle args, in ResultReceiver cb);
-    void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
+    void notifyMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
             int sequenceNumber, in ResultReceiver cb);
-    void onMediaButtonFromController(String packageName, int pid, int uid,
-            ISessionControllerCallback caller, in Intent mediaButtonIntent);
+    void notifyMediaButtonFromController(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, in Intent mediaButtonIntent);
 
     // These callbacks are for the TransportPerformer
-    void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onPrepareFromMediaId(String packageName, int pid, int uid,
-            ISessionControllerCallback caller, String mediaId, in Bundle extras);
-    void onPrepareFromSearch(String packageName, int pid, int uid,
-            ISessionControllerCallback caller, String query, in Bundle extras);
-    void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
+    void notifyPrepare(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifyPrepareFromMediaId(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, String mediaId, in Bundle extras);
+    void notifyPrepareFromSearch(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, String query, in Bundle extras);
+    void notifyPrepareFromUri(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, in Uri uri, in Bundle extras);
+    void notifyPlay(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifyPlayFromMediaId(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, String mediaId, in Bundle extras);
+    void notifyPlayFromSearch(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, String query, in Bundle extras);
+    void notifyPlayFromUri(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             in Uri uri, in Bundle extras);
-    void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller,
-            String mediaId, in Bundle extras);
-    void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller,
-            String query, in Bundle extras);
-    void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
-            in Uri uri, in Bundle extras);
-    void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller,
+    void notifySkipToTrack(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             long id);
-    void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller);
-    void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
+    void notifyPause(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifyStop(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifyNext(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifyPrevious(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifyFastForward(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifyRewind(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+    void notifySeekTo(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             long pos);
-    void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+    void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             in Rating rating);
-    void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller,
+    void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             String action, in Bundle args);
 
     // These callbacks are for volume handling
-    void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller,
+    void notifyAdjustVolume(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             int direction);
-    void onSetVolumeTo(String packageName, int pid, int uid,
-            ISessionControllerCallback caller, int value);
+    void notifySetVolumeTo(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, int value);
 }
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e61bf5b..2ba09fd 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -17,12 +17,11 @@
 
 import android.app.PendingIntent;
 import android.content.Intent;
-import android.content.pm.ParceledListSlice;
 import android.media.MediaMetadata;
 import android.media.Rating;
-import android.media.session.ISessionControllerCallback;
+import android.media.session.ControllerCallbackLink;
+import android.media.session.MediaController;
 import android.media.session.MediaSession;
-import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
 import android.net.Uri;
 import android.os.Bundle;
@@ -36,53 +35,53 @@
  * @hide
  */
 interface ISessionController {
-    void sendCommand(String packageName, ISessionControllerCallback caller,
+    void sendCommand(String packageName, in ControllerCallbackLink caller,
             String command, in Bundle args, in ResultReceiver cb);
-    boolean sendMediaButton(String packageName, ISessionControllerCallback caller,
+    boolean sendMediaButton(String packageName, in ControllerCallbackLink caller,
             boolean asSystemService, in KeyEvent mediaButton);
-    void registerCallbackListener(String packageName, ISessionControllerCallback cb);
-    void unregisterCallbackListener(ISessionControllerCallback cb);
+    void registerCallbackListener(String packageName, in ControllerCallbackLink cb);
+    void unregisterCallbackListener(in ControllerCallbackLink cb);
     boolean isTransportControlEnabled();
     String getPackageName();
     String getTag();
     PendingIntent getLaunchPendingIntent();
     long getFlags();
-    ParcelableVolumeInfo getVolumeAttributes();
+    MediaController.PlaybackInfo getVolumeAttributes();
     void adjustVolume(String packageName, String opPackageName,
-            in ISessionControllerCallback caller, boolean asSystemService, int direction,
+            in ControllerCallbackLink caller, boolean asSystemService, int direction,
             int flags);
-    void setVolumeTo(String packageName, String opPackageName, in ISessionControllerCallback caller,
+    void setVolumeTo(String packageName, String opPackageName, in ControllerCallbackLink caller,
             int value, int flags);
 
     // These commands are for the TransportControls
-    void prepare(String packageName, ISessionControllerCallback caller);
-    void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
+    void prepare(String packageName, in ControllerCallbackLink caller);
+    void prepareFromMediaId(String packageName, in ControllerCallbackLink caller,
             String mediaId, in Bundle extras);
-    void prepareFromSearch(String packageName, ISessionControllerCallback caller,
+    void prepareFromSearch(String packageName, in ControllerCallbackLink caller,
             String string, in Bundle extras);
-    void prepareFromUri(String packageName, ISessionControllerCallback caller,
+    void prepareFromUri(String packageName, in ControllerCallbackLink caller,
             in Uri uri, in Bundle extras);
-    void play(String packageName, ISessionControllerCallback caller);
-    void playFromMediaId(String packageName, ISessionControllerCallback caller,
+    void play(String packageName, in ControllerCallbackLink caller);
+    void playFromMediaId(String packageName, in ControllerCallbackLink caller,
             String mediaId, in Bundle extras);
-    void playFromSearch(String packageName, ISessionControllerCallback caller,
+    void playFromSearch(String packageName, in ControllerCallbackLink caller,
             String string, in Bundle extras);
-    void playFromUri(String packageName, ISessionControllerCallback caller,
+    void playFromUri(String packageName, in ControllerCallbackLink caller,
             in Uri uri, in Bundle extras);
-    void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id);
-    void pause(String packageName, ISessionControllerCallback caller);
-    void stop(String packageName, ISessionControllerCallback caller);
-    void next(String packageName, ISessionControllerCallback caller);
-    void previous(String packageName, ISessionControllerCallback caller);
-    void fastForward(String packageName, ISessionControllerCallback caller);
-    void rewind(String packageName, ISessionControllerCallback caller);
-    void seekTo(String packageName, ISessionControllerCallback caller, long pos);
-    void rate(String packageName, ISessionControllerCallback caller, in Rating rating);
-    void sendCustomAction(String packageName, ISessionControllerCallback caller,
+    void skipToQueueItem(String packageName, in ControllerCallbackLink caller, long id);
+    void pause(String packageName, in ControllerCallbackLink caller);
+    void stop(String packageName, in ControllerCallbackLink caller);
+    void next(String packageName, in ControllerCallbackLink caller);
+    void previous(String packageName, in ControllerCallbackLink caller);
+    void fastForward(String packageName, in ControllerCallbackLink caller);
+    void rewind(String packageName, in ControllerCallbackLink caller);
+    void seekTo(String packageName, in ControllerCallbackLink caller, long pos);
+    void rate(String packageName, in ControllerCallbackLink caller, in Rating rating);
+    void sendCustomAction(String packageName, in ControllerCallbackLink caller,
             String action, in Bundle args);
     MediaMetadata getMetadata();
     PlaybackState getPlaybackState();
-    ParceledListSlice getQueue();
+    List<MediaSession.QueueItem> getQueue();
     CharSequence getQueueTitle();
     Bundle getExtras();
     int getRatingType();
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index cf31767..5c02e7c 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -15,25 +15,24 @@
 
 package android.media.session;
 
-import android.content.pm.ParceledListSlice;
 import android.media.MediaMetadata;
-import android.media.session.ParcelableVolumeInfo;
-import android.media.session.PlaybackState;
+import android.media.session.MediaController;
 import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
 import android.os.Bundle;
 
 /**
  * @hide
  */
 oneway interface ISessionControllerCallback {
-    void onEvent(String event, in Bundle extras);
-    void onSessionDestroyed();
+    void notifyEvent(String event, in Bundle extras);
+    void notifySessionDestroyed();
 
     // These callbacks are for the TransportController
-    void onPlaybackStateChanged(in PlaybackState state);
-    void onMetadataChanged(in MediaMetadata metadata);
-    void onQueueChanged(in ParceledListSlice queue);
-    void onQueueTitleChanged(CharSequence title);
-    void onExtrasChanged(in Bundle extras);
-    void onVolumeInfoChanged(in ParcelableVolumeInfo info);
+    void notifyPlaybackStateChanged(in PlaybackState state);
+    void notifyMetadataChanged(in MediaMetadata metadata);
+    void notifyQueueChanged(in List<MediaSession.QueueItem> queue);
+    void notifyQueueTitleChanged(CharSequence title);
+    void notifyExtrasChanged(in Bundle extras);
+    void notifyVolumeInfoChanged(in MediaController.PlaybackInfo info);
 }
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index d6c226f..46516e0 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,12 +17,14 @@
 
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
+import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
 import android.media.session.IOnVolumeKeyLongPressListener;
 import android.media.session.ISession;
-import android.media.session.ISessionCallback;
+import android.media.session.ISession2TokensListener;
+import android.media.session.SessionCallbackLink;
 import android.os.Bundle;
 import android.view.KeyEvent;
 
@@ -31,8 +33,10 @@
  * @hide
  */
 interface ISessionManager {
-    ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+    ISession createSession(String packageName, in SessionCallbackLink cb, String tag, int userId);
+    void notifySession2Created(in Session2Token sessionToken);
     List<IBinder> getSessions(in ComponentName compName, int userId);
+    List<Session2Token> getSession2Tokens(int userId);
     void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
             boolean needWakeLock);
     void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService,
@@ -42,6 +46,8 @@
     void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
             int userId);
     void removeSessionsListener(in IActiveSessionsListener listener);
+    void addSession2TokensListener(in ISession2TokensListener listener, int userId);
+    void removeSession2TokensListener(in ISession2TokensListener listener);
 
     // This is for the system volume UI only
     void setRemoteVolumeController(in IRemoteVolumeController rvc);
@@ -53,6 +59,5 @@
     void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
     void setOnMediaKeyListener(in IOnMediaKeyListener listener);
 
-    // MediaSession2
     boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
 }
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/media/java/android/media/session/MediaController.aidl
similarity index 93%
rename from media/java/android/media/session/ParcelableVolumeInfo.aidl
rename to media/java/android/media/session/MediaController.aidl
index c4250f0..17167f4 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.aidl
+++ b/media/java/android/media/session/MediaController.aidl
@@ -15,4 +15,4 @@
 
 package android.media.session;
 
-parcelable ParcelableVolumeInfo;
+parcelable MediaController.PlaybackInfo;
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 181ee53..a1b8170 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -21,17 +21,19 @@
 import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
+import android.media.session.MediaSession.QueueItem;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.text.TextUtils;
@@ -70,7 +72,8 @@
 
     private final MediaSession.Token mToken;
     private final Context mContext;
-    private final CallbackStub mCbStub = new CallbackStub(this);
+    private final ControllerCallbackLink mCbStub =
+            new ControllerCallbackLink(new CallbackStub(this));
     private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
     private final Object mLock = new Object();
 
@@ -249,10 +252,7 @@
      */
     public @Nullable List<MediaSession.QueueItem> getQueue() {
         try {
-            ParceledListSlice queue = mSessionBinder.getQueue();
-            if (queue != null) {
-                return queue.getList();
-            }
+            return mSessionBinder.getQueue();
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling getQueue.", e);
         }
@@ -327,10 +327,7 @@
      */
     public @Nullable PlaybackInfo getPlaybackInfo() {
         try {
-            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
-            return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType,
-                    result.maxVolume, result.currentVolume);
-
+            return mSessionBinder.getVolumeAttributes();
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling getAudioInfo.", e);
         }
@@ -986,15 +983,15 @@
      * Holds information about the current playback and how audio is handled for
      * this session.
      */
-    public static final class PlaybackInfo {
-        /**
-         * The session uses remote playback.
-         */
-        public static final int PLAYBACK_TYPE_REMOTE = 2;
+    public static final class PlaybackInfo implements Parcelable {
         /**
          * The session uses local playback.
          */
         public static final int PLAYBACK_TYPE_LOCAL = 1;
+        /**
+         * The session uses remote playback.
+         */
+        public static final int PLAYBACK_TYPE_REMOTE = 2;
 
         private final int mVolumeType;
         private final int mVolumeControl;
@@ -1005,12 +1002,20 @@
         /**
          * @hide
          */
-        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
+        public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) {
             mVolumeType = type;
-            mAudioAttrs = attrs;
             mVolumeControl = control;
             mMaxVolume = max;
             mCurrentVolume = current;
+            mAudioAttrs = attrs;
+        }
+
+        PlaybackInfo(Parcel in) {
+            mVolumeType = in.readInt();
+            mVolumeControl = in.readInt();
+            mMaxVolume = in.readInt();
+            mCurrentVolume = in.readInt();
+            mAudioAttrs = in.readParcelable(null);
         }
 
         /**
@@ -1027,18 +1032,6 @@
         }
 
         /**
-         * Get the audio attributes for this session. The attributes will affect
-         * volume handling for the session. When the volume type is
-         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
-         * remote volume handler.
-         *
-         * @return The attributes for this session.
-         */
-        public AudioAttributes getAudioAttributes() {
-            return mAudioAttrs;
-        }
-
-        /**
          * Get the type of volume control that can be used. One of:
          * <ul>
          * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
@@ -1070,12 +1063,58 @@
         public int getCurrentVolume() {
             return mCurrentVolume;
         }
+
+        /**
+         * Get the audio attributes for this session. The attributes will affect
+         * volume handling for the session. When the volume type is
+         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
+         * remote volume handler.
+         *
+         * @return The attributes for this session.
+         */
+        public AudioAttributes getAudioAttributes() {
+            return mAudioAttrs;
+        }
+
+        @Override
+        public String toString() {
+            return "volumeType=" + mVolumeType + ", volumeControl=" + mVolumeControl
+                    + ", maxVolume=" + mMaxVolume + ", currentVolume=" + mCurrentVolume
+                    + ", audioAttrs=" + mAudioAttrs;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mVolumeType);
+            dest.writeInt(mVolumeControl);
+            dest.writeInt(mMaxVolume);
+            dest.writeInt(mCurrentVolume);
+            dest.writeParcelable(mAudioAttrs, flags);
+        }
+
+        public static final Parcelable.Creator<PlaybackInfo> CREATOR =
+                new Parcelable.Creator<PlaybackInfo>() {
+            @Override
+            public PlaybackInfo createFromParcel(Parcel in) {
+                return new PlaybackInfo(in);
+            }
+
+            @Override
+            public PlaybackInfo[] newArray(int size) {
+                return new PlaybackInfo[size];
+            }
+        };
     }
 
-    private final static class CallbackStub extends ISessionControllerCallback.Stub {
+    private static final class CallbackStub extends ControllerCallbackLink.CallbackStub {
         private final WeakReference<MediaController> mController;
 
-        public CallbackStub(MediaController controller) {
+        CallbackStub(MediaController controller) {
             mController = new WeakReference<MediaController>(controller);
         }
 
@@ -1112,9 +1151,7 @@
         }
 
         @Override
-        public void onQueueChanged(ParceledListSlice parceledQueue) {
-            List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
-                    .getList();
+        public void onQueueChanged(List<QueueItem> queue) {
             MediaController controller = mController.get();
             if (controller != null) {
                 controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
@@ -1138,15 +1175,12 @@
         }
 
         @Override
-        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
+        public void onVolumeInfoChanged(PlaybackInfo info) {
             MediaController controller = mController.get();
             if (controller != null) {
-                PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs,
-                        pvi.controlType, pvi.maxVolume, pvi.currentVolume);
                 controller.postMessage(MSG_UPDATE_VOLUME, info, null);
             }
         }
-
     }
 
     private final static class MessageHandler extends Handler {
@@ -1154,7 +1188,7 @@
         private boolean mRegistered = false;
 
         public MessageHandler(Looper looper, MediaController.Callback cb) {
-            super(looper, null, true);
+            super(looper);
             mCallback = cb;
         }
 
@@ -1193,6 +1227,7 @@
 
         public void post(int what, Object obj, Bundle data) {
             Message msg = obtainMessage(what, obj);
+            msg.setAsynchronous(true);
             msg.setData(data);
             msg.sendToTarget();
         }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 8962bb7..e07cf15 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -24,7 +24,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.MediaDescription;
 import android.media.MediaMetadata;
@@ -130,7 +129,7 @@
     private final MediaSession.Token mSessionToken;
     private final MediaController mController;
     private final ISession mBinder;
-    private final CallbackStub mCbStub;
+    private final SessionCallbackLink mCbStub;
 
     // Do not change the name of mCallback. Support lib accesses this by using reflection.
     @UnsupportedAppUsage
@@ -173,7 +172,7 @@
         }
         mMaxBitmapSize = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
-        mCbStub = new CallbackStub(this);
+        mCbStub = new SessionCallbackLink(new CallbackStub(this));
         MediaSessionManager manager = (MediaSessionManager) context
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
         try {
@@ -467,7 +466,7 @@
      */
     public void setQueue(@Nullable List<QueueItem> queue) {
         try {
-            mBinder.setQueue(queue == null ? null : new ParceledListSlice<QueueItem>(queue));
+            mBinder.setQueue(queue);
         } catch (RemoteException e) {
             Log.wtf("Dead object in setQueue.", e);
         }
@@ -1063,7 +1062,7 @@
     /**
      * @hide
      */
-    public static class CallbackStub extends ISessionCallback.Stub {
+    public static final class CallbackStub extends SessionCallbackLink.CallbackStub {
         private WeakReference<MediaSession> mMediaSession;
 
         public CallbackStub(MediaSession session) {
@@ -1071,14 +1070,14 @@
         }
 
         private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             return new RemoteUserInfo(packageName, pid, uid,
-                    caller != null ? caller.asBinder() : null);
+                    caller != null ? caller.getBinder() : null);
         }
 
         @Override
         public void onCommand(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
+                ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1104,7 +1103,7 @@
 
         @Override
         public void onMediaButtonFromController(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, Intent mediaButtonIntent) {
+                ControllerCallbackLink caller, Intent mediaButtonIntent) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1114,7 +1113,7 @@
 
         @Override
         public void onPrepare(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1123,7 +1122,7 @@
 
         @Override
         public void onPrepareFromMediaId(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String mediaId,
+                ControllerCallbackLink caller, String mediaId,
                 Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1134,7 +1133,7 @@
 
         @Override
         public void onPrepareFromSearch(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String query,
+                ControllerCallbackLink caller, String query,
                 Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1145,7 +1144,7 @@
 
         @Override
         public void onPrepareFromUri(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, Uri uri, Bundle extras) {
+                ControllerCallbackLink caller, Uri uri, Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1155,7 +1154,7 @@
 
         @Override
         public void onPlay(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1164,7 +1163,7 @@
 
         @Override
         public void onPlayFromMediaId(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String mediaId,
+                ControllerCallbackLink caller, String mediaId,
                 Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1175,7 +1174,7 @@
 
         @Override
         public void onPlayFromSearch(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String query,
+                ControllerCallbackLink caller, String query,
                 Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1186,7 +1185,7 @@
 
         @Override
         public void onPlayFromUri(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, Uri uri, Bundle extras) {
+                ControllerCallbackLink caller, Uri uri, Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1196,7 +1195,7 @@
 
         @Override
         public void onSkipToTrack(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, long id) {
+                ControllerCallbackLink caller, long id) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id);
@@ -1205,7 +1204,7 @@
 
         @Override
         public void onPause(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1214,7 +1213,7 @@
 
         @Override
         public void onStop(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1223,7 +1222,7 @@
 
         @Override
         public void onNext(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1232,7 +1231,7 @@
 
         @Override
         public void onPrevious(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1241,7 +1240,7 @@
 
         @Override
         public void onFastForward(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1250,7 +1249,7 @@
 
         @Override
         public void onRewind(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1259,7 +1258,7 @@
 
         @Override
         public void onSeekTo(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, long pos) {
+                ControllerCallbackLink caller, long pos) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos);
@@ -1267,7 +1266,7 @@
         }
 
         @Override
-        public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+        public void onRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
                 Rating rating) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1277,7 +1276,7 @@
 
         @Override
         public void onCustomAction(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String action, Bundle args) {
+                ControllerCallbackLink caller, String action, Bundle args) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1287,7 +1286,7 @@
 
         @Override
         public void onAdjustVolume(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, int direction) {
+                ControllerCallbackLink caller, int direction) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1297,7 +1296,7 @@
 
         @Override
         public void onSetVolumeTo(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, int value) {
+                ControllerCallbackLink caller, int value) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1453,7 +1452,7 @@
         private RemoteUserInfo mCurrentControllerInfo;
 
         public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
-            super(looper, null, true);
+            super(looper);
             mCallback = callback;
             mCallback.mHandler = this;
         }
@@ -1461,6 +1460,7 @@
         public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
             Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
             Message msg = obtainMessage(what, objWithCaller);
+            msg.setAsynchronous(true);
             msg.setData(data);
             if (delayMs > 0) {
                 sendMessageDelayed(msg, delayMs);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 4221d66..4596c22 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSession2;
+import android.media.Session2Token;
 import android.media.browse.MediaBrowser;
 import android.os.Handler;
 import android.os.IBinder;
@@ -39,6 +41,8 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -67,9 +71,13 @@
      */
     public static final int RESULT_MEDIA_KEY_HANDLED = 1;
 
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
     private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
             = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
-    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper>
+            mSession2TokensListeners = new ArrayMap<>();
     private final ISessionManager mService;
 
     private Context mContext;
@@ -96,12 +104,36 @@
      * @return The binder object from the system
      * @hide
      */
-    public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
+    public @NonNull ISession createSession(@NonNull SessionCallbackLink cbStub,
             @NonNull String tag, int userId) throws RemoteException {
         return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
     }
 
     /**
+     * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
+     * created.
+     * <p>
+     * Do not use this API directly, but create a new instance through the
+     * {@link MediaSession2.Builder} instead.
+     *
+     * @param token newly created session2 token
+     * @hide
+     */
+    public void notifySession2Created(@NonNull Session2Token token) {
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        if (token.getType() != Session2Token.TYPE_SESSION) {
+            throw new IllegalArgumentException("token's type should be TYPE_SESSION");
+        }
+        try {
+            mService.notifySession2Created(token);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get a list of controllers for all ongoing sessions. The controllers will
      * be provided in priority order with the most important controller at index
      * 0.
@@ -153,6 +185,44 @@
     }
 
     /**
+     * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
+     * current user.
+     * <p>
+     * Although this API can be used without any restriction, each session owners can accept or
+     * reject your uses of {@link MediaSession2}.
+     *
+     * @return A list of {@link Session2Token}.
+     * @hide
+     */
+    // TODO: unhide
+    @NonNull
+    public List<Session2Token> getSession2Tokens() {
+        return getSession2Tokens(UserHandle.myUserId());
+    }
+
+    /**
+     * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
+     * given user.
+     * <p>
+     * If you want to get tokens for another user, you must hold the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+     *
+     * @param userId The user id to fetch sessions for.
+     * @return A list of {@link Session2Token}
+     * @hide
+     */
+    // TODO: unhide
+    @NonNull
+    public List<Session2Token> getSession2Tokens(int userId) {
+        try {
+            return mService.getSession2Tokens(userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get session tokens", e);
+        }
+        return new ArrayList<>();
+    }
+
+    /**
      * Add a listener to be notified when the list of active sessions
      * changes.This requires the
      * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
@@ -260,6 +330,87 @@
     }
 
     /**
+     * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+     * for consistent behavior across all devices.
+     *
+     * @param listener The listener to add
+     * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
+     *                be used.
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    public void addOnSession2TokensChangedListener(
+            @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
+        addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler);
+    }
+
+    /**
+     * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+     * for consistent behavior across all devices.
+     *
+     * @param userId The userId to listen for changes on
+     * @param listener The listener to add
+     * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
+     *                be used.
+     * @hide
+     */
+    public void addOnSession2TokensChangedListener(int userId,
+            @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener shouldn't be null");
+        }
+        synchronized (mLock) {
+            if (mSession2TokensListeners.get(listener) != null) {
+                Log.w(TAG, "Attempted to add session listener twice, ignoring.");
+                return;
+            }
+            Session2TokensChangedWrapper wrapper =
+                    new Session2TokensChangedWrapper(listener, handler);
+            try {
+                mService.addSession2TokensListener(wrapper.getStub(), userId);
+                mSession2TokensListeners.put(listener, wrapper);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in addSessionTokensListener.", e);
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates.
+     *
+     * @param listener The listener to remove.
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    public void removeOnSession2TokensChangedListener(
+            @NonNull OnSession2TokensChangedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener may not be null");
+        }
+        final Session2TokensChangedWrapper wrapper;
+        synchronized (mLock) {
+            wrapper = mSession2TokensListeners.remove(listener);
+        }
+        if (wrapper != null) {
+            try {
+                mService.removeSession2TokensListener(wrapper.getStub());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in removeSessionTokensListener.", e);
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Set the remote volume controller to receive volume updates on. Only for
      * use by system UI.
      *
@@ -526,6 +677,26 @@
     }
 
     /**
+     * Listens for changes to the {@link #getSession2Tokens()}. This can be added
+     * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+     * for consistent behavior across all devices.
+     *
+     * @hide
+     */
+    public interface OnSession2TokensChangedListener {
+        /**
+         * Called when the {@link #getSession2Tokens()} is changed.
+         *
+         * @param tokens list of {@link Session2Token}
+         */
+        void onSession2TokensChanged(@NonNull List<Session2Token> tokens);
+    }
+
+    /**
      * Listens the volume key long-presses.
      * @hide
      */
@@ -743,6 +914,27 @@
         }
     }
 
+    private static final class Session2TokensChangedWrapper {
+        private final OnSession2TokensChangedListener mListener;
+        private final Handler mHandler;
+        private final ISession2TokensListener.Stub mStub =
+                new ISession2TokensListener.Stub() {
+                    @Override
+                    public void onSession2TokensChanged(final List<Session2Token> tokens) {
+                        mHandler.post(() -> mListener.onSession2TokensChanged(tokens));
+                    }
+                };
+
+        Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) {
+            mListener = listener;
+            mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper());
+        }
+
+        public ISession2TokensListener.Stub getStub() {
+            return mStub;
+        }
+    }
+
     private static final class OnVolumeKeyLongPressListenerImpl
             extends IOnVolumeKeyLongPressListener.Stub {
         private OnVolumeKeyLongPressListener mListener;
diff --git a/media/java/android/media/session/MediaSessionProviderService.java b/media/java/android/media/session/MediaSessionProviderService.java
new file mode 100644
index 0000000..9a346ff
--- /dev/null
+++ b/media/java/android/media/session/MediaSessionProviderService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 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.session;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Abstract class for mainline module services.
+ *
+ * @hide  // TODO: Make it as a @SystemApi
+ */
+public abstract class MediaSessionProviderService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        // TODO: Return IMediaSessionProviderService.Stub()
+        return null;
+    }
+}
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
deleted file mode 100644
index f59c975..0000000
--- a/media/java/android/media/session/ParcelableVolumeInfo.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright 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.
- */
-
-package android.media.session;
-
-import android.media.AudioAttributes;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Convenience class for passing information about the audio configuration of a
- * session. The public implementation is {@link MediaController.PlaybackInfo}.
- *
- * @hide
- */
-public class ParcelableVolumeInfo implements Parcelable {
-    public int volumeType;
-    public AudioAttributes audioAttrs;
-    public int controlType;
-    public int maxVolume;
-    public int currentVolume;
-
-    public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType,
-            int maxVolume,
-            int currentVolume) {
-        this.volumeType = volumeType;
-        this.audioAttrs = audioAttrs;
-        this.controlType = controlType;
-        this.maxVolume = maxVolume;
-        this.currentVolume = currentVolume;
-    }
-
-    public ParcelableVolumeInfo(Parcel from) {
-        volumeType = from.readInt();
-        controlType = from.readInt();
-        maxVolume = from.readInt();
-        currentVolume = from.readInt();
-        audioAttrs = AudioAttributes.CREATOR.createFromParcel(from);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(volumeType);
-        dest.writeInt(controlType);
-        dest.writeInt(maxVolume);
-        dest.writeInt(currentVolume);
-        audioAttrs.writeToParcel(dest, flags);
-    }
-
-
-    public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
-            = new Parcelable.Creator<ParcelableVolumeInfo>() {
-        @Override
-        public ParcelableVolumeInfo createFromParcel(Parcel in) {
-            return new ParcelableVolumeInfo(in);
-        }
-
-        @Override
-        public ParcelableVolumeInfo[] newArray(int size) {
-            return new ParcelableVolumeInfo[size];
-        }
-    };
-}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/media/java/android/media/session/SessionCallbackLink.aidl
new file mode 100644
index 0000000..c489e5b
--- /dev/null
+++ b/media/java/android/media/session/SessionCallbackLink.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019 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.session;
+
+parcelable SessionCallbackLink;
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
new file mode 100644
index 0000000..7547bff
--- /dev/null
+++ b/media/java/android/media/session/SessionCallbackLink.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright 2019 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.session;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.media.Rating;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+
+/**
+ * Handles incoming commands to {@link MediaSession.Callback}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ */
+public final class SessionCallbackLink implements Parcelable {
+    final CallbackStub mCallbackStub;
+    final ISessionCallback mISessionCallback;
+
+    /**
+     * Constructor for stub (Callee)
+     */
+    SessionCallbackLink(@NonNull CallbackStub callbackStub) {
+        mCallbackStub = callbackStub;
+        mISessionCallback = new CallbackStubProxy();
+    }
+
+    /**
+     * Constructor for interface (Caller)
+     */
+    SessionCallbackLink(Parcel in) {
+        mCallbackStub = null;
+        mISessionCallback = ISessionCallback.Stub.asInterface(in.readStrongBinder());
+    }
+
+    /**
+     * Notify session that a controller sends a command.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param command the name of the command
+     * @param args the arguments included with the command
+     * @param cb the result receiver for getting the result of the command
+     */
+    public void notifyCommand(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull String command,
+            @Nullable Bundle args, @Nullable ResultReceiver cb) {
+        try {
+            mISessionCallback.notifyCommand(packageName, pid, uid, caller, command, args, cb);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that the android system sends a media button event.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param mediaButtonIntent the media button intent
+     * @param sequenceNumber the sequence number of this call
+     * @param cb the result receiver for getting the result of the command
+     */
+    public void notifyMediaButton(@NonNull String packageName, int pid, int uid,
+            @NonNull Intent mediaButtonIntent, int sequenceNumber,
+            @Nullable ResultReceiver cb) {
+        try {
+            mISessionCallback.notifyMediaButton(packageName, pid, uid, mediaButtonIntent,
+                    sequenceNumber, cb);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller sends a media button event.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param mediaButtonIntent the media button intent
+     */
+    public void notifyMediaButtonFromController(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull Intent mediaButtonIntent) {
+        try {
+            mISessionCallback.notifyMediaButtonFromController(packageName, pid, uid, caller,
+                    mediaButtonIntent);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests preparing media.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyPrepare(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyPrepare(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests preparing media from given media ID.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param mediaId the ID of the media
+     * @param extras the extras included with this request.
+     */
+    public void notifyPrepareFromMediaId(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+            @Nullable Bundle extras) {
+        try {
+            mISessionCallback.notifyPrepareFromMediaId(packageName, pid, uid, caller, mediaId,
+                    extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests preparing media from given search query.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param query the search query
+     * @param extras the extras included with this request.
+     */
+    public void notifyPrepareFromSearch(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull String query,
+            @Nullable Bundle extras) {
+        try {
+            mISessionCallback.notifyPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests preparing media from given uri.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param uri the uri of the media
+     * @param extras the extras included with this request.
+     */
+    public void notifyPrepareFromUri(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+        try {
+            mISessionCallback.notifyPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests playing media.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyPlay(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyPlay(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests playing media from given media ID.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param mediaId the ID of the media
+     * @param extras the extras included with this request.
+     */
+    public void notifyPlayFromMediaId(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+            @Nullable Bundle extras) {
+        try {
+            mISessionCallback.notifyPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests playing media from given search query.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param query the search query
+     * @param extras the extras included with this request.
+     */
+    public void notifyPlayFromSearch(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull String query,
+            @Nullable Bundle extras) {
+        try {
+            mISessionCallback.notifyPlayFromSearch(packageName, pid, uid, caller, query, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests playing media from given uri.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param uri the uri of the media
+     * @param extras the extras included with this request.
+     */
+    public void notifyPlayFromUri(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+        try {
+            mISessionCallback.notifyPlayFromUri(packageName, pid, uid, caller, uri, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests skipping to the queue item with given ID.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param id the queue id of the item
+     */
+    public void notifySkipToTrack(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, long id) {
+        try {
+            mISessionCallback.notifySkipToTrack(packageName, pid, uid, caller, id);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests pausing media.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyPause(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyPause(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests stopping media.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyStop(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyStop(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests skipping to the next queue item.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyNext(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyNext(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests skipping to the previous queue item.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyPrevious(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyPrevious(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests fast-forwarding.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyFastForward(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyFastForward(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests rewinding.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     */
+    public void notifyRewind(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller) {
+        try {
+            mISessionCallback.notifyRewind(packageName, pid, uid, caller);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests seeking to the specific position.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param pos the position to move to, in milliseconds
+     */
+    public void notifySeekTo(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, long pos) {
+        try {
+            mISessionCallback.notifySeekTo(packageName, pid, uid, caller, pos);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests rating of the current media.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param rating the rating of the current media
+     */
+    public void notifyRate(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull Rating rating) {
+        try {
+            mISessionCallback.notifyRate(packageName, pid, uid, caller, rating);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller sends a custom action.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param action the name of the action
+     * @param args the arguments included with this action
+     */
+    public void notifyCustomAction(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, @NonNull String action, @Nullable Bundle args) {
+        try {
+            mISessionCallback.notifyCustomAction(packageName, pid, uid, caller, action, args);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests adjusting volume.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param direction the direction of the volume change.
+     */
+    public void notifyAdjustVolume(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, int direction) {
+        try {
+            mISessionCallback.notifyAdjustVolume(packageName, pid, uid, caller, direction);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify session that a controller requests setting volume.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param value the volume value to set
+     */
+    public void notifySetVolumeTo(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, int value) {
+        try {
+            mISessionCallback.notifySetVolumeTo(packageName, pid, uid, caller, value);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Gets the binder */
+    @NonNull
+    public IBinder getBinder() {
+        return mISessionCallback.asBinder();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mISessionCallback.asBinder());
+    }
+
+    public static final Parcelable.Creator<SessionCallbackLink> CREATOR =
+            new Parcelable.Creator<SessionCallbackLink>() {
+                @Override
+                public SessionCallbackLink createFromParcel(Parcel in) {
+                    return new SessionCallbackLink(in);
+                }
+
+                @Override
+                public SessionCallbackLink[] newArray(int size) {
+                    return new SessionCallbackLink[size];
+                }
+            };
+
+    /**
+     * Class for Stub implementation
+     */
+    abstract static class CallbackStub {
+        /** Stub method for ISessionCallback.notifyCommand */
+        public void onCommand(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull String command,
+                @Nullable Bundle args, @Nullable ResultReceiver cb) {
+        }
+
+        /** Stub method for ISessionCallback.notifyMediaButton */
+        public void onMediaButton(@NonNull String packageName, int pid, int uid,
+                @NonNull Intent mediaButtonIntent, int sequenceNumber,
+                @Nullable ResultReceiver cb) {
+        }
+
+        /** Stub method for ISessionCallback.notifyMediaButtonFromController */
+        public void onMediaButtonFromController(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull Intent mediaButtonIntent) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPrepare */
+        public void onPrepare(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPrepareFromMediaId */
+        public void onPrepareFromMediaId(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+                @Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPrepareFromSearch */
+        public void onPrepareFromSearch(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, String query, @Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPrepareFromUri */
+        public void onPrepareFromUri(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPlay */
+        public void onPlay(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPlayFromMediaId */
+        public void onPlayFromMediaId(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+                @Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPlayFromSearch */
+        public void onPlayFromSearch(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, String query, @Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPlayFromUri */
+        public void onPlayFromUri(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+        }
+
+        /** Stub method for ISessionCallback.notifySkipToTrack */
+        public void onSkipToTrack(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, long id) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPause */
+        public void onPause(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifyStop */
+        public void onStop(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifyNext */
+        public void onNext(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifyPrevious */
+        public void onPrevious(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifyFastForward */
+        public void onFastForward(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifyRewind */
+        public void onRewind(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller) {
+        }
+
+        /** Stub method for ISessionCallback.notifySeekTo */
+        public void onSeekTo(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, long pos) {
+        }
+
+        /** Stub method for ISessionCallback.notifyRate */
+        public void onRate(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull Rating rating) {
+        }
+
+        /** Stub method for ISessionCallback.notifyCustomAction */
+        public void onCustomAction(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, @NonNull String action,
+                @Nullable Bundle args) {
+        }
+
+        /** Stub method for ISessionCallback.notifyAdjustVolume */
+        public void onAdjustVolume(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, int direction) {
+        }
+
+        /** Stub method for ISessionCallback.notifySetVolumeTo */
+        public void onSetVolumeTo(@NonNull String packageName, int pid, int uid,
+                @NonNull ControllerCallbackLink caller, int value) {
+        }
+    }
+
+    private class CallbackStubProxy extends ISessionCallback.Stub {
+        @Override
+        public void notifyCommand(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
+            mCallbackStub.onCommand(packageName, pid, uid, caller, command, args, cb);
+        }
+
+        @Override
+        public void notifyMediaButton(String packageName, int pid, int uid,
+                Intent mediaButtonIntent, int sequenceNumber, ResultReceiver cb) {
+            mCallbackStub.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceNumber,
+                    cb);
+        }
+
+        @Override
+        public void notifyMediaButtonFromController(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, Intent mediaButtonIntent) {
+            mCallbackStub.onMediaButtonFromController(packageName, pid, uid, caller,
+                    mediaButtonIntent);
+        }
+
+        @Override
+        public void notifyPrepare(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onPrepare(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifyPrepareFromMediaId(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, String mediaId, Bundle extras) {
+            mCallbackStub.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+        }
+
+        @Override
+        public void notifyPrepareFromSearch(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, String query, Bundle extras) {
+            mCallbackStub.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+        }
+
+        @Override
+        public void notifyPrepareFromUri(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, Uri uri, Bundle extras) {
+            mCallbackStub.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+        }
+
+        @Override
+        public void notifyPlay(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onPlay(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifyPlayFromMediaId(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, String mediaId, Bundle extras) {
+            mCallbackStub.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+        }
+
+        @Override
+        public void notifyPlayFromSearch(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, String query, Bundle extras) {
+            mCallbackStub.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
+        }
+
+        @Override
+        public void notifyPlayFromUri(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, Uri uri, Bundle extras) {
+            mCallbackStub.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
+        }
+
+        @Override
+        public void notifySkipToTrack(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, long id) {
+            mCallbackStub.onSkipToTrack(packageName, pid, uid, caller, id);
+        }
+
+        @Override
+        public void notifyPause(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onPause(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifyStop(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onStop(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifyNext(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onNext(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifyPrevious(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onPrevious(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifyFastForward(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onFastForward(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifyRewind(String packageName, int pid, int uid,
+                ControllerCallbackLink caller) {
+            mCallbackStub.onRewind(packageName, pid, uid, caller);
+        }
+
+        @Override
+        public void notifySeekTo(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, long pos) {
+            mCallbackStub.onSeekTo(packageName, pid, uid, caller, pos);
+        }
+
+        @Override
+        public void notifyRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
+                Rating rating) {
+            mCallbackStub.onRate(packageName, pid, uid, caller, rating);
+        }
+
+        @Override
+        public void notifyCustomAction(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, String action, Bundle args) {
+            mCallbackStub.onCustomAction(packageName, pid, uid, caller, action, args);
+        }
+
+        @Override
+        public void notifyAdjustVolume(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, int direction) {
+            mCallbackStub.onAdjustVolume(packageName, pid, uid, caller, direction);
+        }
+
+        @Override
+        public void notifySetVolumeTo(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, int value) {
+            mCallbackStub.onSetVolumeTo(packageName, pid, uid, caller, value);
+        }
+    }
+}
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index fa69062..ada77c5 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -372,6 +372,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    @UnsupportedAppUsage
     public int getModelState(UUID soundModelId) {
         if (soundModelId == null) {
             return STATUS_ERROR;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f75f69b..0198470 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -34,7 +34,6 @@
         "libutils",
         "libbinder",
         "libmedia",
-        "libmediaextractor",
         "libmedia_omx",
         "libmediametrics",
         "libmediadrm",
@@ -85,7 +84,7 @@
 }
 
 cc_library_shared {
-    name: "libmedia2_jni",
+    name: "libmediaplayer2_jni",
 
     srcs: [
         "android_media_DataSourceCallback.cpp",
@@ -102,7 +101,6 @@
         "libhidlbase",
         "libhidlmemory",
 
-        "libpowermanager",  // Used by JWakeLock. Will be replace with public SDJ API.
         "libmediametrics",  // Used by MediaMetrics. Will be replaced with stable C API.
         "libbinder",  // Used by JWakeLock and MediaMetrics.
 
@@ -111,13 +109,17 @@
 
         // NDK or NDK-compliant
         "libandroid",
+        "libbinder_ndk",
         "libmediandk",
         "libnativehelper_compat_libc++",
         "liblog",
         "libz",
     ],
 
-    header_libs: ["libhardware_headers"],
+    header_libs: [
+        "libhardware_headers",
+        "libnativewindow_headers",
+    ],
 
     static_libs: [
         "libbase",
@@ -125,12 +127,12 @@
         "libcutils",
         "libmedia_helper",
         "libmedia_player2_util",
-        "libmediaextractor",
         "libmediaplayer2",
         "libmediaplayer2-protos",
         "libmediandk_utils",
         "libmediautils",
         "libprotobuf-cpp-lite",
+        "libstagefright",
         "libstagefright_esds",
         "libstagefright_foundation",
         "libstagefright_httplive",
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
new file mode 100644
index 0000000..1d7d6de
--- /dev/null
+++ b/media/jni/Android.mk
@@ -0,0 +1,30 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_SRC_FILES := \
+    Media2Jni.cpp \
+
+# TODO: Move libmedia2_jni from system to media apex. Currently, libraries defined in
+#       Android.mk is not visible in apex build.
+LOCAL_MODULE:= libmedia2_jni
+LOCAL_SHARED_LIBRARIES := libdl liblog
+
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(UBSAN_RUNTIME_LIBRARY) \
+  $(TSAN_RUNTIME_LIBRARY)))
+
+# $(info Sanitizer:  $(sanitizer_runtime_libraries))
+
+ndk_libraries := $(call normalize-path-list,$(addprefix lib,$(addsuffix .so,\
+  $(NDK_PREBUILT_SHARED_LIBRARIES))))
+
+# $(info NDK:  $(ndk_libraries))
+
+LOCAL_CFLAGS += -DLINKED_LIBRARIES='"$(sanitizer_runtime_libraries):$(ndk_libraries)"'
+
+sanitizer_runtime_libraries :=
+ndk_libraries :=
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/jni/Media2Jni.cpp b/media/jni/Media2Jni.cpp
new file mode 100644
index 0000000..6c0a65c
--- /dev/null
+++ b/media/jni/Media2Jni.cpp
@@ -0,0 +1,89 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaPlayer2-JNI"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+extern "C" {
+  // Copied from GraphicsEnv.cpp
+  // TODO(b/37049319) Get this from a header once one exists
+  android_namespace_t* android_create_namespace(const char* name,
+                                                const char* ld_library_path,
+                                                const char* default_library_path,
+                                                uint64_t type,
+                                                const char* permitted_when_isolated_path,
+                                                android_namespace_t* parent);
+  bool android_link_namespaces(android_namespace_t* from,
+                               android_namespace_t* to,
+                               const char* shared_libs_sonames);
+  enum {
+     ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+  };
+
+}  // extern "C"
+
+static const char kApexLibPath[] =  "/apex/com.android.media/lib"
+#ifdef __LP64__
+    "64"
+#endif
+    "/";
+static const char kMediaPlayer2LibPath[] =  "/apex/com.android.media/lib"
+#ifdef __LP64__
+    "64"
+#endif
+    "/libmediaplayer2_jni.so";
+
+typedef jint (*Media2JniOnLoad)(JavaVM*, void*);
+
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    android_namespace_t *media2Ns = android_create_namespace("media2",
+            nullptr,  // ld_library_path
+            kApexLibPath,
+            ANDROID_NAMESPACE_TYPE_ISOLATED,
+            nullptr,  // permitted_when_isolated_path
+            nullptr); // parent
+    if (!android_link_namespaces(media2Ns, nullptr, LINKED_LIBRARIES)) {
+        ALOGE("Failed to link namespace. Failed to load extractor plug-ins in apex.");
+        return -1;
+    }
+    const android_dlextinfo dlextinfo = {
+        .flags = ANDROID_DLEXT_USE_NAMESPACE,
+        .library_namespace = media2Ns,
+    };
+    // load libmediaplayer2_jni and call JNI_OnLoad.
+    void *libHandle = android_dlopen_ext(kMediaPlayer2LibPath, RTLD_NOW | RTLD_LOCAL, &dlextinfo);
+    if (libHandle == NULL) {
+        ALOGW("couldn't dlopen(%s) %s", kMediaPlayer2LibPath, strerror(errno));
+        return -1;
+    }
+    Media2JniOnLoad media2JniOnLoad = (Media2JniOnLoad) dlsym(libHandle, "JNI_OnLoad");
+    if (!media2JniOnLoad) {
+        ALOGW("%s does not contain JNI_OnLoad()", kMediaPlayer2LibPath);
+        dlclose(libHandle);
+        return -1;
+    }
+    return media2JniOnLoad(vm, reserved);
+}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 2578608..406f9dd 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -72,7 +72,10 @@
     jint cryptoErrorResourceBusy;
     jint cryptoErrorInsufficientOutputProtection;
     jint cryptoErrorSessionNotOpened;
+    jint cryptoErrorInsufficientSecurity;
     jint cryptoErrorUnsupportedOperation;
+    jint cryptoErrorFrameTooLarge;
+    jint cryptoErrorLostState;
 } gCryptoErrorCodes;
 
 static struct CodecActionCodes {
@@ -645,7 +648,6 @@
 
     capabilities->getSupportedColorFormats(&colorFormats);
     capabilities->getSupportedProfileLevels(&profileLevels);
-    uint32_t flags = capabilities->getFlags();
     sp<AMessage> details = capabilities->getDetails();
 
     jobject defaultFormatObj = NULL;
@@ -684,7 +686,7 @@
 
     return env->NewObject(
             gCodecInfo.capsClazz, gCodecInfo.capsCtorId,
-            profileLevelArray.get(), colorFormatsArray.get(), isEncoder, flags,
+            profileLevelArray.get(), colorFormatsArray.get(), isEncoder,
             defaultFormatRef.get(), detailsRef.get());
 }
 
@@ -697,23 +699,28 @@
         return err;
     }
 
+    // TODO: get alias
     ScopedLocalRef<jstring> nameObject(env,
             env->NewStringUTF(codecInfo->getCodecName()));
 
+    ScopedLocalRef<jstring> canonicalNameObject(env,
+            env->NewStringUTF(codecInfo->getCodecName()));
+
+    MediaCodecInfo::Attributes attributes = codecInfo->getAttributes();
     bool isEncoder = codecInfo->isEncoder();
 
-    Vector<AString> mimes;
-    codecInfo->getSupportedMimes(&mimes);
+    Vector<AString> mediaTypes;
+    codecInfo->getSupportedMediaTypes(&mediaTypes);
 
     ScopedLocalRef<jobjectArray> capsArrayObj(env,
-        env->NewObjectArray(mimes.size(), gCodecInfo.capsClazz, NULL));
+        env->NewObjectArray(mediaTypes.size(), gCodecInfo.capsClazz, NULL));
 
-    for (size_t i = 0; i < mimes.size(); i++) {
+    for (size_t i = 0; i < mediaTypes.size(); i++) {
         const sp<MediaCodecInfo::Capabilities> caps =
-                codecInfo->getCapabilitiesFor(mimes[i].c_str());
+                codecInfo->getCapabilitiesFor(mediaTypes[i].c_str());
 
         ScopedLocalRef<jobject> capsObj(env, getCodecCapabilitiesObject(
-                env, mimes[i].c_str(), isEncoder, caps));
+                env, mediaTypes[i].c_str(), isEncoder, caps));
 
         env->SetObjectArrayElement(capsArrayObj.get(), i, capsObj.get());
     }
@@ -723,10 +730,10 @@
     CHECK(codecInfoClazz.get() != NULL);
 
     jmethodID codecInfoCtorID = env->GetMethodID(codecInfoClazz.get(), "<init>",
-            "(Ljava/lang/String;Z[Landroid/media/MediaCodecInfo$CodecCapabilities;)V");
+            "(Ljava/lang/String;Ljava/lang/String;I[Landroid/media/MediaCodecInfo$CodecCapabilities;)V");
 
     *codecInfoObject = env->NewObject(codecInfoClazz.get(), codecInfoCtorID,
-            nameObject.get(), isEncoder, capsArrayObj.get());
+            nameObject.get(), canonicalNameObject.get(), attributes, capsArrayObj.get());
 
     return OK;
 }
@@ -1005,10 +1012,22 @@
             err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
             defaultMsg = "Attempted to use a closed session";
             break;
+        case ERROR_DRM_INSUFFICIENT_SECURITY:
+            err = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
+            defaultMsg = "Required security level is not met";
+            break;
         case ERROR_DRM_CANNOT_HANDLE:
             err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
             defaultMsg = "Operation not supported in this configuration";
             break;
+        case ERROR_DRM_FRAME_TOO_LARGE:
+            err = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
+            defaultMsg = "Decrytped frame exceeds size of output buffer";
+            break;
+        case ERROR_DRM_SESSION_LOST_STATE:
+            err = gCryptoErrorCodes.cryptoErrorLostState;
+            defaultMsg = "Session state was lost, open a new session and retry";
+            break;
         default:  /* Other negative DRM error codes go out as is. */
             break;
     }
@@ -1994,11 +2013,26 @@
     gCryptoErrorCodes.cryptoErrorSessionNotOpened =
         env->GetStaticIntField(clazz.get(), field);
 
+    field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_SECURITY", "I");
+    CHECK(field != NULL);
+    gCryptoErrorCodes.cryptoErrorInsufficientSecurity =
+        env->GetStaticIntField(clazz.get(), field);
+
     field = env->GetStaticFieldID(clazz.get(), "ERROR_UNSUPPORTED_OPERATION", "I");
     CHECK(field != NULL);
     gCryptoErrorCodes.cryptoErrorUnsupportedOperation =
         env->GetStaticIntField(clazz.get(), field);
 
+    field = env->GetStaticFieldID(clazz.get(), "ERROR_FRAME_TOO_LARGE", "I");
+    CHECK(field != NULL);
+    gCryptoErrorCodes.cryptoErrorFrameTooLarge =
+        env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "ERROR_LOST_STATE", "I");
+    CHECK(field != NULL);
+    gCryptoErrorCodes.cryptoErrorLostState =
+        env->GetStaticIntField(clazz.get(), field);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
     CHECK(clazz.get() != NULL);
     field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
@@ -2049,7 +2083,7 @@
     gCodecInfo.capsClazz = (jclass)env->NewGlobalRef(clazz.get());
 
     method = env->GetMethodID(clazz.get(), "<init>",
-            "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZI"
+            "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ"
             "Ljava/util/Map;Ljava/util/Map;)V");
     CHECK(method != NULL);
     gCodecInfo.capsCtorId = method;
@@ -2187,7 +2221,7 @@
     { "getImage", "(ZI)Landroid/media/Image;",
       (void *)android_media_MediaCodec_getImage },
 
-    { "getName", "()Ljava/lang/String;",
+    { "getCanonicalName", "()Ljava/lang/String;",
       (void *)android_media_MediaCodec_getName },
 
     { "getOwnCodecInfo", "()Landroid/media/MediaCodecInfo;",
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 8de11ca..cf14942 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -41,6 +41,21 @@
     return mcl;
 }
 
+static sp<MediaCodecInfo> getCodecInfo(JNIEnv *env, jint index) {
+    sp<IMediaCodecList> mcl = getCodecList(env);
+    if (mcl == NULL) {
+        // Runtime exception already pending.
+        return NULL;
+    }
+
+    sp<MediaCodecInfo> info = mcl->getCodecInfo(index);
+    if (info == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+    }
+
+    return info;
+}
+
 static jint android_media_MediaCodecList_getCodecCount(
         JNIEnv *env, jobject /* thiz */) {
     sp<IMediaCodecList> mcl = getCodecList(env);
@@ -53,15 +68,22 @@
 
 static jstring android_media_MediaCodecList_getCodecName(
         JNIEnv *env, jobject /* thiz */, jint index) {
-    sp<IMediaCodecList> mcl = getCodecList(env);
-    if (mcl == NULL) {
+    sp<MediaCodecInfo> info = getCodecInfo(env, index);
+    if (info == NULL) {
         // Runtime exception already pending.
         return NULL;
     }
 
-    const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
+    // TODO: support aliases
+    const char *name = info->getCodecName();
+    return env->NewStringUTF(name);
+}
+
+static jstring android_media_MediaCodecList_getCanonicalName(
+        JNIEnv *env, jobject /* thiz */, jint index) {
+    sp<MediaCodecInfo> info = getCodecInfo(env, index);
     if (info == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        // Runtime exception already pending.
         return NULL;
     }
 
@@ -94,39 +116,27 @@
     return ret;
 }
 
-static jboolean android_media_MediaCodecList_isEncoder(
+static jboolean android_media_MediaCodecList_getAttributes(
         JNIEnv *env, jobject /* thiz */, jint index) {
-    sp<IMediaCodecList> mcl = getCodecList(env);
-    if (mcl == NULL) {
-        // Runtime exception already pending.
-        return false;
-    }
-
-    const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
+    sp<MediaCodecInfo> info = getCodecInfo(env, index);
     if (info == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return false;
+        // Runtime exception already pending.
+        return 0;
     }
 
-    return info->isEncoder();
+    return info->getAttributes();
 }
 
 static jarray android_media_MediaCodecList_getSupportedTypes(
         JNIEnv *env, jobject /* thiz */, jint index) {
-    sp<IMediaCodecList> mcl = getCodecList(env);
-    if (mcl == NULL) {
+    sp<MediaCodecInfo> info = getCodecInfo(env, index);
+    if (info == NULL) {
         // Runtime exception already pending.
         return NULL;
     }
 
-    const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
-    if (info == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return NULL;
-    }
-
     Vector<AString> types;
-    info->getSupportedMimes(&types);
+    info->getSupportedMediaTypes(&types);
 
     jclass clazz = env->FindClass("java/lang/String");
     CHECK(clazz != NULL);
@@ -150,17 +160,12 @@
         return NULL;
     }
 
-    sp<IMediaCodecList> mcl = getCodecList(env);
-    if (mcl == NULL) {
+    sp<MediaCodecInfo> info = getCodecInfo(env, index);
+    if (info == NULL) {
         // Runtime exception already pending.
         return NULL;
     }
 
-    const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
-    if (info == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return NULL;
-    }
 
     const char *typeStr = env->GetStringUTFChars(type, NULL);
     if (typeStr == NULL) {
@@ -186,7 +191,6 @@
 
     capabilities->getSupportedColorFormats(&colorFormats);
     capabilities->getSupportedProfileLevels(&profileLevels);
-    uint32_t flags = capabilities->getFlags();
     sp<AMessage> details = capabilities->getDetails();
     bool isEncoder = info->isEncoder();
 
@@ -240,11 +244,11 @@
     }
 
     jmethodID capsConstructID = env->GetMethodID(capsClazz, "<init>",
-            "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZI"
+            "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ"
             "Ljava/util/Map;Ljava/util/Map;)V");
 
     jobject caps = env->NewObject(capsClazz, capsConstructID,
-            profileLevelArray, colorFormatsArray, isEncoder, flags,
+            profileLevelArray, colorFormatsArray, isEncoder,
             defaultFormatObj, infoObj);
 
     env->DeleteLocalRef(profileLevelArray);
@@ -288,9 +292,15 @@
 
 static const JNINativeMethod gMethods[] = {
     { "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount },
+
+    { "getCanonicalName", "(I)Ljava/lang/String;",
+      (void *)android_media_MediaCodecList_getCanonicalName },
+
     { "getCodecName", "(I)Ljava/lang/String;",
       (void *)android_media_MediaCodecList_getCodecName },
-    { "isEncoder", "(I)Z", (void *)android_media_MediaCodecList_isEncoder },
+
+    { "getAttributes", "(I)I", (void *)android_media_MediaCodecList_getAttributes },
+
     { "getSupportedTypes", "(I)[Ljava/lang/String;",
       (void *)android_media_MediaCodecList_getSupportedTypes },
 
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index be71dad5..8336459 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,7 @@
     jint kWhatDrmEvent;
     jint kWhatExpirationUpdate;
     jint kWhatKeyStatusChange;
+    jint kWhatSessionLostState;
 } gEventWhat;
 
 struct KeyTypes {
@@ -141,6 +142,16 @@
     jclass classId;
 };
 
+struct SessionExceptionFields {
+    jmethodID init;
+    jclass classId;
+    jfieldID errorCode;
+};
+
+struct SessionExceptionErrorCodes {
+    jint kResourceContention;
+} gSessionExceptionErrorCodes;
+
 struct HDCPLevels {
     jint kHdcpLevelUnknown;
     jint kHdcpNone;
@@ -180,6 +191,7 @@
     EntryFields entry;
     CertificateFields certificate;
     StateExceptionFields stateException;
+    SessionExceptionFields sessionException;
     jclass certificateClassId;
     jclass hashmapClassId;
     jclass arraylistClassId;
@@ -310,6 +322,9 @@
          case DrmPlugin::kDrmPluginEventKeysChange:
             jwhat = gEventWhat.kWhatKeyStatusChange;
             break;
+         case DrmPlugin::kDrmPluginEventSessionLostState:
+            jwhat = gEventWhat.kWhatSessionLostState;
+            break;
         default:
             ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
             return;
@@ -343,6 +358,30 @@
     env->Throw(static_cast<jthrowable>(exception));
 }
 
+static void throwSessionException(JNIEnv *env, const char *msg, status_t err) {
+    ALOGE("Session exception: %s (%d)", msg, err);
+
+    jint jErrorCode = 0;
+    switch(err) {
+        case ERROR_DRM_RESOURCE_CONTENTION:
+            jErrorCode = gSessionExceptionErrorCodes.kResourceContention;
+            break;
+        default:
+            break;
+    }
+
+    jobject exception = env->NewObject(gFields.sessionException.classId,
+            gFields.sessionException.init, static_cast<int>(err),
+            env->NewStringUTF(msg));
+
+    env->SetIntField(exception, gFields.sessionException.errorCode, jErrorCode);
+    env->Throw(static_cast<jthrowable>(exception));
+}
+
+static bool isSessionException(status_t err) {
+    return err == ERROR_DRM_RESOURCE_CONTENTION;
+}
+
 static bool throwExceptionAsNecessary(
         JNIEnv *env, status_t err, const char *msg = NULL) {
 
@@ -370,7 +409,7 @@
     case ERROR_DRM_CANNOT_HANDLE:
         drmMessage = "Invalid parameter or data format";
         break;
-    case ERROR_DRM_TAMPER_DETECTED:
+    case ERROR_DRM_INVALID_STATE:
         drmMessage = "Invalid state";
         break;
     default:
@@ -399,6 +438,9 @@
         jniThrowException(env, "android/media/MediaDrmResetException",
                 "mediaserver died");
         return true;
+    } else if (isSessionException(err)) {
+        throwSessionException(env, msg, err);
+        return true;
     } else if (err != OK) {
         String8 errbuf;
         if (drmMessage != NULL) {
@@ -705,6 +747,8 @@
     gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field);
     GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I");
     gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "SESSION_LOST_STATE", "I");
+    gEventWhat.kWhatSessionLostState = env->GetStaticIntField(clazz, field);
 
     GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
     gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
@@ -831,6 +875,14 @@
     FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
     GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
     gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+    FIND_CLASS(clazz, "android/media/MediaDrm$SessionException");
+    GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+    gFields.sessionException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+    GET_FIELD_ID(gFields.sessionException.errorCode, clazz, "mErrorCode", "I");
+
+    GET_STATIC_FIELD_ID(field, clazz, "ERROR_RESOURCE_CONTENTION", "I");
+    gSessionExceptionErrorCodes.kResourceContention = env->GetStaticIntField(clazz, field);
 }
 
 static void android_media_MediaDrm_native_setup(
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 7e6a8ab..9b4e730 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -86,7 +86,8 @@
 // ----------------------------------------------------------------------------
 
 struct fields_t {
-    jfieldID    context;
+    jfieldID    context;               // passed from Java to native, used for creating JWakeLock
+    jfieldID    nativeContext;         // mNativeContext in MediaPlayer2.java
     jfieldID    surface_texture;
 
     jmethodID   post_event;
@@ -225,21 +226,21 @@
 static sp<MediaPlayer2> getMediaPlayer(JNIEnv* env, jobject thiz)
 {
     Mutex::Autolock l(sLock);
-    MediaPlayer2* const p = (MediaPlayer2*)env->GetLongField(thiz, fields.context);
+    MediaPlayer2* const p = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
     return sp<MediaPlayer2>(p);
 }
 
 static sp<MediaPlayer2> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer2>& player)
 {
     Mutex::Autolock l(sLock);
-    sp<MediaPlayer2> old = (MediaPlayer2*)env->GetLongField(thiz, fields.context);
+    sp<MediaPlayer2> old = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
     if (player.get()) {
         player->incStrong((void*)setMediaPlayer);
     }
     if (old != 0) {
         old->decStrong((void*)setMediaPlayer);
     }
-    env->SetLongField(thiz, fields.context, (jlong)player.get());
+    env->SetLongField(thiz, fields.nativeContext, (jlong)player.get());
     return old;
 }
 
@@ -955,11 +956,16 @@
         return;
     }
 
-    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
+    fields.context = env->GetFieldID(clazz, "mContext", "Landroid/content/Context;");
     if (fields.context == NULL) {
         return;
     }
 
+    fields.nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
+    if (fields.nativeContext == NULL) {
+        return;
+    }
+
     fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                                "(Ljava/lang/Object;JIII[B)V");
     if (fields.post_event == NULL) {
@@ -1013,7 +1019,8 @@
         jint sessionId, jobject weak_this)
 {
     ALOGV("native_setup");
-    sp<MediaPlayer2> mp = MediaPlayer2::Create(sessionId);
+    jobject context = env->GetObjectField(thiz, fields.context);
+    sp<MediaPlayer2> mp = MediaPlayer2::Create(sessionId, context);
     if (mp == NULL) {
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 8c43683..44f8725 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -18,5 +18,7 @@
     name: "com.android.mediadrm.signer",
     srcs: ["java/**/*.java"],
     api_packages: ["com.android.mediadrm.signer"],
-    metalava_enabled: false,
+    srcs_lib: "framework",
+    srcs_lib_whitelist_dirs: ["media/java"],
+    srcs_lib_whitelist_pkgs: ["android.media"],
 }
diff --git a/media/packages/MediaCore/Android.bp b/media/packages/MediaCore/Android.bp
new file mode 100644
index 0000000..c7fd58b
--- /dev/null
+++ b/media/packages/MediaCore/Android.bp
@@ -0,0 +1,21 @@
+android_app {
+    name: "MediaCore",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        // TODO: Temporarily statically linked. Should go into "libs"
+        "media1",
+    ],
+
+    // System app
+    platform_apis: true,
+
+    // Privileged app
+    privileged: true,
+
+    // Make sure that the implementation only relies on SDK or system APIs.
+    sdk_version: "system_current",
+}
diff --git a/media/packages/MediaCore/AndroidManifest.xml b/media/packages/MediaCore/AndroidManifest.xml
new file mode 100644
index 0000000..4e2b274
--- /dev/null
+++ b/media/packages/MediaCore/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/AndroidManifest.xml
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.media" coreApp="true" android:sharedUserId="android.uid.system"
+    android:sharedUserLabel="@string/android_system_label">
+    <application android:process="system"
+        android:persistent="true"
+        android:directBootAware="true">
+        <service android:name="AmlMediaSessionProviderService" android:singleUser="true">
+            <intent-filter>
+                <action android:name="android.media.session.MediaSessionProviderService"/>
+            </intent-filter>
+        </service>
+    </application>
+</manifest>
diff --git a/media/packages/MediaCore/res/values/strings.xml b/media/packages/MediaCore/res/values/strings.xml
new file mode 100644
index 0000000..59fd635
--- /dev/null
+++ b/media/packages/MediaCore/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Label for the Android system components when they are shown to the user. -->
+    <string name="android_system_label" translatable="false">Android System</string>
+</resources>
+
diff --git a/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java b/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java
new file mode 100644
index 0000000..43b95ab
--- /dev/null
+++ b/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 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.media;
+
+import android.content.Context;
+import android.media.session.MediaSessionProviderService;
+import android.os.PowerManager;
+import android.util.Log;
+
+/**
+ * System implementation of MediaSessionProviderService
+ */
+public class AmlMediaSessionProviderService extends MediaSessionProviderService {
+    private static final String TAG = "AmlMediaSessionProviderS";
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private Context mContext;
+
+    public AmlMediaSessionProviderService(Context context) {
+        mContext = context;
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+    }
+}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 5cfb09b..73d4c45 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -49,6 +49,7 @@
         "sharedmem.cpp",
         "storage_manager.cpp",
         "surface_texture.cpp",
+        "surface_control.cpp",
         "system_fonts.cpp",
         "trace.cpp",
     ],
@@ -83,6 +84,10 @@
     include_dirs: ["bionic/libc/dns/include"],
 
     version_script: "libandroid.map.txt",
+    stubs: {
+        symbol_file: "libandroid.map.txt",
+        versions: ["29"],
+    },
 }
 
 // Network library.
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 537aed4..8be8eda 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -205,6 +205,9 @@
     AStorageManager_mountObb;
     AStorageManager_new;
     AStorageManager_unmountObb;
+    ASurfaceControl_create; # introduced=29
+    ASurfaceControl_createFromWindow; # introduced=29
+    ASurfaceControl_destroy; # introduced=29
     ASurfaceTexture_acquireANativeWindow; # introduced=28
     ASurfaceTexture_attachToGLContext; # introduced=28
     ASurfaceTexture_detachFromGLContext; # introduced=28
@@ -213,6 +216,16 @@
     ASurfaceTexture_getTransformMatrix; # introduced=28
     ASurfaceTexture_release; # introduced=28
     ASurfaceTexture_updateTexImage; # introduced=28
+    ASurfaceTransaction_apply; # introduced=29
+    ASurfaceTransaction_create; # introduced=29
+    ASurfaceTransaction_delete; # introduced=29
+    ASurfaceTransaction_setBuffer; # introduced=29
+    ASurfaceTransaction_setBufferTransparency; # introduced=29
+    ASurfaceTransaction_setDamageRegion; # introduced=29
+    ASurfaceTransaction_setGeometry; # introduced=29
+    ASurfaceTransaction_setOnComplete; # introduced=29
+    ASurfaceTransaction_setVisibility; # introduced=29
+    ASurfaceTransaction_setZOrder; # introduced=29
     ASystemFontIterator_open; # introduced=29
     ASystemFontIterator_close; # introduced=29
     ASystemFontIterator_next; # introduced=29
diff --git a/native/android/net.c b/native/android/net.c
index 4cac371..a8104fc 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -84,26 +84,28 @@
     return android_getaddrinfofornet(node, service, hints, netid, 0, res);
 }
 
-int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type) {
+int android_res_nquery(net_handle_t network, const char *dname,
+        int ns_class, int ns_type, enum ResNsendFlags flags) {
     unsigned netid;
     if (!getnetidfromhandle(network, &netid)) {
         return -ENONET;
     }
 
-    return resNetworkQuery(netid, dname, ns_class, ns_type);
+    return resNetworkQuery(netid, dname, ns_class, ns_type, flags);
 }
 
 int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) {
     return resNetworkResult(fd, rcode, answer, anslen);
 }
 
-int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen) {
+int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen,
+        enum ResNsendFlags flags) {
     unsigned netid;
     if (!getnetidfromhandle(network, &netid)) {
         return -ENONET;
     }
 
-    return resNetworkSend(netid, msg, msglen);
+    return resNetworkSend(netid, msg, msglen, flags);
 }
 
 void android_res_cancel(int nsend_fd) {
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 8e58210..c3b2e25 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -342,3 +342,8 @@
     RETURN_IF_SENSOR_IS_NULL(ASENSOR_DIRECT_RATE_STOP);
     return static_cast<Sensor const *>(sensor)->getHighestDirectReportRateLevel();
 }
+
+int ASensor_getHandle(ASensor const* sensor) {
+    RETURN_IF_SENSOR_IS_NULL(ASENSOR_INVALID);
+    return static_cast<Sensor const*>(sensor)->getHandle();
+}
diff --git a/native/android/sharedmem.cpp b/native/android/sharedmem.cpp
index 757aaec..4410bd6 100644
--- a/native/android/sharedmem.cpp
+++ b/native/android/sharedmem.cpp
@@ -71,7 +71,7 @@
     }
     int fd = env->CallIntMethod(javaSharedMemory, sSharedMemory.getFd);
     if (fd != -1) {
-        fd = dup(fd);
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
     }
     return fd;
 }
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
new file mode 100644
index 0000000..ead5b0b
--- /dev/null
+++ b/native/android/surface_control.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#include <android/native_window.h>
+#include <android/surface_control.h>
+
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+using namespace android;
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+#define CHECK_NOT_NULL(name) \
+    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+#define CHECK_VALID_RECT(name)                                     \
+    LOG_ALWAYS_FATAL_IF(!static_cast<const Rect&>(name).isValid(), \
+                        "invalid arg passed as " #name " argument");
+
+Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
+    return reinterpret_cast<Transaction*>(aSurfaceTransaction);
+}
+
+SurfaceControl* ASurfaceControl_to_SurfaceControl(ASurfaceControl* aSurfaceControl) {
+    return reinterpret_cast<SurfaceControl*>(aSurfaceControl);
+}
+
+void SurfaceControl_acquire(SurfaceControl* surfaceControl) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    surfaceControl->incStrong((void*)SurfaceControl_acquire);
+}
+
+void SurfaceControl_release(SurfaceControl* surfaceControl) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    surfaceControl->decStrong((void*)SurfaceControl_acquire);
+}
+
+ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const char* debug_name) {
+    CHECK_NOT_NULL(window);
+    CHECK_NOT_NULL(debug_name);
+
+    sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+    if (client->initCheck() != NO_ERROR) {
+        return nullptr;
+    }
+
+    uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
+    sp<SurfaceControl> surfaceControl =
+            client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
+                                            // Format is only relevant for buffer queue layers.
+                                            PIXEL_FORMAT_UNKNOWN /* format */, flags,
+                                            static_cast<Surface*>(window));
+    if (!surfaceControl) {
+        return nullptr;
+    }
+
+    SurfaceControl_acquire(surfaceControl.get());
+    return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
+}
+
+ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) {
+    CHECK_NOT_NULL(parent);
+    CHECK_NOT_NULL(debug_name);
+
+    SurfaceComposerClient* client = ASurfaceControl_to_SurfaceControl(parent)->getClient().get();
+
+    SurfaceControl* surfaceControlParent = ASurfaceControl_to_SurfaceControl(parent);
+
+    uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
+    sp<SurfaceControl> surfaceControl =
+            client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
+                                  // Format is only relevant for buffer queue layers.
+                                  PIXEL_FORMAT_UNKNOWN /* format */, flags,
+                                  surfaceControlParent);
+    if (!surfaceControl) {
+        return nullptr;
+    }
+
+    SurfaceControl_acquire(surfaceControl.get());
+    return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
+}
+
+void ASurfaceControl_destroy(ASurfaceControl* aSurfaceControl) {
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+
+    Transaction().reparent(surfaceControl, nullptr).apply();
+    SurfaceControl_release(surfaceControl.get());
+}
+
+ASurfaceTransaction* ASurfaceTransaction_create() {
+    Transaction* transaction = new Transaction;
+    return reinterpret_cast<ASurfaceTransaction*>(transaction);
+}
+
+void ASurfaceTransaction_delete(ASurfaceTransaction* aSurfaceTransaction) {
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+    delete transaction;
+}
+
+void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->apply();
+}
+
+void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, void* context,
+                                       ASurfaceTransaction_OnComplete func) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(context);
+    CHECK_NOT_NULL(func);
+
+    TransactionCompletedCallbackTakesContext callback = [func](void* callback_context,
+                                                               const TransactionStats& stats) {
+        int fence = (stats.presentFence) ? stats.presentFence->dup() : -1;
+        (*func)(callback_context, fence);
+    };
+
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->addTransactionCompletedCallback(callback, context);
+}
+
+void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                       int8_t visibility) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    switch (visibility) {
+    case ASURFACE_TRANSACTION_VISIBILITY_SHOW:
+        transaction->show(surfaceControl);
+        break;
+    case ASURFACE_TRANSACTION_VISIBILITY_HIDE:
+        transaction->hide(surfaceControl);
+        break;
+    default:
+        LOG_ALWAYS_FATAL("invalid visibility %d", visibility);
+    }
+}
+
+void ASurfaceTransaction_setZOrder(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                   int32_t z_order) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->setLayer(surfaceControl, z_order);
+}
+
+void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                   AHardwareBuffer* buffer, int fence_fd) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer));
+
+    transaction->setBuffer(surfaceControl, graphic_buffer);
+    if (fence_fd != -1) {
+        sp<Fence> fence = new Fence(fence_fd);
+        transaction->setAcquireFence(surfaceControl, fence);
+    }
+}
+
+void ASurfaceTransaction_setGeometry(ASurfaceTransaction* aSurfaceTransaction,
+                                     ASurfaceControl* aSurfaceControl, const ARect& source,
+                                     const ARect& destination, int32_t transform) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+    CHECK_VALID_RECT(source);
+    CHECK_VALID_RECT(destination);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->setCrop(surfaceControl, static_cast<const Rect&>(source));
+    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));
+    transaction->setTransform(surfaceControl, transform);
+}
+
+void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* aSurfaceTransaction,
+                                               ASurfaceControl* aSurfaceControl,
+                                               int8_t transparency) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    uint32_t flags = (transparency == ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE) ?
+                      layer_state_t::eLayerOpaque : 0;
+    transaction->setFlags(surfaceControl, flags, layer_state_t::eLayerOpaque);
+}
+
+void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                         const ARect rects[], uint32_t count) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    Region region;
+    for (uint32_t i = 0; i < count; ++i) {
+        region.merge(static_cast<const Rect&>(rects[i]));
+    }
+
+    transaction->setSurfaceDamageRegion(surfaceControl, region);
+}
diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp
index 0936256..88decc8 100644
--- a/native/webview/plat_support/Android.bp
+++ b/native/webview/plat_support/Android.bp
@@ -24,7 +24,6 @@
     srcs: [
         "draw_functor.cpp",
         "draw_gl_functor.cpp",
-        "draw_vk_functor.cpp",
         "functor_utils.cpp",
         "jni_entry_point.cpp",
         "graphics_utils.cpp",
diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
index 6afd883..0490e65 100644
--- a/native/webview/plat_support/draw_fn.h
+++ b/native/webview/plat_support/draw_fn.h
@@ -74,7 +74,10 @@
   VkQueue queue;
   uint32_t graphics_queue_index;
   uint32_t instance_version;
-  const char* const* enabled_extension_names;
+  const char* const* enabled_instance_extension_names;
+  uint32_t enabled_instance_extension_names_length;
+  const char* const* enabled_device_extension_names;
+  uint32_t enabled_device_extension_names_length;
   // Only one of device_features and device_features_2 should be non-null.
   // If both are null then no features are enabled.
   VkPhysicalDeviceFeatures* device_features;
@@ -109,8 +112,15 @@
   // Input: Format of the destination surface.
   VkFormat format;
 
-  // Input: Color space transformation from linear RGB to D50-adapted XYZ
-  float matrix[9];
+  // Input: Color space parameters.
+  float transfer_function_g;
+  float transfer_function_a;
+  float transfer_function_b;
+  float transfer_function_c;
+  float transfer_function_d;
+  float transfer_function_e;
+  float transfer_function_f;
+  float color_space_toXYZD50[9];
 
   // Input: current clip rect
   int clip_left;
@@ -121,15 +131,13 @@
 
 struct AwDrawFn_PostDrawVkParams {
   int version;
-
-  // Input: Fence for the composite command buffer to signal it has finished its
-  // work on the GPU.
-  int fd;
 };
 
 // Called on render thread while UI thread is blocked. Called for both GL and
 // VK.
-typedef void AwDrawFn_OnSync(int functor, void* data, AwDrawFn_OnSyncParams* params);
+typedef void AwDrawFn_OnSync(int functor,
+                             void* data,
+                             AwDrawFn_OnSyncParams* params);
 
 // Called on render thread when either the context is destroyed _or_ when the
 // functor's last reference goes away. Will always be called with an active
@@ -143,17 +151,24 @@
 typedef void AwDrawFn_OnDestroyed(int functor, void* data);
 
 // Only called for GL.
-typedef void AwDrawFn_DrawGL(int functor, void* data, AwDrawFn_DrawGLParams* params);
+typedef void AwDrawFn_DrawGL(int functor,
+                             void* data,
+                             AwDrawFn_DrawGLParams* params);
 
 // Initialize vulkan state. Needs to be called again after any
 // OnContextDestroyed. Only called for Vulkan.
-typedef void AwDrawFn_InitVk(int functor, void* data, AwDrawFn_InitVkParams* params);
+typedef void AwDrawFn_InitVk(int functor,
+                             void* data,
+                             AwDrawFn_InitVkParams* params);
 
 // Only called for Vulkan.
-typedef void AwDrawFn_DrawVk(int functor, void* data, AwDrawFn_DrawVkParams* params);
+typedef void AwDrawFn_DrawVk(int functor,
+                             void* data,
+                             AwDrawFn_DrawVkParams* params);
 
 // Only called for Vulkan.
-typedef void AwDrawFn_PostDrawVk(int functor, void* data,
+typedef void AwDrawFn_PostDrawVk(int functor,
+                                 void* data,
                                  AwDrawFn_PostDrawVkParams* params);
 
 struct AwDrawFnFunctorCallbacks {
@@ -176,7 +191,8 @@
 typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void);
 
 // Create a functor. |functor_callbacks| should be valid until OnDestroyed.
-typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks);
+typedef int AwDrawFn_CreateFunctor(void* data,
+                                   AwDrawFnFunctorCallbacks* functor_callbacks);
 
 // May be called on any thread to signal that the functor should be destroyed.
 // The functor will receive an onDestroyed when the last usage of it is
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 820bac5..b97bbc3 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -74,6 +74,79 @@
   support->callbacks.draw_gl(functor, support->data, &params);
 }
 
+void initializeVk(int functor, void* data,
+                  const uirenderer::VkFunctorInitParams& init_vk_params) {
+  SupportData* support = static_cast<SupportData*>(data);
+  VkPhysicalDeviceFeatures2 device_features_2;
+  if (init_vk_params.device_features_2)
+    device_features_2 = *init_vk_params.device_features_2;
+
+  AwDrawFn_InitVkParams params{
+      .version = kAwDrawFnVersion,
+      .instance = init_vk_params.instance,
+      .physical_device = init_vk_params.physical_device,
+      .device = init_vk_params.device,
+      .queue = init_vk_params.queue,
+      .graphics_queue_index = init_vk_params.graphics_queue_index,
+      .instance_version = init_vk_params.instance_version,
+      .enabled_instance_extension_names =
+          init_vk_params.enabled_instance_extension_names,
+      .enabled_instance_extension_names_length =
+          init_vk_params.enabled_instance_extension_names_length,
+      .enabled_device_extension_names =
+          init_vk_params.enabled_device_extension_names,
+      .enabled_device_extension_names_length =
+          init_vk_params.enabled_device_extension_names_length,
+      .device_features = nullptr,
+      .device_features_2 =
+          init_vk_params.device_features_2 ? &device_features_2 : nullptr,
+  };
+  support->callbacks.init_vk(functor, support->data, &params);
+}
+
+void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw_vk_params) {
+  SupportData* support = static_cast<SupportData*>(data);
+  float gabcdef[7];
+  draw_vk_params.color_space_ptr->transferFn(gabcdef);
+  AwDrawFn_DrawVkParams params{
+      .version = kAwDrawFnVersion,
+      .width = draw_vk_params.width,
+      .height = draw_vk_params.height,
+      .is_layer = draw_vk_params.is_layer,
+      .secondary_command_buffer = draw_vk_params.secondary_command_buffer,
+      .color_attachment_index = draw_vk_params.color_attachment_index,
+      .compatible_render_pass = draw_vk_params.compatible_render_pass,
+      .format = draw_vk_params.format,
+      .transfer_function_g = gabcdef[0],
+      .transfer_function_a = gabcdef[1],
+      .transfer_function_b = gabcdef[2],
+      .transfer_function_c = gabcdef[3],
+      .transfer_function_d = gabcdef[4],
+      .transfer_function_e = gabcdef[5],
+      .transfer_function_f = gabcdef[6],
+      .clip_left = draw_vk_params.clip_left,
+      .clip_top = draw_vk_params.clip_top,
+      .clip_right = draw_vk_params.clip_right,
+      .clip_bottom = draw_vk_params.clip_bottom,
+  };
+  COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3),
+                 gamut_transform_size_mismatch);
+  draw_vk_params.color_space_ptr->toXYZD50(
+      reinterpret_cast<skcms_Matrix3x3*>(&params.color_space_toXYZD50));
+  COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_vk_params.transform),
+                 mismatched_transform_matrix_sizes);
+  for (int i = 0; i < NELEM(params.transform); ++i) {
+    params.transform[i] = draw_vk_params.transform[i];
+  }
+  support->callbacks.draw_vk(functor, support->data, &params);
+}
+
+void postDrawVk(int functor, void* data) {
+  SupportData* support = static_cast<SupportData*>(data);
+  AwDrawFn_PostDrawVkParams params{.version = kAwDrawFnVersion};
+  support->callbacks.post_draw_vk(functor, support->data, &params);
+}
+
 int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
   static bool callbacks_initialized = false;
   static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = {
@@ -87,6 +160,12 @@
         webview_functor_callbacks.gles.draw = &draw_gl;
         break;
       case uirenderer::RenderMode::Vulkan:
+        webview_functor_callbacks.vk.initialize = &initializeVk;
+        webview_functor_callbacks.vk.draw = &drawVk;
+        webview_functor_callbacks.vk.postDraw = &postDrawVk;
+        // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor
+        // no longer uses GL interop.
+        webview_functor_callbacks.gles.draw = &draw_gl;
         break;
     }
     callbacks_initialized = true;
diff --git a/native/webview/plat_support/draw_vk.h b/native/webview/plat_support/draw_vk.h
deleted file mode 100644
index 6b7d8d0..0000000
--- a/native/webview/plat_support/draw_vk.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-//******************************************************************************
-// This is a copy of the coresponding android_webview/public/browser header.
-// Any changes to the interface should be made there.
-//
-// The purpose of having the copy is twofold:
-//  - it removes the need to have Chromium sources present in the tree in order
-//    to build the plat_support library,
-//  - it captures API that the corresponding Android release supports.
-//******************************************************************************
-
-#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
-#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
-
-#include <vulkan/vulkan.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static const int kAwDrawVKInfoVersion = 1;
-
-// Holds the information required to trigger initialization of the Vulkan
-// functor.
-struct InitParams {
-  // All params are input
-  VkInstance instance;
-  VkPhysicalDevice physical_device;
-  VkDevice device;
-  VkQueue queue;
-  uint32_t graphics_queue_index;
-  uint32_t instance_version;
-  const char* const* enabled_extension_names;
-  // Only one of device_features and device_features_2 should be non-null.
-  // If both are null then no features are enabled.
-  VkPhysicalDeviceFeatures* device_features;
-  VkPhysicalDeviceFeatures2* device_features_2;
-};
-
-// Holds the information required to trigger an Vulkan composite operation.
-struct CompositeParams {
-  // Input: current width/height of destination surface.
-  int width;
-  int height;
-
-  // Input: is the render target a FBO
-  bool is_layer;
-
-  // Input: current transform matrix
-  float transform[16];
-
-  // Input WebView should do its main compositing draws into this. It cannot do
-  // anything that would require stopping the render pass.
-  VkCommandBuffer secondary_command_buffer;
-
-  // Input: The main color attachment index where secondary_command_buffer will
-  // eventually be submitted.
-  uint32_t color_attachment_index;
-
-  // Input: A render pass which will be compatible to the one which the
-  // secondary_command_buffer will be submitted into.
-  VkRenderPass compatible_render_pass;
-
-  // Input: Format of the destination surface.
-  VkFormat format;
-
-  // Input: Color space transfer params
-  float G;
-  float A;
-  float B;
-  float C;
-  float D;
-  float E;
-  float F;
-
-  // Input: Color space transformation from linear RGB to D50-adapted XYZ
-  float matrix[9];
-
-  // Input: current clip rect
-  int clip_left;
-  int clip_top;
-  int clip_right;
-  int clip_bottom;
-};
-
-// Holds the information for the post-submission callback of main composite
-// draw.
-struct PostCompositeParams {
-  // Input: Fence for the composite command buffer to signal it has finished its
-  // work on the GPU.
-  int fd;
-};
-
-// Holds the information required to trigger an Vulkan operation.
-struct AwDrawVKInfo {
-  int version;  // The AwDrawVKInfo this struct was built with.
-
-  // Input: tells the draw function what action to perform.
-  enum Mode {
-    kModeInit = 0,
-    kModeReInit = 1,
-    kModePreComposite = 2,
-    kModeComposite = 3,
-    kModePostComposite = 4,
-    kModeSync = 5,
-  } mode;
-
-  // Input: The parameters for the functor being called
-  union ParamUnion {
-    struct InitParams init_params;
-    struct CompositeParams composite_params;
-    struct PostCompositeParams post_composite_params;
-  } info;
-};
-
-typedef void(AwDrawVKFunction)(long view_context, AwDrawVKInfo* draw_info);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
diff --git a/native/webview/plat_support/draw_vk_functor.cpp b/native/webview/plat_support/draw_vk_functor.cpp
deleted file mode 100644
index eab1340..0000000
--- a/native/webview/plat_support/draw_vk_functor.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.
- */
-
-// Provides a webviewchromium glue layer adapter from the internal Android
-// Vulkan Functor data types into the types the chromium stack expects, and
-// back.
-
-#define LOG_TAG "webviewchromium_plat_support"
-
-#include "draw_fn.h"
-#include "draw_vk.h"
-
-#include <jni.h>
-#include <private/hwui/DrawVkInfo.h>
-#include <utils/Functor.h>
-#include <utils/Log.h>
-
-#include "functor_utils.h"
-
-#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
-
-namespace android {
-namespace {
-
-AwDrawVKFunction* g_aw_drawvk_function = NULL;
-
-class DrawVKFunctor : public Functor {
- public:
-  explicit DrawVKFunctor(jlong view_context) : view_context_(view_context) {}
-  ~DrawVKFunctor() override {}
-
-  // Functor
-  status_t operator ()(int what, void* data) override {
-    using uirenderer::DrawVkInfo;
-    if (!g_aw_drawvk_function) {
-      ALOGE("Cannot draw: no DrawVK Function installed");
-      return DrawVkInfo::kStatusDone;
-    }
-
-    AwDrawVKInfo aw_info;
-    aw_info.version = kAwDrawVKInfoVersion;
-    switch (what) {
-      case DrawVkInfo::kModeComposite: {
-        aw_info.mode = AwDrawVKInfo::kModeComposite;
-        DrawVkInfo* vk_info = reinterpret_cast<DrawVkInfo*>(data);
-
-        // Map across the input values.
-        CompositeParams& params = aw_info.info.composite_params;
-        params.width = vk_info->width;
-        params.height = vk_info->height;
-        params.is_layer = vk_info->isLayer;
-        for (size_t i = 0; i < 16; i++) {
-            params.transform[i] = vk_info->transform[i];
-        }
-        params.secondary_command_buffer = vk_info->secondaryCommandBuffer;
-        params.color_attachment_index = vk_info->colorAttachmentIndex;
-        params.compatible_render_pass = vk_info->compatibleRenderPass;
-        params.format = vk_info->format;
-        params.G = vk_info->G;
-        params.A = vk_info->A;
-        params.B = vk_info->B;
-        params.C = vk_info->C;
-        params.D = vk_info->D;
-        params.E = vk_info->E;
-        params.F = vk_info->F;
-        for (size_t i = 0; i < 9; i++) {
-            params.matrix[i] = vk_info->matrix[i];
-        }
-        params.clip_left = vk_info->clipLeft;
-        params.clip_top = vk_info->clipTop;
-        params.clip_right = vk_info->clipRight;
-        params.clip_bottom = vk_info->clipBottom;
-
-        break;
-      }
-      case DrawVkInfo::kModePostComposite:
-        break;
-      case DrawVkInfo::kModeSync:
-        aw_info.mode = AwDrawVKInfo::kModeSync;
-        break;
-      default:
-        ALOGE("Unexpected DrawVKInfo type %d", what);
-        return DrawVkInfo::kStatusDone;
-    }
-
-    // Invoke the DrawVK method.
-    g_aw_drawvk_function(view_context_, &aw_info);
-
-    return DrawVkInfo::kStatusDone;
-  }
-
- private:
-  intptr_t view_context_;
-};
-
-jlong CreateVKFunctor(JNIEnv*, jclass, jlong view_context) {
-  RaiseFileNumberLimit();
-  return reinterpret_cast<jlong>(new DrawVKFunctor(view_context));
-}
-
-void DestroyVKFunctor(JNIEnv*, jclass, jlong functor) {
-  delete reinterpret_cast<DrawVKFunctor*>(functor);
-}
-
-void SetChromiumAwDrawVKFunction(JNIEnv*, jclass, jlong draw_function) {
-  g_aw_drawvk_function = reinterpret_cast<AwDrawVKFunction*>(draw_function);
-}
-
-const char kClassName[] = "com/android/webview/chromium/DrawVKFunctor";
-const JNINativeMethod kJniMethods[] = {
-    { "nativeCreateVKFunctor", "(J)J",
-        reinterpret_cast<void*>(CreateVKFunctor) },
-    { "nativeDestroyVKFunctor", "(J)V",
-        reinterpret_cast<void*>(DestroyVKFunctor) },
-    { "nativeSetChromiumAwDrawVKFunction", "(J)V",
-        reinterpret_cast<void*>(SetChromiumAwDrawVKFunction) },
-};
-
-}  // namespace
-
-void RegisterDrawVKFunctor(JNIEnv* env) {
-  jclass clazz = env->FindClass(kClassName);
-  LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName);
-
-  int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods));
-  LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res);
-}
-
-}  // namespace android
diff --git a/packages/AppPredictionLib/Android.bp b/packages/AppPredictionLib/Android.bp
new file mode 100644
index 0000000..e0f4ded
--- /dev/null
+++ b/packages/AppPredictionLib/Android.bp
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_library {
+    name: "app_prediction",
+
+    sdk_version: "system_current",
+    min_sdk_version: "system_current",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+}
diff --git a/packages/AppPredictionLib/AndroidManifest.xml b/packages/AppPredictionLib/AndroidManifest.xml
new file mode 100644
index 0000000..b992788
--- /dev/null
+++ b/packages/AppPredictionLib/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.app.prediction">
+</manifest>
diff --git a/packages/AppPredictionLib/src/com/android/app/prediction/Constants.java b/packages/AppPredictionLib/src/com/android/app/prediction/Constants.java
new file mode 100644
index 0000000..0993c9a
--- /dev/null
+++ b/packages/AppPredictionLib/src/com/android/app/prediction/Constants.java
@@ -0,0 +1,71 @@
+/*
+ * 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.app.prediction;
+
+/**
+ * Constants to be used with {@link android.app.prediction.AppPredictor}.
+ */
+public class Constants {
+
+    /**
+     * UI surface for predictions displayed on the user's home screen
+     */
+    public static final String UI_SURFACE_HOME = "home";
+
+    /**
+     * UI surface for predictions displayed on the recents/task switcher view
+     */
+    public static final String UI_SURFACE_RECENTS = "recents";
+
+    /**
+     * UI surface for predictions displayed on the share sheet.
+     */
+    public static final String UI_SURFACE_SHARE = "share";
+
+    /**
+     * Location constant when an app target or shortcut is started from the apps list
+     */
+    public static final String LAUNCH_LOCATION_APPS_LIST = "apps_list";
+
+    /**
+     * Location constant when an app target or shortcut is started from the user's home screen
+     */
+    public static final String LAUNCH_LOCATION_APPS_HOME = "home";
+
+    /**
+     * Location constant when an app target or shortcut is started from task switcher
+     */
+    public static final String LAUNCH_LOCATION_APPS_RECENTS = "recents";
+
+    /**
+     * Location constant when an app target or shortcut is started in the share sheet while it is
+     * in collapsed state (showing a limited set of result).
+     */
+    public static final String LAUNCH_LOCATION_APPS_SHARE_COLLAPSED = "share_collapsed";
+
+    /**
+     * Location constant when an app target or shortcut is started in the share sheet while it is
+     * in expended state and showing all the results.
+     */
+    public static final String LAUNCH_LOCATION_APPS_SHARE_EXPANDED = "shared_expanded";
+
+    /**
+     * Location constant when an app target or shortcut is started in the share sheet when the
+     * target is displayed as a placeholder for an deprecated object.
+     */
+    public static final String LAUNCH_LOCATION_APPS_SHARE_LEGACY = "share_legacy";
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
index 62502ef..6d960d7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
 public class CarNotificationInterruptionStateProvider extends
@@ -29,7 +29,7 @@
     }
 
     @Override
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
+    public boolean shouldHeadsUp(NotificationEntry entry) {
         // 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.
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.mk b/packages/CarrierDefaultApp/tests/unit/Android.mk
index 8e3785e..4c638811 100644
--- a/packages/CarrierDefaultApp/tests/unit/Android.mk
+++ b/packages/CarrierDefaultApp/tests/unit/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules mockito-target-minus-junit4
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml b/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml
index 3a06a09..7a26d95 100644
--- a/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml
@@ -21,7 +21,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.carrierdefaultapp"
         android:label="CarrierDefaultApp Unit Test Cases">
     </instrumentation>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index af52c00..499d493 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -286,6 +286,9 @@
             if (!isForCurrentUser(sbn)) {
                 return;
             }
+
+            mAgingHelper.onNotificationRemoved(sbn.getKey());
+
             boolean updatedImpressions = false;
             String channelId = mLiveNotifications.remove(sbn.getKey()).getChannel().getId();
             String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
@@ -366,9 +369,9 @@
     }
 
     @Override
-    public void onNotificationDirectReply(@NonNull String key) {
-        if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
-        mSmartActionsHelper.onNotificationDirectReply(key);
+    public void onNotificationDirectReplied(@NonNull String key) {
+        if (DEBUG) Log.i(TAG, "onNotificationDirectReplied " + key);
+        mSmartActionsHelper.onNotificationDirectReplied(key);
     }
 
     @Override
@@ -382,11 +385,11 @@
     }
 
     @Override
-    public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+    public void onActionInvoked(@NonNull String key, @NonNull Notification.Action action,
             @Source int source) {
         if (DEBUG) {
             Log.d(TAG,
-                    "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+                    "onActionInvoked() called with: key = [" + key + "], action = [" + action.title
                             + "], source = [" + source + "]");
         }
         mSmartActionsHelper.onActionClicked(key, action, source);
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
index 71fd9ce..acf1180 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
@@ -27,7 +27,9 @@
 import android.app.NotificationChannel;
 import android.app.Person;
 import android.app.RemoteInput;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
 import android.media.AudioSystem;
 import android.os.Build;
@@ -67,8 +69,12 @@
 
     private boolean isPreChannelsNotification() {
         try {
-            mTargetSdkVersion = mPackageManager.getApplicationInfo(
-                    mSbn.getPackageName(), 0, mSbn.getUserId()).targetSdkVersion;
+            ApplicationInfo info = mPackageManager.getApplicationInfo(
+                    mSbn.getPackageName(), PackageManager.MATCH_ALL,
+                    mSbn.getUserId());
+            if (info != null) {
+                mTargetSdkVersion = info.targetSdkVersion;
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Couldn't look up " + mSbn.getPackageName());
         }
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 56c4158..95df5f2 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -27,6 +27,7 @@
 import android.service.notification.NotificationAssistantService;
 import android.text.TextUtils;
 import android.util.LruCache;
+import android.view.textclassifier.ConversationAction;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationContext;
@@ -65,13 +66,13 @@
     private static final int MAX_MESSAGES_TO_EXTRACT = 5;
     private static final int MAX_RESULT_ID_TO_CACHE = 20;
 
-    private static final ConversationActions.TypeConfig TYPE_CONFIG =
-            new ConversationActions.TypeConfig.Builder().setIncludedTypes(
-                    Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+    private static final TextClassifier.EntityConfig TYPE_CONFIG =
+            new TextClassifier.EntityConfig.Builder().setIncludedTypes(
+                    Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
                     .includeTypesFromTextClassifier(false)
                     .build();
     private static final List<String> HINTS =
-            Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION);
+            Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION);
 
     private Context mContext;
     @Nullable
@@ -137,7 +138,7 @@
 
         ConversationActions conversationActionsResult =
                 mTextClassifier.suggestConversationActions(request);
-        List<ConversationActions.ConversationAction> conversationActions =
+        List<ConversationAction> conversationActions =
                 conversationActionsResult.getConversationActions();
         ArrayList<CharSequence> replies = conversationActions.stream()
                 .map(conversationAction -> conversationAction.getTextReply())
@@ -165,7 +166,7 @@
         }
     }
 
-    void onNotificationDirectReply(@NonNull String key) {
+    void onNotificationDirectReplied(@NonNull String key) {
         if (mTextClassifier == null) {
             return;
         }
@@ -193,7 +194,7 @@
         }
         TextClassifierEvent textClassifierEvent =
                 createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
-                        .setEntityType(ConversationActions.TYPE_TEXT_REPLY)
+                        .setEntityType(ConversationAction.TYPE_TEXT_REPLY)
                         .build();
         mTextClassifier.onTextClassifierEvent(textClassifierEvent);
     }
@@ -383,8 +384,7 @@
                         remoteAction.getIcon(),
                         remoteAction.getTitle(),
                         remoteAction.getActionIntent())
-                        .setSemanticAction(
-                                Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+                        .setContextual(true)
                         .addExtras(Bundle.forPair(KEY_ACTION_TYPE, classification.getEntity(0)))
                         .build();
                 actions.add(action);
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index da382a0..7b7ce3d 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -32,6 +32,7 @@
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.ConversationAction;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
@@ -65,9 +66,10 @@
     private static final String NOTIFICATION_KEY = "key";
     private static final String RESULT_ID = "id";
 
-    private static final ConversationActions.ConversationAction REPLY_ACTION =
-            new ConversationActions.ConversationAction.Builder(
-                    ConversationActions.TYPE_TEXT_REPLY).setTextReply("Home").build();
+    private static final ConversationAction REPLY_ACTION =
+            new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY)
+            .setTextReply("Home")
+            .build();
 
     private SmartActionsHelper mSmartActionsHelper;
     private Context mContext;
@@ -241,7 +243,7 @@
         when(mNotificationEntry.getNotification()).thenReturn(notification);
 
         mSmartActionsHelper.suggestReplies(mNotificationEntry);
-        mSmartActionsHelper.onNotificationDirectReply(NOTIFICATION_KEY);
+        mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY);
 
         ArgumentCaptor<TextClassifierEvent> argumentCaptor =
                 ArgumentCaptor.forClass(TextClassifierEvent.class);
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 55bb517..2f7d599 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -21,7 +21,11 @@
     installable: true,
     srcs: [
         "src/**/*.java",
+        ":services-networkstack-shared-srcs",
     ],
+    static_libs: [
+        "dhcp-packet-lib",
+    ]
 }
 
 // Updatable network stack packaged as an application
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index d1c5cb6..7f8bb93 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -17,13 +17,17 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.google.android.networkstack"
+          package="com.android.mainline.networkstack"
           android:sharedUserId="android.uid.networkstack">
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <!-- Launch captive portal app as specific user -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.NETWORK_STACK" />
     <application
         android:label="NetworkStack"
         android:defaultToDeviceProtectedStorage="true"
diff --git a/services/net/java/android/net/dhcp/DhcpLease.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
similarity index 90%
rename from services/net/java/android/net/dhcp/DhcpLease.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
index 6cdd2aa..6849cfa 100644
--- a/services/net/java/android/net/dhcp/DhcpLease.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
@@ -58,6 +58,11 @@
         mHostname = hostname;
     }
 
+    /**
+     * Get the clientId associated with this lease, if any.
+     *
+     * <p>If the lease is not associated to a clientId, this returns null.
+     */
     @Nullable
     public byte[] getClientId() {
         if (mClientId == null) {
@@ -97,6 +102,11 @@
                 (hostname == null ? mHostname : hostname));
     }
 
+    /**
+     * Determine whether this lease matches a client with the specified parameters.
+     * @param clientId clientId of the client if any, or null otherwise.
+     * @param hwAddr Hardware address of the client.
+     */
     public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
         if (mClientId != null) {
             return Arrays.equals(mClientId, clientId);
@@ -110,7 +120,7 @@
         if (!(obj instanceof DhcpLease)) {
             return false;
         }
-        final DhcpLease other = (DhcpLease)obj;
+        final DhcpLease other = (DhcpLease) obj;
         return Arrays.equals(mClientId, other.mClientId)
                 && mHwAddr.equals(other.mHwAddr)
                 && mNetAddr.equals(other.mNetAddr)
diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
similarity index 98%
rename from services/net/java/android/net/dhcp/DhcpLeaseRepository.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
index 2dda421..0d298de 100644
--- a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
@@ -21,7 +21,8 @@
 import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
 import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
 import static android.net.dhcp.DhcpLease.inet4AddrToString;
-import static android.net.util.NetworkConstants.IPV4_ADDR_BITS;
+
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS;
 
 import static java.lang.Math.min;
 
@@ -29,8 +30,8 @@
 import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.MacAddress;
-import android.net.util.SharedLog;
 import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
 import android.util.ArrayMap;
 
 import java.net.Inet4Address;
@@ -117,7 +118,7 @@
      */
     private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>();
 
-    public DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
+    DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
             long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
         updateParams(prefix, reservedAddrs, leaseTimeMs);
         mLog = log;
@@ -250,8 +251,8 @@
                 // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr.
                 // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration.
                 // In both cases, throw if clientAddr or reqAddr does not match the known lease.
-                throw new InvalidAddressException("Incorrect address for client in " +
-                        (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
+                throw new InvalidAddressException("Incorrect address for client in "
+                        + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
             }
         }
 
diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
similarity index 79%
rename from services/net/java/android/net/dhcp/DhcpPacketListener.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
index 6f620c5..dce8b61 100644
--- a/services/net/java/android/net/dhcp/DhcpPacketListener.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
@@ -32,32 +32,32 @@
  */
 abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> {
     static final class Payload {
-        final byte[] bytes = new byte[DhcpPacket.MAX_LENGTH];
-        Inet4Address srcAddr;
-        int srcPort;
+        protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH];
+        protected Inet4Address mSrcAddr;
+        protected int mSrcPort;
     }
 
-    public DhcpPacketListener(@NonNull Handler handler) {
+    DhcpPacketListener(@NonNull Handler handler) {
         super(handler, new Payload());
     }
 
     @Override
     protected int recvBufSize(@NonNull Payload buffer) {
-        return buffer.bytes.length;
+        return buffer.mBytes.length;
     }
 
     @Override
     protected final void handlePacket(@NonNull Payload recvbuf, int length) {
-        if (recvbuf.srcAddr == null) {
+        if (recvbuf.mSrcAddr == null) {
             return;
         }
 
         try {
-            final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.bytes, length,
+            final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length,
                     DhcpPacket.ENCAP_BOOTP);
-            onReceive(packet, recvbuf.srcAddr, recvbuf.srcPort);
+            onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort);
         } catch (DhcpPacket.ParseException e) {
-            logParseError(recvbuf.bytes, length, e);
+            logParseError(recvbuf.mBytes, length, e);
         }
     }
 
@@ -66,11 +66,11 @@
             throws Exception {
         final InetSocketAddress addr = new InetSocketAddress();
         final int read = Os.recvfrom(
-                fd, packetBuffer.bytes, 0, packetBuffer.bytes.length, 0 /* flags */, addr);
+                fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr);
 
         // Buffers with null srcAddr will be dropped in handlePacket()
-        packetBuffer.srcAddr = inet4AddrOrNull(addr);
-        packetBuffer.srcPort = addr.getPort();
+        packetBuffer.mSrcAddr = inet4AddrOrNull(addr);
+        packetBuffer.mSrcPort = addr.getPort();
         return read;
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
similarity index 78%
rename from services/net/java/android/net/dhcp/DhcpServer.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 35d29e7..14e2936 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -23,7 +23,8 @@
 import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
 import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
 import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
-import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
+import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
@@ -32,21 +33,28 @@
 import static android.system.OsConstants.SO_BROADCAST;
 import static android.system.OsConstants.SO_REUSEADDR;
 
+import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
+import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
+
 import static java.lang.Integer.toUnsignedLong;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.INetworkStackStatusCallback;
 import android.net.MacAddress;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
 import android.net.util.SharedLog;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
@@ -70,7 +78,7 @@
  * on the looper asynchronously.
  * @hide
  */
-public class DhcpServer {
+public class DhcpServer extends IDhcpServer.Stub {
     private static final String REPO_TAG = "Repository";
 
     // Lease time to transmit to client instead of a negative time in case a lease expired before
@@ -82,7 +90,7 @@
     private static final int CMD_UPDATE_PARAMS = 3;
 
     @NonNull
-    private final ServerHandler mHandler;
+    private final HandlerThread mHandlerThread;
     @NonNull
     private final String mIfName;
     @NonNull
@@ -93,14 +101,25 @@
     private final Dependencies mDeps;
     @NonNull
     private final Clock mClock;
-    @NonNull
-    private final DhcpPacketListener mPacketListener;
 
     @Nullable
+    private volatile ServerHandler mHandler;
+
+    // Accessed only on the handler thread
+    @Nullable
+    private DhcpPacketListener mPacketListener;
+    @Nullable
     private FileDescriptor mSocket;
     @NonNull
     private DhcpServingParams mServingParams;
 
+    /**
+     * Clock to be used by DhcpServer to track time for lease expiration.
+     *
+     * <p>The clock should track time as may be measured by clients obtaining a lease. It does not
+     * need to be monotonous across restarts of the server as long as leases are cleared when the
+     * server is stopped.
+     */
     public static class Clock {
         /**
          * @see SystemClock#elapsedRealtime()
@@ -110,15 +129,51 @@
         }
     }
 
+    /**
+     * Dependencies for the DhcpServer. Useful to be mocked in tests.
+     */
     public interface Dependencies {
+        /**
+         * Send a packet to the specified datagram socket.
+         *
+         * @param fd File descriptor of the socket.
+         * @param buffer Data to be sent.
+         * @param dst Destination address of the packet.
+         */
         void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
                 @NonNull InetAddress dst) throws ErrnoException, IOException;
+
+        /**
+         * Create a DhcpLeaseRepository for the server.
+         * @param servingParams Parameters used to serve DHCP requests.
+         * @param log Log to be used by the repository.
+         * @param clock Clock that the repository must use to track time.
+         */
         DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
                 @NonNull SharedLog log, @NonNull Clock clock);
+
+        /**
+         * Create a packet listener that will send packets to be processed.
+         */
         DhcpPacketListener makePacketListener();
+
+        /**
+         * Create a clock that the server will use to track time.
+         */
         Clock makeClock();
+
+        /**
+         * Add an entry to the ARP cache table.
+         * @param fd Datagram socket file descriptor that must use the new entry.
+         */
         void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
                 @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
+
+        /**
+         * Verify that the caller is allowed to call public methods on DhcpServer.
+         * @throws SecurityException The caller is not allowed to call public methods on DhcpServer.
+         */
+        void checkCaller() throws SecurityException;
     }
 
     private class DependenciesImpl implements Dependencies {
@@ -134,7 +189,7 @@
             return new DhcpLeaseRepository(
                     DhcpServingParams.makeIpPrefix(servingParams.serverAddr),
                     servingParams.excludedAddrs,
-                    servingParams.dhcpLeaseTimeSecs*1000, log.forSubComponent(REPO_TAG), clock);
+                    servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock);
         }
 
         @Override
@@ -152,6 +207,11 @@
                 @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
             NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
         }
+
+        @Override
+        public void checkCaller() {
+            checkNetworkStackCallingPermission();
+        }
     }
 
     private static class MalformedPacketException extends Exception {
@@ -160,41 +220,62 @@
         }
     }
 
-    public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
+    public DhcpServer(@NonNull String ifName,
             @NonNull DhcpServingParams params, @NonNull SharedLog log) {
-        this(looper, ifName, params, log, null);
+        this(new HandlerThread(DhcpServer.class.getSimpleName() + "." + ifName),
+                ifName, params, log, null);
     }
 
     @VisibleForTesting
-    DhcpServer(@NonNull Looper looper, @NonNull String ifName,
+    DhcpServer(@NonNull HandlerThread handlerThread, @NonNull String ifName,
             @NonNull DhcpServingParams params, @NonNull SharedLog log,
             @Nullable Dependencies deps) {
         if (deps == null) {
             deps = new DependenciesImpl();
         }
-        mHandler = new ServerHandler(looper);
+        mHandlerThread = handlerThread;
         mIfName = ifName;
         mServingParams = params;
         mLog = log;
         mDeps = deps;
         mClock = deps.makeClock();
-        mPacketListener = deps.makePacketListener();
         mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock);
     }
 
     /**
      * Start listening for and responding to packets.
+     *
+     * <p>It is not legal to call this method more than once; in particular the server cannot be
+     * restarted after being stopped.
      */
-    public void start() {
-        mHandler.sendEmptyMessage(CMD_START_DHCP_SERVER);
+    @Override
+    public void start(@Nullable INetworkStackStatusCallback cb) {
+        mDeps.checkCaller();
+        mHandlerThread.start();
+        mHandler = new ServerHandler(mHandlerThread.getLooper());
+        sendMessage(CMD_START_DHCP_SERVER, cb);
     }
 
     /**
      * Update serving parameters. All subsequently received requests will be handled with the new
      * parameters, and current leases that are incompatible with the new parameters are dropped.
      */
-    public void updateParams(@NonNull DhcpServingParams params) {
-        sendMessage(CMD_UPDATE_PARAMS, params);
+    @Override
+    public void updateParams(@Nullable DhcpServingParamsParcel params,
+            @Nullable INetworkStackStatusCallback cb) throws RemoteException {
+        mDeps.checkCaller();
+        final DhcpServingParams parsedParams;
+        try {
+            // throws InvalidParameterException with null params
+            parsedParams = DhcpServingParams.fromParcelableObject(params);
+        } catch (DhcpServingParams.InvalidParameterException e) {
+            mLog.e("Invalid parameters sent to DhcpServer", e);
+            if (cb != null) {
+                cb.onStatusAvailable(STATUS_INVALID_ARGUMENT);
+            }
+            return;
+        }
+        sendMessage(CMD_UPDATE_PARAMS, new Pair<>(parsedParams, cb));
     }
 
     /**
@@ -203,38 +284,63 @@
      * <p>As the server is stopped asynchronously, some packets may still be processed shortly after
      * calling this method.
      */
-    public void stop() {
-        mHandler.sendEmptyMessage(CMD_STOP_DHCP_SERVER);
+    @Override
+    public void stop(@Nullable INetworkStackStatusCallback cb) {
+        mDeps.checkCaller();
+        sendMessage(CMD_STOP_DHCP_SERVER, cb);
     }
 
     private void sendMessage(int what, @Nullable Object obj) {
+        if (mHandler == null) {
+            mLog.e("Attempting to send a command to stopped DhcpServer: " + what);
+            return;
+        }
         mHandler.sendMessage(mHandler.obtainMessage(what, obj));
     }
 
     private class ServerHandler extends Handler {
-        public ServerHandler(@NonNull Looper looper) {
+        ServerHandler(@NonNull Looper looper) {
             super(looper);
         }
 
         @Override
         public void handleMessage(@NonNull Message msg) {
+            final INetworkStackStatusCallback cb;
             switch (msg.what) {
                 case CMD_UPDATE_PARAMS:
-                    final DhcpServingParams params = (DhcpServingParams) msg.obj;
+                    final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
+                            (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
+                    final DhcpServingParams params = pair.first;
                     mServingParams = params;
                     mLeaseRepo.updateParams(
                             DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
                             params.excludedAddrs,
                             params.dhcpLeaseTimeSecs);
+
+                    cb = pair.second;
                     break;
                 case CMD_START_DHCP_SERVER:
-                    // This is a no-op if the listener is already started
+                    mPacketListener = mDeps.makePacketListener();
                     mPacketListener.start();
+                    cb = (INetworkStackStatusCallback) msg.obj;
                     break;
                 case CMD_STOP_DHCP_SERVER:
-                    // This is a no-op if the listener was not started
-                    mPacketListener.stop();
+                    if (mPacketListener != null) {
+                        mPacketListener.stop();
+                        mPacketListener = null;
+                    }
+                    mHandlerThread.quitSafely();
+                    cb = (INetworkStackStatusCallback) msg.obj;
                     break;
+                default:
+                    return;
+            }
+            if (cb != null) {
+                try {
+                    cb.onStatusAvailable(STATUS_SUCCESS);
+                } catch (RemoteException e) {
+                    mLog.e("Could not send status back to caller", e);
+                }
             }
         }
     }
@@ -496,22 +602,24 @@
     }
 
     private class PacketListener extends DhcpPacketListener {
-        public PacketListener() {
+        PacketListener() {
             super(mHandler);
         }
 
         @Override
-        protected void onReceive(DhcpPacket packet, Inet4Address srcAddr, int srcPort) {
+        protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
+                int srcPort) {
             processPacket(packet, srcPort);
         }
 
         @Override
-        protected void logError(String msg, Exception e) {
+        protected void logError(@NonNull String msg, Exception e) {
             mLog.e("Error receiving packet: " + msg, e);
         }
 
         @Override
-        protected void logParseError(byte[] packet, int length, DhcpPacket.ParseException e) {
+        protected void logParseError(@NonNull byte[] packet, int length,
+                @NonNull DhcpPacket.ParseException e) {
             mLog.e("Error parsing packet", e);
         }
 
@@ -533,7 +641,7 @@
                 return mSocket;
             } catch (IOException | ErrnoException e) {
                 mLog.e("Error creating UDP socket", e);
-                DhcpServer.this.stop();
+                DhcpServer.this.stop(null);
                 return null;
             } finally {
                 TrafficStats.setThreadStatsTag(oldTag);
diff --git a/services/net/java/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
similarity index 73%
rename from services/net/java/android/net/dhcp/DhcpServingParams.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
index df15ba1..f38888a 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParams.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
@@ -17,13 +17,16 @@
 package android.net.dhcp;
 
 import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
-import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
-import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
-import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
+
+import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
+import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
+import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU;
 
 import static java.lang.Integer.toUnsignedLong;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
@@ -103,6 +106,41 @@
         this.metered = metered;
     }
 
+    /**
+     * Create parameters from a stable AIDL-compatible parcel.
+     * @throws InvalidParameterException The parameters parcelable is null or invalid.
+     */
+    public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel)
+            throws InvalidParameterException {
+        if (parcel == null) {
+            throw new InvalidParameterException("Null serving parameters");
+        }
+        final LinkAddress serverAddr = new LinkAddress(
+                intToInet4AddressHTH(parcel.serverAddr),
+                parcel.serverAddrPrefixLength);
+        return new Builder()
+                .setServerAddr(serverAddr)
+                .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters))
+                .setDnsServers(toInet4AddressSet(parcel.dnsServers))
+                .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs))
+                .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs)
+                .setLinkMtu(parcel.linkMtu)
+                .setMetered(parcel.metered)
+                .build();
+    }
+
+    private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) {
+        if (addrs == null) {
+            return new HashSet<>(0);
+        }
+
+        final HashSet<Inet4Address> res = new HashSet<>();
+        for (int addr : addrs) {
+            res.add(intToInet4AddressHTH(addr));
+        }
+        return res;
+    }
+
     @NonNull
     public Inet4Address getServerInet4Addr() {
         return (Inet4Address) serverAddr.getAddress();
@@ -134,13 +172,13 @@
      * of the parameters.
      */
     public static class Builder {
-        private LinkAddress serverAddr;
-        private Set<Inet4Address> defaultRouters;
-        private Set<Inet4Address> dnsServers;
-        private Set<Inet4Address> excludedAddrs;
-        private long dhcpLeaseTimeSecs;
-        private int linkMtu = MTU_UNSET;
-        private boolean metered;
+        private LinkAddress mServerAddr;
+        private Set<Inet4Address> mDefaultRouters;
+        private Set<Inet4Address> mDnsServers;
+        private Set<Inet4Address> mExcludedAddrs;
+        private long mDhcpLeaseTimeSecs;
+        private int mLinkMtu = MTU_UNSET;
+        private boolean mMetered;
 
         /**
          * Set the server address and served prefix for the DHCP server.
@@ -148,7 +186,7 @@
          * <p>This parameter is required.
          */
         public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
-            this.serverAddr = serverAddr;
+            this.mServerAddr = serverAddr;
             return this;
         }
 
@@ -159,7 +197,7 @@
          * always be set explicitly before building the {@link DhcpServingParams}.
          */
         public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
-            this.defaultRouters = defaultRouters;
+            this.mDefaultRouters = defaultRouters;
             return this;
         }
 
@@ -189,7 +227,7 @@
          * {@link DhcpServingParams}.
          */
         public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
-            this.dnsServers = dnsServers;
+            this.mDnsServers = dnsServers;
             return this;
         }
 
@@ -219,7 +257,7 @@
          * and do not need to be set here.
          */
         public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
-            this.excludedAddrs = excludedAddrs;
+            this.mExcludedAddrs = excludedAddrs;
             return this;
         }
 
@@ -239,7 +277,7 @@
          * <p>This parameter is required.
          */
         public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
-            this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+            this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
             return this;
         }
 
@@ -250,7 +288,7 @@
          * is optional and defaults to {@link #MTU_UNSET}.
          */
         public Builder setLinkMtu(int linkMtu) {
-            this.linkMtu = linkMtu;
+            this.mLinkMtu = linkMtu;
             return this;
         }
 
@@ -260,7 +298,7 @@
          * <p>If not set, the default value is false.
          */
         public Builder setMetered(boolean metered) {
-            this.metered = metered;
+            this.mMetered = metered;
             return this;
         }
 
@@ -274,54 +312,57 @@
          */
         @NonNull
         public DhcpServingParams build() throws InvalidParameterException {
-            if (serverAddr == null) {
+            if (mServerAddr == null) {
                 throw new InvalidParameterException("Missing serverAddr");
             }
-            if (defaultRouters == null) {
+            if (mDefaultRouters == null) {
                 throw new InvalidParameterException("Missing defaultRouters");
             }
-            if (dnsServers == null) {
+            if (mDnsServers == null) {
                 // Empty set is OK, but enforce explicitly setting it
                 throw new InvalidParameterException("Missing dnsServers");
             }
-            if (dhcpLeaseTimeSecs <= 0 || dhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
-                throw new InvalidParameterException("Invalid lease time: " + dhcpLeaseTimeSecs);
+            if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
+                throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs);
             }
-            if (linkMtu != MTU_UNSET && (linkMtu < IPV4_MIN_MTU || linkMtu > IPV4_MAX_MTU)) {
-                throw new InvalidParameterException("Invalid link MTU: " + linkMtu);
+            if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) {
+                throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu);
             }
-            if (!serverAddr.isIPv4()) {
+            if (!mServerAddr.isIPv4()) {
                 throw new InvalidParameterException("serverAddr must be IPv4");
             }
-            if (serverAddr.getPrefixLength() < MIN_PREFIX_LENGTH
-                    || serverAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
+            if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH
+                    || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
                 throw new InvalidParameterException("Prefix length is not in supported range");
             }
 
-            final IpPrefix prefix = makeIpPrefix(serverAddr);
-            for (Inet4Address addr : defaultRouters) {
+            final IpPrefix prefix = makeIpPrefix(mServerAddr);
+            for (Inet4Address addr : mDefaultRouters) {
                 if (!prefix.contains(addr)) {
                     throw new InvalidParameterException(String.format(
-                            "Default router %s is not in server prefix %s", addr, serverAddr));
+                            "Default router %s is not in server prefix %s", addr, mServerAddr));
                 }
             }
 
             final Set<Inet4Address> excl = new HashSet<>();
-            if (excludedAddrs != null) {
-                excl.addAll(excludedAddrs);
+            if (mExcludedAddrs != null) {
+                excl.addAll(mExcludedAddrs);
             }
-            excl.add((Inet4Address) serverAddr.getAddress());
-            excl.addAll(defaultRouters);
-            excl.addAll(dnsServers);
+            excl.add((Inet4Address) mServerAddr.getAddress());
+            excl.addAll(mDefaultRouters);
+            excl.addAll(mDnsServers);
 
-            return new DhcpServingParams(serverAddr,
-                    Collections.unmodifiableSet(new HashSet<>(defaultRouters)),
-                    Collections.unmodifiableSet(new HashSet<>(dnsServers)),
+            return new DhcpServingParams(mServerAddr,
+                    Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)),
+                    Collections.unmodifiableSet(new HashSet<>(mDnsServers)),
                     Collections.unmodifiableSet(excl),
-                    dhcpLeaseTimeSecs, linkMtu, metered);
+                    mDhcpLeaseTimeSecs, mLinkMtu, mMetered);
         }
     }
 
+    /**
+     * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress.
+     */
     @NonNull
     static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) {
         return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
diff --git a/packages/NetworkStack/src/android/net/util/SharedLog.java b/packages/NetworkStack/src/android/net/util/SharedLog.java
new file mode 100644
index 0000000..4fabf10
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/util/SharedLog.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.StringJoiner;
+
+
+/**
+ * Class to centralize logging functionality for tethering.
+ *
+ * All access to class methods other than dump() must be on the same thread.
+ *
+ * @hide
+ */
+public class SharedLog {
+    private static final int DEFAULT_MAX_RECORDS = 500;
+    private static final String COMPONENT_DELIMITER = ".";
+
+    private enum Category {
+        NONE,
+        ERROR,
+        MARK,
+        WARN,
+    };
+
+    private final LocalLog mLocalLog;
+    // The tag to use for output to the system log. This is not output to the
+    // LocalLog because that would be redundant.
+    private final String mTag;
+    // The component (or subcomponent) of a system that is sharing this log.
+    // This can grow in depth if components call forSubComponent() to obtain
+    // their SharedLog instance. The tag is not included in the component for
+    // brevity.
+    private final String mComponent;
+
+    public SharedLog(String tag) {
+        this(DEFAULT_MAX_RECORDS, tag);
+    }
+
+    public SharedLog(int maxRecords, String tag) {
+        this(new LocalLog(maxRecords), tag, tag);
+    }
+
+    private SharedLog(LocalLog localLog, String tag, String component) {
+        mLocalLog = localLog;
+        mTag = tag;
+        mComponent = component;
+    }
+
+    public String getTag() {
+        return mTag;
+    }
+
+    /**
+     * Create a SharedLog based on this log with an additional component prefix on each logged line.
+     */
+    public SharedLog forSubComponent(String component) {
+        if (!isRootLogInstance()) {
+            component = mComponent + COMPONENT_DELIMITER + component;
+        }
+        return new SharedLog(mLocalLog, mTag, component);
+    }
+
+    /**
+     * Dump the contents of this log.
+     *
+     * <p>This method may be called on any thread.
+     */
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
+    }
+
+    //////
+    // Methods that both log an entry and emit it to the system log.
+    //////
+
+    /**
+     * Log an error due to an exception. This does not include the exception stacktrace.
+     *
+     * <p>The log entry will be also added to the system log.
+     * @see #e(String, Throwable)
+     */
+    public void e(Exception e) {
+        Log.e(mTag, record(Category.ERROR, e.toString()));
+    }
+
+    /**
+     * Log an error message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
+    public void e(String msg) {
+        Log.e(mTag, record(Category.ERROR, msg));
+    }
+
+    /**
+     * Log an error due to an exception, with the exception stacktrace if provided.
+     *
+     * <p>The error and exception message appear in the shared log, but the stacktrace is only
+     * logged in general log output (logcat). The log entry will be also added to the system log.
+     */
+    public void e(@NonNull String msg, @Nullable Throwable exception) {
+        if (exception == null) {
+            e(msg);
+            return;
+        }
+        Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
+    }
+
+    /**
+     * Log an informational message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
+    public void i(String msg) {
+        Log.i(mTag, record(Category.NONE, msg));
+    }
+
+    /**
+     * Log a warning message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
+    public void w(String msg) {
+        Log.w(mTag, record(Category.WARN, msg));
+    }
+
+    //////
+    // Methods that only log an entry (and do NOT emit to the system log).
+    //////
+
+    /**
+     * Log a general message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
+    public void log(String msg) {
+        record(Category.NONE, msg);
+    }
+
+    /**
+     * Log a general, formatted message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     * @see String#format(String, Object...)
+     */
+    public void logf(String fmt, Object... args) {
+        log(String.format(fmt, args));
+    }
+
+    /**
+     * Log a message with MARK level.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
+    public void mark(String msg) {
+        record(Category.MARK, msg);
+    }
+
+    private String record(Category category, String msg) {
+        final String entry = logLine(category, msg);
+        mLocalLog.log(entry);
+        return entry;
+    }
+
+    private String logLine(Category category, String msg) {
+        final StringJoiner sj = new StringJoiner(" ");
+        if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
+        if (category != Category.NONE) sj.add(category.toString());
+        return sj.add(msg).toString();
+    }
+
+    // Check whether this SharedLog instance is nominally the top level in
+    // a potential hierarchy of shared logs (the root of a tree),
+    // or is a subcomponent within the hierarchy.
+    private boolean isRootLogInstance() {
+        return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
+    }
+}
diff --git a/services/net/java/android/net/util/Stopwatch.java b/packages/NetworkStack/src/android/net/util/Stopwatch.java
similarity index 78%
rename from services/net/java/android/net/util/Stopwatch.java
rename to packages/NetworkStack/src/android/net/util/Stopwatch.java
index cb15ee5..c316699 100644
--- a/services/net/java/android/net/util/Stopwatch.java
+++ b/packages/NetworkStack/src/android/net/util/Stopwatch.java
@@ -38,9 +38,9 @@
         return (isStarted() && !isStopped());
     }
 
-    // Returning |this| makes possible the following usage pattern:
-    //
-    //     Stopwatch s = new Stopwatch().start();
+    /**
+     * Start the Stopwatch.
+     */
     public Stopwatch start() {
         if (!isStarted()) {
             mStartTimeMs = SystemClock.elapsedRealtime();
@@ -48,7 +48,10 @@
         return this;
     }
 
-    // Returns the total time recorded, in milliseconds, or 0 if not started.
+    /**
+     * Stop the Stopwatch.
+     * @return the total time recorded, in milliseconds, or 0 if not started.
+     */
     public long stop() {
         if (isRunning()) {
             mStopTimeMs = SystemClock.elapsedRealtime();
@@ -57,9 +60,11 @@
         return (mStopTimeMs - mStartTimeMs);
     }
 
-    // Returns the total time recorded to date, in milliseconds.
-    // If the Stopwatch is not running, returns the same value as stop(),
-    // i.e. either the total time recorded before stopping or 0.
+    /**
+     * Return the total time recorded to date, in milliseconds.
+     * If the Stopwatch is not running, returns the same value as stop(),
+     * i.e. either the total time recorded before stopping or 0.
+     */
     public long lap() {
         if (isRunning()) {
             return (SystemClock.elapsedRealtime() - mStartTimeMs);
@@ -68,6 +73,9 @@
         }
     }
 
+    /**
+     * Reset the Stopwatch. It will be stopped when this method returns.
+     */
     public void reset() {
         mStartTimeMs = 0;
         mStopTimeMs = 0;
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 5afaf58..057012d 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -16,18 +16,40 @@
 
 package com.android.server;
 
-import static android.os.Binder.getCallingUid;
+import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
+
+import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkStackConnector;
+import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.PrivateDnsConfigParcel;
+import android.net.dhcp.DhcpServer;
+import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.shared.PrivateDnsConfig;
+import android.net.util.SharedLog;
 import android.os.IBinder;
-import android.os.Process;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.connectivity.NetworkMonitor;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 
 /**
  * Android service used to start the network stack when bound to via an intent.
@@ -43,32 +65,163 @@
      * <p>On platforms where the network stack runs in the system server process, this method may
      * be called directly instead of obtaining the connector by binding to the service.
      */
-    public static IBinder makeConnector() {
-        return new NetworkStackConnector();
+    public static IBinder makeConnector(Context context) {
+        return new NetworkStackConnector(context);
     }
 
     @NonNull
     @Override
     public IBinder onBind(Intent intent) {
-        return makeConnector();
+        return makeConnector(this);
     }
 
     private static class NetworkStackConnector extends INetworkStackConnector.Stub {
-        // TODO: makeDhcpServer(), etc. will go here.
+        private static final int NUM_VALIDATION_LOG_LINES = 20;
+        private final Context mContext;
+        private final ConnectivityManager mCm;
+
+        private static final int MAX_VALIDATION_LOGS = 10;
+        @GuardedBy("mValidationLogs")
+        private final ArrayDeque<SharedLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS);
+
+        private SharedLog addValidationLogs(Network network, String name) {
+            final SharedLog log = new SharedLog(NUM_VALIDATION_LOG_LINES, network + " - " + name);
+            synchronized (mValidationLogs) {
+                while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
+                    mValidationLogs.removeLast();
+                }
+                mValidationLogs.addFirst(log);
+            }
+            return log;
+        }
+
+        NetworkStackConnector(Context context) {
+            mContext = context;
+            mCm = context.getSystemService(ConnectivityManager.class);
+        }
+
+        @NonNull
+        private final SharedLog mLog = new SharedLog(TAG);
+
+        @Override
+        public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params,
+                @NonNull IDhcpServerCallbacks cb) throws RemoteException {
+            checkNetworkStackCallingPermission();
+            final DhcpServer server;
+            try {
+                server = new DhcpServer(
+                        ifName,
+                        DhcpServingParams.fromParcelableObject(params),
+                        mLog.forSubComponent(ifName + ".DHCP"));
+            } catch (DhcpServingParams.InvalidParameterException e) {
+                mLog.e("Invalid DhcpServingParams", e);
+                cb.onDhcpServerCreated(STATUS_INVALID_ARGUMENT, null);
+                return;
+            } catch (Exception e) {
+                mLog.e("Unknown error starting DhcpServer", e);
+                cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+                return;
+            }
+            cb.onDhcpServerCreated(STATUS_SUCCESS, server);
+        }
+
+        @Override
+        public void makeNetworkMonitor(int netId, String name, INetworkMonitorCallbacks cb)
+                throws RemoteException {
+            final Network network = new Network(netId, false /* privateDnsBypass */);
+            final NetworkRequest defaultRequest = mCm.getDefaultRequest();
+            final SharedLog log = addValidationLogs(network, name);
+            final NetworkMonitor nm = new NetworkMonitor(
+                    mContext, cb, network, defaultRequest, log);
+            cb.onNetworkMonitorCreated(new NetworkMonitorImpl(nm));
+        }
 
         @Override
         protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
                 @Nullable String[] args) {
-            checkCaller();
-            fout.println("NetworkStack logs:");
-            // TODO: dump logs here
+            checkNetworkStackCallingPermission();
+            final IndentingPrintWriter pw = new IndentingPrintWriter(fout, "  ");
+            pw.println("NetworkStack logs:");
+            mLog.dump(fd, pw, args);
+
+            pw.println();
+            pw.println("Validation logs (most recent first):");
+            synchronized (mValidationLogs) {
+                for (SharedLog p : mValidationLogs) {
+                    pw.println(p.getTag());
+                    pw.increaseIndent();
+                    p.dump(fd, pw, args);
+                    pw.decreaseIndent();
+                }
+            }
         }
     }
 
-    private static void checkCaller() {
-        // TODO: check that the calling PID is the system server.
-        if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
-            throw new SecurityException("Invalid caller: " + getCallingUid());
+    private static class NetworkMonitorImpl extends INetworkMonitor.Stub {
+        private final NetworkMonitor mNm;
+
+        NetworkMonitorImpl(NetworkMonitor nm) {
+            mNm = nm;
+        }
+
+        @Override
+        public void start() {
+            checkNetworkStackCallingPermission();
+            mNm.start();
+        }
+
+        @Override
+        public void launchCaptivePortalApp() {
+            checkNetworkStackCallingPermission();
+            mNm.launchCaptivePortalApp();
+        }
+
+        @Override
+        public void forceReevaluation(int uid) {
+            checkNetworkStackCallingPermission();
+            mNm.forceReevaluation(uid);
+        }
+
+        @Override
+        public void notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
+            checkNetworkStackCallingPermission();
+            mNm.notifyPrivateDnsSettingsChanged(PrivateDnsConfig.fromParcel(config));
+        }
+
+        @Override
+        public void notifyDnsResponse(int returnCode) {
+            checkNetworkStackCallingPermission();
+            mNm.notifyDnsResponse(returnCode);
+        }
+
+        @Override
+        public void notifySystemReady() {
+            checkNetworkStackCallingPermission();
+            mNm.notifySystemReady();
+        }
+
+        @Override
+        public void notifyNetworkConnected() {
+            checkNetworkStackCallingPermission();
+            mNm.notifyNetworkConnected();
+        }
+
+        @Override
+        public void notifyNetworkDisconnected() {
+            checkNetworkStackCallingPermission();
+            mNm.notifyNetworkDisconnected();
+        }
+
+        @Override
+        public void notifyLinkPropertiesChanged() {
+            checkNetworkStackCallingPermission();
+            mNm.notifyLinkPropertiesChanged();
+        }
+
+        @Override
+        public void notifyNetworkCapabilitiesChanged() {
+            checkNetworkStackCallingPermission();
+            mNm.notifyNetworkCapabilitiesChanged();
         }
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
similarity index 81%
rename from services/core/java/com/android/server/connectivity/NetworkMonitor.java
rename to packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 9684f4c..4077d93 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -21,6 +21,11 @@
 import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
 import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
 import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE;
 import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS;
 import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
@@ -35,6 +40,9 @@
 import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
 import android.net.ICaptivePortal;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
@@ -46,11 +54,14 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.metrics.ValidationProbeEvent;
+import android.net.shared.NetworkMonitorUtils;
+import android.net.shared.PrivateDnsConfig;
+import android.net.util.SharedLog;
 import android.net.util.Stopwatch;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -65,8 +76,6 @@
 import android.telephony.CellInfoWcdma;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.LocalLog.ReadOnlyLocalLog;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -75,7 +84,6 @@
 import com.android.internal.util.RingBufferIndices;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
-import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
 
 import java.io.IOException;
 import java.net.HttpURLConnection;
@@ -104,9 +112,7 @@
     // Default configuration values for captive portal detection probes.
     // TODO: append a random length parameter to the default HTTPS url.
     // TODO: randomize browser version ids in the default User-Agent String.
-    private static final String DEFAULT_HTTPS_URL     = "https://www.google.com/generate_204";
-    private static final String DEFAULT_HTTP_URL      =
-            "http://connectivitycheck.gstatic.com/generate_204";
+    private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204";
     private static final String DEFAULT_FALLBACK_URL  = "http://www.google.com/gen_204";
     private static final String DEFAULT_OTHER_FALLBACK_URLS =
             "http://play.googleapis.com/generate_204";
@@ -126,51 +132,30 @@
     private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
             (1 << DATA_STALL_EVALUATION_TYPE_DNS);
 
-    static enum EvaluationResult {
+    enum EvaluationResult {
         VALIDATED(true),
         CAPTIVE_PORTAL(false);
-        final boolean isValidated;
+        final boolean mIsValidated;
         EvaluationResult(boolean isValidated) {
-            this.isValidated = isValidated;
+            this.mIsValidated = isValidated;
         }
     }
 
-    static enum ValidationStage {
+    enum ValidationStage {
         FIRST_VALIDATION(true),
         REVALIDATION(false);
-        final boolean isFirstValidation;
+        final boolean mIsFirstValidation;
         ValidationStage(boolean isFirstValidation) {
-            this.isFirstValidation = isFirstValidation;
+            this.mIsFirstValidation = isFirstValidation;
         }
     }
 
-    // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
-    // The network should be used as a default internet connection.  It was found to be:
-    // 1. a functioning network providing internet access, or
-    // 2. a captive portal and the user decided to use it as is.
-    public static final int NETWORK_TEST_RESULT_VALID = 0;
-    // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
-    // The network should not be used as a default internet connection.  It was found to be:
-    // 1. a captive portal and the user is prompted to sign-in, or
-    // 2. a captive portal and the user did not want to use it, or
-    // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed).
-    public static final int NETWORK_TEST_RESULT_INVALID = 1;
-
     private static final int BASE = Protocol.BASE_NETWORK_MONITOR;
-
     /**
-     * Inform NetworkMonitor that their network is connected.
+     * ConnectivityService has sent a notification to indicate that network has connected.
      * Initiates Network Validation.
      */
-    public static final int CMD_NETWORK_CONNECTED = BASE + 1;
-
-    /**
-     * Inform ConnectivityService that the network has been tested.
-     * obj = String representing URL that Internet probe was redirect to, if it was redirected.
-     * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
-     * arg2 = NetID.
-     */
-    public static final int EVENT_NETWORK_TESTED = BASE + 2;
+    private static final int CMD_NETWORK_CONNECTED = BASE + 1;
 
     /**
      * Message to self indicating it's time to evaluate a network's connectivity.
@@ -179,9 +164,9 @@
     private static final int CMD_REEVALUATE = BASE + 6;
 
     /**
-     * Inform NetworkMonitor that the network has disconnected.
+     * ConnectivityService has sent a notification to indicate that network has disconnected.
      */
-    public static final int CMD_NETWORK_DISCONNECTED = BASE + 7;
+    private static final int CMD_NETWORK_DISCONNECTED = BASE + 7;
 
     /**
      * Force evaluation even if it has succeeded in the past.
@@ -199,21 +184,13 @@
     private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
 
     /**
-     * Request ConnectivityService display provisioning notification.
-     * arg1    = Whether to make the notification visible.
-     * arg2    = NetID.
-     * obj     = Intent to be launched when notification selected by user, null if !arg1.
-     */
-    public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10;
-
-    /**
      * Message indicating sign-in app should be launched.
      * Sent by mLaunchCaptivePortalAppBroadcastReceiver when the
      * user touches the sign in notification, or sent by
      * ConnectivityService when the user touches the "sign into
      * network" button in the wifi access point detail page.
      */
-    public static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
+    private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
 
     /**
      * Retest network to see if captive portal is still in place.
@@ -234,7 +211,6 @@
      * validation phase is completed.
      */
     private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
-    public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
     private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
 
     /**
@@ -251,7 +227,7 @@
 
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
-    private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
+    private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
     // Before network has been evaluated this many times, ignore repeated reevaluate requests.
     private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
     private int mReevaluateToken = 0;
@@ -261,25 +237,18 @@
     // Stop blaming UID that requested re-evaluation after this many attempts.
     private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5;
     // Delay between reevaluations once a captive portal has been found.
-    private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10*60*1000;
-
-    private static final int NUM_VALIDATION_LOG_LINES = 20;
+    private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
 
     private String mPrivateDnsProviderHostname = "";
 
-    public static boolean isValidationRequired(
-            NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
-        // TODO: Consider requiring validation for DUN networks.
-        return dfltNetCap.satisfiedByNetworkCapabilities(nc);
-    }
-
     private final Context mContext;
-    private final Handler mConnectivityServiceHandler;
-    private final NetworkAgentInfo mNetworkAgentInfo;
+    private final INetworkMonitorCallbacks mCallback;
     private final Network mNetwork;
+    private final Network mNonPrivateDnsBypassNetwork;
     private final int mNetId;
     private final TelephonyManager mTelephonyManager;
     private final WifiManager mWifiManager;
+    private final ConnectivityManager mCm;
     private final NetworkRequest mDefaultRequest;
     private final IpConnectivityLog mMetricsLog;
     private final Dependencies mDependencies;
@@ -292,6 +261,9 @@
     @Nullable
     private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs;
 
+    private NetworkCapabilities mNetworkCapabilities;
+    private LinkProperties mLinkProperties;
+
     @VisibleForTesting
     protected boolean mIsCaptivePortalCheckEnabled;
 
@@ -304,7 +276,7 @@
     // Avoids surfacing "Sign in to network" notification.
     private boolean mDontDisplaySigninNotification = false;
 
-    public boolean systemReady = false;
+    private volatile boolean mSystemReady = false;
 
     private final State mDefaultState = new DefaultState();
     private final State mValidatedState = new ValidatedState();
@@ -317,7 +289,7 @@
 
     private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
 
-    private final LocalLog validationLogs = new LocalLog(NUM_VALIDATION_LOG_LINES);
+    private final SharedLog mValidationLogs;
 
     private final Stopwatch mEvaluationTimer = new Stopwatch();
 
@@ -328,6 +300,7 @@
     private final Random mRandom;
     private int mNextFallbackUrlIndex = 0;
 
+
     private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
     private int mEvaluateAttempts = 0;
     private volatile int mProbeToken = 0;
@@ -338,17 +311,18 @@
     private final DnsStallDetector mDnsStallDetector;
     private long mLastProbeTime;
 
-    public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
-            NetworkRequest defaultRequest) {
-        this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(),
+    public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
+            NetworkRequest defaultRequest, SharedLog validationLog) {
+        this(context, cb, network, defaultRequest, new IpConnectivityLog(), validationLog,
                 Dependencies.DEFAULT);
     }
 
     @VisibleForTesting
-    protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
-            NetworkRequest defaultRequest, IpConnectivityLog logger, Dependencies deps) {
+    protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
+            NetworkRequest defaultRequest, IpConnectivityLog logger, SharedLog validationLogs,
+            Dependencies deps) {
         // Add suffix indicating which NetworkMonitor we're talking about.
-        super(TAG + networkAgentInfo.name());
+        super(TAG + "/" + network.netId);
 
         // Logs with a tag of the form given just above, e.g.
         //     <timestamp>   862  2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
@@ -356,15 +330,18 @@
 
         mContext = context;
         mMetricsLog = logger;
-        mConnectivityServiceHandler = handler;
+        mValidationLogs = validationLogs;
+        mCallback = cb;
         mDependencies = deps;
-        mNetworkAgentInfo = networkAgentInfo;
-        mNetwork = deps.getNetwork(networkAgentInfo).getPrivateDnsBypassingCopy();
+        mNonPrivateDnsBypassNetwork = network;
+        mNetwork = deps.getPrivateDnsBypassNetwork(network);
         mNetId = mNetwork.netId;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         mDefaultRequest = defaultRequest;
 
+        // CHECKSTYLE:OFF IndentationCheck
         addState(mDefaultState);
         addState(mMaybeNotifyState, mDefaultState);
             addState(mEvaluatingState, mMaybeNotifyState);
@@ -374,12 +351,13 @@
         addState(mEvaluatingPrivateDnsState, mDefaultState);
         addState(mValidatedState, mDefaultState);
         setInitialState(mDefaultState);
+        // CHECKSTYLE:ON IndentationCheck
 
         mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
         mUseHttps = getUseHttpsValidation();
         mCaptivePortalUserAgent = getCaptivePortalUserAgent();
         mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
-        mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(deps, context));
+        mCaptivePortalHttpUrl = makeURL(deps.getCaptivePortalServerHttpUrl(context));
         mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
         mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
         mRandom = deps.getRandom();
@@ -390,13 +368,34 @@
         mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
         mDataStallEvaluationType = getDataStallEvalutionType();
 
-        start();
+        // mLinkProperties and mNetworkCapbilities must never be null or we will NPE.
+        // Provide empty objects in case we are started and the network disconnects before
+        // we can ever fetch them.
+        // TODO: Delete ASAP.
+        mLinkProperties = new LinkProperties();
+        mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities.clearAll();
     }
 
+    /**
+     * Request the NetworkMonitor to reevaluate the network.
+     */
     public void forceReevaluation(int responsibleUid) {
         sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
     }
 
+    /**
+     * Send a notification to NetworkMonitor indicating that there was a DNS query response event.
+     * @param returnCode the DNS return code of the response.
+     */
+    public void notifyDnsResponse(int returnCode) {
+        sendMessage(EVENT_DNS_NOTIFICATION, returnCode);
+    }
+
+    /**
+     * Send a notification to NetworkMonitor indicating that private DNS settings have changed.
+     * @param newCfg The new private DNS configuration.
+     */
     public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
         // Cancel any outstanding resolutions.
         removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
@@ -404,9 +403,75 @@
         sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
     }
 
+    /**
+     * Send a notification to NetworkMonitor indicating that the system is ready.
+     */
+    public void notifySystemReady() {
+        // No need to run on the handler thread: mSystemReady is volatile and read only once on the
+        // isCaptivePortal() thread.
+        mSystemReady = true;
+    }
+
+    /**
+     * Send a notification to NetworkMonitor indicating that the network is now connected.
+     */
+    public void notifyNetworkConnected() {
+        sendMessage(CMD_NETWORK_CONNECTED);
+    }
+
+    /**
+     * Send a notification to NetworkMonitor indicating that the network is now disconnected.
+     */
+    public void notifyNetworkDisconnected() {
+        sendMessage(CMD_NETWORK_DISCONNECTED);
+    }
+
+    /**
+     * Send a notification to NetworkMonitor indicating that link properties have changed.
+     */
+    public void notifyLinkPropertiesChanged() {
+        getHandler().post(() -> {
+            updateLinkProperties();
+        });
+    }
+
+    private void updateLinkProperties() {
+        final LinkProperties lp = mCm.getLinkProperties(mNetwork);
+        // If null, we should soon get a message that the network was disconnected, and will stop.
+        if (lp != null) {
+            // TODO: send LinkProperties parceled in notifyLinkPropertiesChanged() and start().
+            mLinkProperties = lp;
+        }
+    }
+
+    /**
+     * Send a notification to NetworkMonitor indicating that network capabilities have changed.
+     */
+    public void notifyNetworkCapabilitiesChanged() {
+        getHandler().post(() -> {
+            updateNetworkCapabilities();
+        });
+    }
+
+    private void updateNetworkCapabilities() {
+        final NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
+        // If null, we should soon get a message that the network was disconnected, and will stop.
+        if (nc != null) {
+            // TODO: send NetworkCapabilities parceled in notifyNetworkCapsChanged() and start().
+            mNetworkCapabilities = nc;
+        }
+    }
+
+    /**
+     * Request the captive portal application to be launched.
+     */
+    public void launchCaptivePortalApp() {
+        sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
+    }
+
     @Override
     protected void log(String s) {
-        if (DBG) Log.d(TAG + "/" + mNetworkAgentInfo.name(), s);
+        if (DBG) Log.d(TAG + "/" + mNetwork.netId, s);
     }
 
     private void validationLog(int probeType, Object url, String msg) {
@@ -416,11 +481,7 @@
 
     private void validationLog(String s) {
         if (DBG) log(s);
-        validationLogs.log(s);
-    }
-
-    public ReadOnlyLocalLog getValidationLogs() {
-        return validationLogs.readOnlyLocalLog();
+        mValidationLogs.log(s);
     }
 
     private ValidationStage validationStage() {
@@ -428,20 +489,46 @@
     }
 
     private boolean isValidationRequired() {
-        return isValidationRequired(
-                mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
+        return NetworkMonitorUtils.isValidationRequired(
+                mDefaultRequest.networkCapabilities, mNetworkCapabilities);
     }
 
 
-    private void notifyNetworkTestResultInvalid(Object obj) {
-        mConnectivityServiceHandler.sendMessage(obtainMessage(
-                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, obj));
+    private void notifyNetworkTested(int result, @Nullable String redirectUrl) {
+        try {
+            mCallback.notifyNetworkTested(result, redirectUrl);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error sending network test result", e);
+        }
+    }
+
+    private void showProvisioningNotification(String action) {
+        try {
+            mCallback.showProvisioningNotification(action);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error showing provisioning notification", e);
+        }
+    }
+
+    private void hideProvisioningNotification() {
+        try {
+            mCallback.hideProvisioningNotification();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error hiding provisioning notification", e);
+        }
     }
 
     // DefaultState is the parent of all States.  It exists only to handle CMD_* messages but
     // does not entail any real state (hence no enter() or exit() routines).
     private class DefaultState extends State {
         @Override
+        public void enter() {
+            // TODO: have those passed parceled in start() and remove this
+            updateLinkProperties();
+            updateNetworkCapabilities();
+        }
+
+        @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case CMD_NETWORK_CONNECTED:
@@ -458,7 +545,9 @@
                     return HANDLED;
                 case CMD_FORCE_REEVALUATION:
                 case CMD_CAPTIVE_PORTAL_RECHECK:
-                    log("Forcing reevaluation for UID " + message.arg1);
+                    final int dnsCount = mDnsStallDetector.getConsecutiveTimeoutCount();
+                    validationLog("Forcing reevaluation for UID " + message.arg1
+                            + ". Dns signal count: " + dnsCount);
                     mUidResponsibleForReeval = message.arg1;
                     transitionTo(mEvaluatingState);
                     return HANDLED;
@@ -492,7 +581,7 @@
                         case APP_RETURN_UNWANTED:
                             mDontDisplaySigninNotification = true;
                             mUserDoesNotWant = true;
-                            notifyNetworkTestResultInvalid(null);
+                            notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null);
                             // TODO: Should teardown network.
                             mUidResponsibleForReeval = 0;
                             transitionTo(mEvaluatingState);
@@ -556,8 +645,7 @@
         public void enter() {
             maybeLogEvaluationResult(
                     networkEventType(validationStage(), EvaluationResult.VALIDATED));
-            mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
-                    NETWORK_TEST_RESULT_VALID, mNetId, null));
+            notifyNetworkTested(INetworkMonitor.NETWORK_TEST_RESULT_VALID, null);
             mValidations++;
         }
 
@@ -626,8 +714,7 @@
 
         @Override
         public void exit() {
-            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0, mNetId, null);
-            mConnectivityServiceHandler.sendMessage(message);
+            hideProvisioningNotification();
         }
     }
 
@@ -655,8 +742,9 @@
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case CMD_REEVALUATE:
-                    if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
+                    if (message.arg1 != mReevaluateToken || mUserDoesNotWant) {
                         return HANDLED;
+                    }
                     // Don't bother validating networks that don't satisfy the default request.
                     // This includes:
                     //  - VPNs which can be considered explicitly desired by the user and the
@@ -743,9 +831,7 @@
                         CMD_LAUNCH_CAPTIVE_PORTAL_APP);
             }
             // Display the sign in notification.
-            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1, mNetId,
-                    mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent());
-            mConnectivityServiceHandler.sendMessage(message);
+            showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction);
             // Retest for captive portal occasionally.
             sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */,
                     CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
@@ -813,9 +899,9 @@
         }
 
         private boolean isStrictModeHostnameResolved() {
-            return (mPrivateDnsConfig != null) &&
-                   mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) &&
-                   (mPrivateDnsConfig.ips.length > 0);
+            return (mPrivateDnsConfig != null)
+                    && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname)
+                    && (mPrivateDnsConfig.ips.length > 0);
         }
 
         private void resolveStrictModeHostname() {
@@ -831,12 +917,15 @@
         }
 
         private void notifyPrivateDnsConfigResolved() {
-            mConnectivityServiceHandler.sendMessage(obtainMessage(
-                    EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId, mPrivateDnsConfig));
+            try {
+                mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error sending private DNS config resolved notification", e);
+            }
         }
 
         private void handlePrivateDnsEvaluationFailure() {
-            notifyNetworkTestResultInvalid(null);
+            notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null);
 
             // Queue up a re-evaluation with backoff.
             //
@@ -852,12 +941,12 @@
 
         private boolean sendPrivateDnsProbe() {
             // q.v. system/netd/server/dns/DnsTlsTransport.cpp
-            final String ONE_TIME_HOSTNAME_SUFFIX = "-dnsotls-ds.metric.gstatic.com";
-            final String host = UUID.randomUUID().toString().substring(0, 8) +
-                    ONE_TIME_HOSTNAME_SUFFIX;
+            final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com";
+            final String host = UUID.randomUUID().toString().substring(0, 8)
+                    + oneTimeHostnameSuffix;
             final Stopwatch watch = new Stopwatch().start();
             try {
-                final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
+                final InetAddress[] ips = mNonPrivateDnsBypassNetwork.getAllByName(host);
                 final long time = watch.stop();
                 final String strIps = Arrays.toString(ips);
                 final boolean success = (ips != null && ips.length > 0);
@@ -907,12 +996,12 @@
                         // state (even if no Private DNS validation required).
                         transitionTo(mEvaluatingPrivateDnsState);
                     } else if (probeResult.isPortal()) {
-                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
+                        notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
                         mLastPortalProbeResult = probeResult;
                         transitionTo(mCaptivePortalState);
                     } else {
                         logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
-                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
+                        notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
                         transitionTo(mWaitingForNextProbeState);
                     }
                     return HANDLED;
@@ -966,7 +1055,7 @@
     // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
     // to complete, regardless of how many IP addresses a host has.
     private static class OneAddressPerFamilyNetwork extends Network {
-        public OneAddressPerFamilyNetwork(Network network) {
+        OneAddressPerFamilyNetwork(Network network) {
             // Always bypass Private DNS.
             super(network.getPrivateDnsBypassingCopy());
         }
@@ -988,19 +1077,20 @@
         }
     }
 
-    public boolean getIsCaptivePortalCheckEnabled() {
+    private boolean getIsCaptivePortalCheckEnabled() {
         String symbol = Settings.Global.CAPTIVE_PORTAL_MODE;
         int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT;
         int mode = mDependencies.getSetting(mContext, symbol, defaultValue);
         return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
     }
 
-    public boolean getUseHttpsValidation() {
+    private boolean getUseHttpsValidation() {
         return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
     }
 
-    public boolean getWifiScansAlwaysAvailableDisabled() {
-        return mDependencies.getSetting(mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
+    private boolean getWifiScansAlwaysAvailableDisabled() {
+        return mDependencies.getSetting(
+                mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
     }
 
     private String getCaptivePortalServerHttpsUrl() {
@@ -1031,15 +1121,6 @@
                 DEFAULT_DATA_STALL_EVALUATION_TYPES);
     }
 
-    // Static for direct access by ConnectivityService
-    public static String getCaptivePortalServerHttpUrl(Context context) {
-        return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context);
-    }
-
-    public static String getCaptivePortalServerHttpUrl(Dependencies deps, Context context) {
-        return deps.getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
-    }
-
     private URL[] makeCaptivePortalFallbackUrls() {
         try {
             String separator = ",";
@@ -1135,7 +1216,7 @@
         // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
         //    fact block fetching of the generate_204 URL which would lead to false negative
         //    results for network validation.
-        final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
+        final ProxyInfo proxyInfo = mLinkProperties.getHttpProxy();
         if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
             pacUrl = makeURL(proxyInfo.getPacFileUrl().toString());
             if (pacUrl == null) {
@@ -1246,10 +1327,10 @@
             // Time how long it takes to get a response to our request
             long responseTimestamp = SystemClock.elapsedRealtime();
 
-            validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" +
-                    " ret=" + httpResponseCode +
-                    " request=" + requestHeader +
-                    " headers=" + urlConnection.getHeaderFields());
+            validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms"
+                    + " ret=" + httpResponseCode
+                    + " request=" + requestHeader
+                    + " headers=" + urlConnection.getHeaderFields());
             // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
             // portal.  The only example of this seen so far was a captive portal.  For
             // the time being go with prior behavior of assuming it's not a captive
@@ -1267,7 +1348,7 @@
                     // sign-in to an empty page. Probably the result of a broken transparent proxy.
                     // See http://b/9972012.
                     validationLog(probeType, url,
-                        "200 response with Content-length=0 interpreted as 204 response.");
+                            "200 response with Content-length=0 interpreted as 204 response.");
                     httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
                 } else if (urlConnection.getContentLengthLong() == -1) {
                     // When no Content-length (default value == -1), attempt to read a byte from the
@@ -1309,7 +1390,7 @@
             private final boolean mIsHttps;
             private volatile CaptivePortalProbeResult mResult = CaptivePortalProbeResult.FAILED;
 
-            public ProbeThread(boolean isHttps) {
+            ProbeThread(boolean isHttps) {
                 mIsHttps = isHttps;
             }
 
@@ -1407,99 +1488,98 @@
             return;
         }
 
-        if (!systemReady) {
+        if (!mSystemReady) {
             return;
         }
 
         Intent latencyBroadcast =
-                new Intent(ConnectivityConstants.ACTION_NETWORK_CONDITIONS_MEASURED);
-        switch (mNetworkAgentInfo.networkInfo.getType()) {
-            case ConnectivityManager.TYPE_WIFI:
-                WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
-                if (currentWifiInfo != null) {
-                    // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
-                    // surrounded by double quotation marks (thus violating the Javadoc), but this
-                    // was changed to match the Javadoc in API 17. Since clients may have started
-                    // sanitizing the output of this method since API 17 was released, we should
-                    // not change it here as it would become impossible to tell whether the SSID is
-                    // simply being surrounded by quotes due to the API, or whether those quotes
-                    // are actually part of the SSID.
-                    latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_SSID,
-                            currentWifiInfo.getSSID());
-                    latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_BSSID,
-                            currentWifiInfo.getBSSID());
-                } else {
-                    if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
-                    return;
-                }
-                break;
-            case ConnectivityManager.TYPE_MOBILE:
-                latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_NETWORK_TYPE,
-                        mTelephonyManager.getNetworkType());
-                List<CellInfo> info = mTelephonyManager.getAllCellInfo();
-                if (info == null) return;
-                int numRegisteredCellInfo = 0;
-                for (CellInfo cellInfo : info) {
-                    if (cellInfo.isRegistered()) {
-                        numRegisteredCellInfo++;
-                        if (numRegisteredCellInfo > 1) {
-                            if (VDBG) logw("more than one registered CellInfo." +
-                                    " Can't tell which is active.  Bailing.");
-                            return;
+                new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED);
+        if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+            WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
+            if (currentWifiInfo != null) {
+                // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
+                // surrounded by double quotation marks (thus violating the Javadoc), but this
+                // was changed to match the Javadoc in API 17. Since clients may have started
+                // sanitizing the output of this method since API 17 was released, we should
+                // not change it here as it would become impossible to tell whether the SSID is
+                // simply being surrounded by quotes due to the API, or whether those quotes
+                // are actually part of the SSID.
+                latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_SSID,
+                        currentWifiInfo.getSSID());
+                latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_BSSID,
+                        currentWifiInfo.getBSSID());
+            } else {
+                if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
+                return;
+            }
+            latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI);
+        } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE,
+                    mTelephonyManager.getNetworkType());
+            List<CellInfo> info = mTelephonyManager.getAllCellInfo();
+            if (info == null) return;
+            int numRegisteredCellInfo = 0;
+            for (CellInfo cellInfo : info) {
+                if (cellInfo.isRegistered()) {
+                    numRegisteredCellInfo++;
+                    if (numRegisteredCellInfo > 1) {
+                        if (VDBG) {
+                            logw("more than one registered CellInfo."
+                                    + " Can't tell which is active.  Bailing.");
                         }
-                        if (cellInfo instanceof CellInfoCdma) {
-                            CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
-                        } else if (cellInfo instanceof CellInfoGsm) {
-                            CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
-                        } else if (cellInfo instanceof CellInfoLte) {
-                            CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
-                        } else if (cellInfo instanceof CellInfoWcdma) {
-                            CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
-                        } else {
-                            if (VDBG) logw("Registered cellinfo is unrecognized");
-                            return;
-                        }
+                        return;
+                    }
+                    if (cellInfo instanceof CellInfoCdma) {
+                        CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
+                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+                    } else if (cellInfo instanceof CellInfoGsm) {
+                        CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
+                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+                    } else if (cellInfo instanceof CellInfoLte) {
+                        CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
+                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+                    } else if (cellInfo instanceof CellInfoWcdma) {
+                        CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
+                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+                    } else {
+                        if (VDBG) logw("Registered cellinfo is unrecognized");
+                        return;
                     }
                 }
-                break;
-            default:
-                return;
+            }
+            latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
+        } else {
+            return;
         }
-        latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CONNECTIVITY_TYPE,
-                mNetworkAgentInfo.networkInfo.getType());
-        latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_RECEIVED,
+        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_RECEIVED,
                 responseReceived);
-        latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_REQUEST_TIMESTAMP_MS,
+        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_REQUEST_TIMESTAMP_MS,
                 requestTimestampMs);
 
         if (responseReceived) {
-            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_IS_CAPTIVE_PORTAL,
+            latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_IS_CAPTIVE_PORTAL,
                     isCaptivePortal);
-            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_TIMESTAMP_MS,
+            latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_TIMESTAMP_MS,
                     responseTimestampMs);
         }
         mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
-                ConnectivityConstants.PERMISSION_ACCESS_NETWORK_CONDITIONS);
+                NetworkMonitorUtils.PERMISSION_ACCESS_NETWORK_CONDITIONS);
     }
 
     private void logNetworkEvent(int evtype) {
-        int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+        int[] transports = mNetworkCapabilities.getTransportTypes();
         mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype));
     }
 
     private int networkEventType(ValidationStage s, EvaluationResult r) {
-        if (s.isFirstValidation) {
-            if (r.isValidated) {
+        if (s.mIsFirstValidation) {
+            if (r.mIsValidated) {
                 return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS;
             } else {
                 return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND;
             }
         } else {
-            if (r.isValidated) {
+            if (r.mIsValidated) {
                 return NetworkEvent.NETWORK_REVALIDATION_SUCCESS;
             } else {
                 return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND;
@@ -1509,15 +1589,15 @@
 
     private void maybeLogEvaluationResult(int evtype) {
         if (mEvaluationTimer.isRunning()) {
-            int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+            int[] transports = mNetworkCapabilities.getTransportTypes();
             mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop()));
             mEvaluationTimer.reset();
         }
     }
 
     private void logValidationProbe(long durationMs, int probeType, int probeResult) {
-        int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
-        boolean isFirstValidation = validationStage().isFirstValidation;
+        int[] transports = mNetworkCapabilities.getTransportTypes();
+        boolean isFirstValidation = validationStage().mIsFirstValidation;
         ValidationProbeEvent ev = new ValidationProbeEvent();
         ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);
         ev.returnCode = probeResult;
@@ -1526,19 +1606,36 @@
     }
 
     @VisibleForTesting
-    public static class Dependencies {
-        public Network getNetwork(NetworkAgentInfo networkAgentInfo) {
-            return new OneAddressPerFamilyNetwork(networkAgentInfo.network());
+    static class Dependencies {
+        public Network getPrivateDnsBypassNetwork(Network network) {
+            return new OneAddressPerFamilyNetwork(network);
         }
 
         public Random getRandom() {
             return new Random();
         }
 
+        /**
+         * Get the captive portal server HTTP URL that is configured on the device.
+         */
+        public String getCaptivePortalServerHttpUrl(Context context) {
+            return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(context);
+        }
+
+        /**
+         * Get the value of a global integer setting.
+         * @param symbol Name of the setting
+         * @param defaultValue Value to return if the setting is not defined.
+         */
         public int getSetting(Context context, String symbol, int defaultValue) {
             return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
         }
 
+        /**
+         * Get the value of a global String setting.
+         * @param symbol Name of the setting
+         * @param defaultValue Value to return if the setting is not defined.
+         */
         public String getSetting(Context context, String symbol, String defaultValue) {
             final String value = Settings.Global.getString(context.getContentResolver(), symbol);
             return value != null ? value : defaultValue;
@@ -1645,7 +1742,7 @@
         boolean result = false;
         // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
         // possible traffic cost in metered network.
-        if (mNetworkAgentInfo.networkCapabilities.isMetered()
+        if (mNetworkCapabilities.isMetered()
                 && (SystemClock.elapsedRealtime() - getLastProbeTime()
                 < mDataStallMinEvaluateTime)) {
             return false;
diff --git a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
new file mode 100644
index 0000000..bb5900c
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.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.server.util;
+
+/**
+ * Network constants used by the network stack.
+ */
+public final class NetworkStackConstants {
+
+    /**
+     * IPv4 constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc791
+     */
+    public static final int IPV4_ADDR_BITS = 32;
+    public static final int IPV4_MIN_MTU = 68;
+    public static final int IPV4_MAX_MTU = 65_535;
+
+    /**
+     * DHCP constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc2131
+     */
+    public static final int INFINITE_LEASE = 0xffffffff;
+
+    private NetworkStackConstants() {
+        throw new UnsupportedOperationException("This class is not to be instantiated");
+    }
+}
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
new file mode 100644
index 0000000..733f873
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util;
+
+import static android.os.Binder.getCallingUid;
+
+import android.os.Process;
+
+/**
+ * Utility class to check calling permissions on the network stack.
+ */
+public final class PermissionUtil {
+
+    /**
+     * Check that the caller is allowed to communicate with the network stack.
+     * @throws SecurityException The caller is not allowed to communicate with the network stack.
+     */
+    public static void checkNetworkStackCallingPermission() {
+        // TODO: check that the calling PID is the system server.
+        if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
+            throw new SecurityException("Invalid caller: " + getCallingUid());
+        }
+    }
+
+    private PermissionUtil() {
+        throw new UnsupportedOperationException("This class is not to be instantiated");
+    }
+}
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
new file mode 100644
index 0000000..bd7ff2a
--- /dev/null
+++ b/packages/NetworkStack/tests/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+android_test {
+    name: "NetworkStackTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "android-support-test",
+        "mockito-target-extended-minus-junit4",
+        "NetworkStackLib",
+        "testables",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ]
+}
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/AndroidManifest.xml b/packages/NetworkStack/tests/AndroidManifest.xml
new file mode 100644
index 0000000..8b8474f
--- /dev/null
+++ b/packages/NetworkStack/tests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.server.networkstack.tests">
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.server.networkstack.tests"
+        android:label="Networking service tests">
+    </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/AndroidTest.xml b/packages/NetworkStack/tests/AndroidTest.xml
new file mode 100644
index 0000000..6b08b57
--- /dev/null
+++ b/packages/NetworkStack/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 NetworkStack">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="NetworkStackTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="NetworkStackTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.server.networkstack.tests" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
similarity index 99%
rename from tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
index 7f8e7b5..51d50d9 100644
--- a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -16,6 +16,7 @@
 
 package android.net.dhcp;
 
+import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
 import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
 import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
@@ -29,14 +30,13 @@
 import static org.mockito.Mockito.when;
 
 import static java.lang.String.format;
-import static java.net.InetAddress.parseNumericAddress;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.MacAddress;
-import android.net.util.SharedLog;
 import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -126,7 +126,7 @@
         mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
 
         // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
-        requestAddresses((byte)11);
+        requestAddresses((byte) 11);
 
         try {
             mRepo.getOffer(null, TEST_MAC_2,
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
similarity index 89%
rename from tests/net/java/android/net/dhcp/DhcpServerTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
index ab9bd84..d4c1e2e 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
@@ -16,11 +16,13 @@
 
 package android.net.dhcp;
 
+import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
 import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
 import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -33,14 +35,14 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import static java.net.InetAddress.parseNumericAddress;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.INetworkStackStatusCallback;
 import android.net.LinkAddress;
 import android.net.MacAddress;
 import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
@@ -48,9 +50,11 @@
 import android.net.dhcp.DhcpServer.Clock;
 import android.net.dhcp.DhcpServer.Dependencies;
 import android.net.util.SharedLog;
-import android.os.test.TestLooper;
+import android.os.HandlerThread;
 import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.After;
 import org.junit.Before;
@@ -67,10 +71,10 @@
 import java.util.HashSet;
 import java.util.Set;
 
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
 @SmallTest
+@RunWithLooper
 public class DhcpServerTest {
-    private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
     private static final String TEST_IFACE = "testiface";
 
     private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
@@ -113,18 +117,25 @@
     private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
 
     @NonNull
-    private TestLooper mLooper;
+    private HandlerThread mHandlerThread;
+    @NonNull
+    private TestableLooper mLooper;
     @NonNull
     private DhcpServer mServer;
 
     @Nullable
     private String mPrevShareClassloaderProp;
 
+    private final INetworkStackStatusCallback mAssertSuccessCallback =
+            new INetworkStackStatusCallback.Stub() {
+        @Override
+        public void onStatusAvailable(int statusCode) {
+            assertEquals(STATUS_SUCCESS, statusCode);
+        }
+    };
+
     @Before
     public void setUp() throws Exception {
-        // Allow mocking package-private classes
-        mPrevShareClassloaderProp = System.getProperty(PROP_DEXMAKER_SHARE_CLASSLOADER);
-        System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER, "true");
         MockitoAnnotations.initMocks(this);
 
         when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository);
@@ -143,20 +154,22 @@
                 .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
                 .build();
 
-        mLooper = new TestLooper();
-        mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
+        mLooper = TestableLooper.get(this);
+        mHandlerThread = spy(new HandlerThread("TestDhcpServer"));
+        when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
+        mServer = new DhcpServer(mHandlerThread, TEST_IFACE, servingParams,
                 new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
 
-        mServer.start();
-        mLooper.dispatchAll();
+        mServer.start(mAssertSuccessCallback);
+        mLooper.processAllMessages();
     }
 
     @After
-    public void tearDown() {
-        // Calling stop() several times is not an issue
-        mServer.stop();
-        System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER,
-                (mPrevShareClassloaderProp == null ? "" : mPrevShareClassloaderProp));
+    public void tearDown() throws Exception {
+        mServer.stop(mAssertSuccessCallback);
+        mLooper.processMessages(1);
+        verify(mPacketListener, times(1)).stop();
+        verify(mHandlerThread, times(1)).quitSafely();
     }
 
     @Test
@@ -165,13 +178,6 @@
     }
 
     @Test
-    public void testStop() throws Exception {
-        mServer.stop();
-        mLooper.dispatchAll();
-        verify(mPacketListener, times(1)).stop();
-    }
-
-    @Test
     public void testDiscover() throws Exception {
         // TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields
         when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
similarity index 74%
rename from tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
index b6a4073..3ca0564 100644
--- a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
@@ -16,17 +16,18 @@
 
 package android.net.dhcp;
 
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
 import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import static java.net.InetAddress.parseNumericAddress;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.LinkAddress;
+import android.net.NetworkUtils;
 import android.net.dhcp.DhcpServingParams.InvalidParameterException;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -35,8 +36,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.reflect.Modifier;
 import java.net.Inet4Address;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -56,6 +59,7 @@
     private static final int TEST_MTU = 1500;
     private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
             Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
+    private static final boolean TEST_METERED = true;
 
     @Before
     public void setUp() {
@@ -65,7 +69,8 @@
                 .setDnsServers(TEST_DNS_SERVERS)
                 .setServerAddr(TEST_LINKADDR)
                 .setLinkMtu(TEST_MTU)
-                .setExcludedAddrs(TEST_EXCLUDED_ADDRS);
+                .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
+                .setMetered(TEST_METERED);
     }
 
     @Test
@@ -91,6 +96,7 @@
         assertEquals(TEST_DNS_SERVERS, params.dnsServers);
         assertEquals(TEST_LINKADDR, params.serverAddr);
         assertEquals(TEST_MTU, params.linkMtu);
+        assertEquals(TEST_METERED, params.metered);
 
         assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS);
         assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS);
@@ -159,6 +165,44 @@
         mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build();
     }
 
+    @Test
+    public void testFromParcelableObject() throws InvalidParameterException {
+        final DhcpServingParams params = mBuilder.build();
+        final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel();
+        parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS);
+        parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS;
+        parcel.dnsServers = toIntArray(TEST_DNS_SERVERS);
+        parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR);
+        parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength();
+        parcel.linkMtu = TEST_MTU;
+        parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS);
+        parcel.metered = TEST_METERED;
+        final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel);
+
+        assertEquals(params.defaultRouters, parceled.defaultRouters);
+        assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs);
+        assertEquals(params.dnsServers, parceled.dnsServers);
+        assertEquals(params.serverAddr, parceled.serverAddr);
+        assertEquals(params.linkMtu, parceled.linkMtu);
+        assertEquals(params.excludedAddrs, parceled.excludedAddrs);
+        assertEquals(params.metered, parceled.metered);
+
+        // Ensure that we do not miss any field if added in the future
+        final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields())
+                .filter(f -> !Modifier.isStatic(f.getModifiers()))
+                .count();
+        assertEquals(7, numFields);
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testFromParcelableObject_NullArgument() throws InvalidParameterException {
+        DhcpServingParams.fromParcelableObject(null);
+    }
+
+    private static int[] toIntArray(Collection<Inet4Address> addrs) {
+        return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
+    }
+
     private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) {
         for (final T elem : subset) {
             assertContains(set, elem);
diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
similarity index 68%
rename from tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
rename to packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 6e07b26..d31fa77 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,6 +16,14 @@
 
 package com.android.server.connectivity;
 
+import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
+import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 
 import static org.junit.Assert.assertTrue;
@@ -24,13 +32,21 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.Intent;
+import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
+import android.net.INetworkMonitorCallbacks;
+import android.net.InetAddresses;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -38,9 +54,12 @@
 import android.net.NetworkRequest;
 import android.net.captiveportal.CaptivePortalProbeResult;
 import android.net.metrics.IpConnectivityLog;
+import android.net.util.SharedLog;
 import android.net.wifi.WifiManager;
+import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -50,8 +69,10 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.io.IOException;
 import java.net.HttpURLConnection;
@@ -68,21 +89,23 @@
     private static final String LOCATION_HEADER = "location";
 
     private @Mock Context mContext;
-    private @Mock Handler mHandler;
     private @Mock IpConnectivityLog mLogger;
-    private @Mock NetworkAgentInfo mAgent;
-    private @Mock NetworkAgentInfo mNotMeteredAgent;
+    private @Mock SharedLog mValidationLogger;
     private @Mock NetworkInfo mNetworkInfo;
-    private @Mock NetworkRequest mRequest;
+    private @Mock ConnectivityManager mCm;
     private @Mock TelephonyManager mTelephony;
     private @Mock WifiManager mWifi;
-    private @Mock Network mNetwork;
     private @Mock HttpURLConnection mHttpConnection;
     private @Mock HttpURLConnection mHttpsConnection;
     private @Mock HttpURLConnection mFallbackConnection;
     private @Mock HttpURLConnection mOtherFallbackConnection;
     private @Mock Random mRandom;
     private @Mock NetworkMonitor.Dependencies mDependencies;
+    private @Mock INetworkMonitorCallbacks mCallbacks;
+    private @Spy Network mNetwork = new Network(TEST_NETID);
+    private NetworkRequest mRequest;
+
+    private static final int TEST_NETID = 4242;
 
     private static final String TEST_HTTP_URL = "http://www.google.com/gen_204";
     private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
@@ -93,33 +116,37 @@
     private static final int RETURN_CODE_DNS_SUCCESS = 0;
     private static final int RETURN_CODE_DNS_TIMEOUT = 255;
 
+    private static final int HANDLER_TIMEOUT_MS = 1000;
+
+    private static final LinkProperties TEST_LINKPROPERTIES = new LinkProperties();
+
+    private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+            .addCapability(NET_CAPABILITY_INTERNET);
+
+    private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+            .addCapability(NET_CAPABILITY_INTERNET)
+            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+
+    private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+
     @Before
     public void setUp() throws IOException {
         MockitoAnnotations.initMocks(this);
-        mAgent.linkProperties = new LinkProperties();
-        mAgent.networkCapabilities = new NetworkCapabilities()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        mAgent.networkInfo = mNetworkInfo;
-
-        mNotMeteredAgent.linkProperties = new LinkProperties();
-        mNotMeteredAgent.networkCapabilities = new NetworkCapabilities()
-            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-        mNotMeteredAgent.networkInfo = mNetworkInfo;
-
-        when(mAgent.network()).thenReturn(mNetwork);
-        when(mDependencies.getNetwork(any())).thenReturn(mNetwork);
+        when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mNetwork);
         when(mDependencies.getRandom()).thenReturn(mRandom);
         when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt()))
                 .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
         when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS),
                 anyInt())).thenReturn(1);
-        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL),
-                anyString())).thenReturn(TEST_HTTP_URL);
+        when(mDependencies.getCaptivePortalServerHttpUrl(any())).thenReturn(TEST_HTTP_URL);
         when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL),
                 anyString())).thenReturn(TEST_HTTPS_URL);
-        when(mNetwork.getPrivateDnsBypassingCopy()).thenReturn(mNetwork);
+        doReturn(mNetwork).when(mNetwork).getPrivateDnsBypassingCopy();
 
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
         when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
 
@@ -129,7 +156,7 @@
         setFallbackSpecs(null); // Test with no fallback spec by default
         when(mRandom.nextInt()).thenReturn(0);
 
-        when(mNetwork.openConnection(any())).then((invocation) -> {
+        doAnswer((invocation) -> {
             URL url = invocation.getArgument(0);
             switch(url.toString()) {
                 case TEST_HTTP_URL:
@@ -144,12 +171,20 @@
                     fail("URL not mocked: " + url.toString());
                     return null;
             }
-        });
+        }).when(mNetwork).openConnection(any());
         when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
         when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
-        when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] {
-            InetAddress.parseNumericAddress("192.168.0.0")
-        });
+        doReturn(new InetAddress[] {
+                InetAddresses.parseNumericAddress("192.168.0.0")
+        }).when(mNetwork).getAllByName(any());
+
+        mRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                .build();
+        // Default values. Individual tests can override these.
+        when(mCm.getLinkProperties(any())).thenReturn(TEST_LINKPROPERTIES);
+        when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
 
         setMinDataStallEvaluateInterval(500);
         setDataStallEvaluationType(1 << DATA_STALL_EVALUATION_TYPE_DNS);
@@ -160,10 +195,10 @@
     private class WrappedNetworkMonitor extends NetworkMonitor {
         private long mProbeTime = 0;
 
-        WrappedNetworkMonitor(Context context, Handler handler,
-                NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
+        WrappedNetworkMonitor(Context context, Network network, NetworkRequest defaultRequest,
                 IpConnectivityLog logger, Dependencies deps) {
-                super(context, handler, networkAgentInfo, defaultRequest, logger, deps);
+                super(context, mCallbacks, network, defaultRequest, logger,
+                        new SharedLog("test_nm"), deps);
         }
 
         @Override
@@ -176,19 +211,39 @@
         }
     }
 
-    WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
-        return new WrappedNetworkMonitor(
-                mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+    private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
+        final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
+                mContext, mNetwork, mRequest, mLogger, mDependencies);
+        when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
+        nm.start();
+        waitForIdle(nm.getHandler());
+        return nm;
     }
 
-    WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
-        return new WrappedNetworkMonitor(
-                mContext, mHandler, mNotMeteredAgent, mRequest, mLogger, mDependencies);
+    private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
+        final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
+                mContext, mNetwork, mRequest, mLogger, mDependencies);
+        when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES);
+        nm.start();
+        waitForIdle(nm.getHandler());
+        return nm;
     }
 
-    NetworkMonitor makeMonitor() {
-        return new NetworkMonitor(
-                mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+    private NetworkMonitor makeMonitor() {
+        final NetworkMonitor nm = new NetworkMonitor(
+                mContext, mCallbacks, mNetwork, mRequest, mLogger, mValidationLogger,
+                mDependencies);
+        nm.start();
+        waitForIdle(nm.getHandler());
+        return nm;
+    }
+
+    private void waitForIdle(Handler handler) {
+        final ConditionVariable cv = new ConditionVariable(false);
+        handler.post(cv::open);
+        if (!cv.block(HANDLER_TIMEOUT_MS)) {
+            fail("Timed out waiting for handler");
+        }
     }
 
     @Test
@@ -319,6 +374,15 @@
     }
 
     @Test
+    public void testIsCaptivePortal_IgnorePortals() throws IOException {
+        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
+        setSslException(mHttpsConnection);
+        setPortal302(mHttpConnection);
+
+        assertNotPortal(makeMonitor().isCaptivePortal());
+    }
+
+    @Test
     public void testIsDataStall_EvaluationDisabled() {
         setDataStallEvaluationType(0);
         WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
@@ -390,6 +454,63 @@
         assertFalse(wrappedMonitor.isDataStall());
     }
 
+    @Test
+    public void testBrokenNetworkNotValidated() throws Exception {
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 500);
+        setStatus(mFallbackConnection, 404);
+        when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
+
+        final NetworkMonitor nm = makeMonitor();
+        nm.notifyNetworkConnected();
+
+        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null);
+    }
+
+    @Test
+    public void testNoInternetCapabilityValidated() throws Exception {
+        when(mCm.getNetworkCapabilities(any())).thenReturn(NO_INTERNET_CAPABILITIES);
+
+        final NetworkMonitor nm = makeMonitor();
+        nm.notifyNetworkConnected();
+
+        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
+        verify(mNetwork, never()).openConnection(any());
+    }
+
+    @Test
+    public void testLaunchCaptivePortalApp() throws Exception {
+        setSslException(mHttpsConnection);
+        setPortal302(mHttpConnection);
+
+        final NetworkMonitor nm = makeMonitor();
+        nm.notifyNetworkConnected();
+
+        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .showProvisioningNotification(any());
+
+        // Check that startCaptivePortalApp sends the expected intent.
+        nm.launchCaptivePortalApp();
+
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT));
+        final Intent intent = intentCaptor.getValue();
+        assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
+        final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
+        assertEquals(TEST_NETID, network.netId);
+
+        // Have the app report that the captive portal is dismissed, and check that we revalidate.
+        setStatus(mHttpsConnection, 204);
+        setStatus(mHttpConnection, 204);
+        final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL);
+        captivePortal.reportCaptivePortalDismissed();
+        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
+    }
+
     private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
         for (int i = 0; i < count; i++) {
             wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
@@ -440,6 +561,11 @@
                 eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
     }
 
+    private void setCaptivePortalMode(int mode) {
+        when(mDependencies.getSetting(any(),
+                eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode);
+    }
+
     private void assertPortal(CaptivePortalProbeResult result) {
         assertTrue(result.isPortal());
         assertFalse(result.isFailed());
@@ -459,12 +585,12 @@
     }
 
     private void setSslException(HttpURLConnection connection) throws IOException {
-        when(connection.getResponseCode()).thenThrow(new SSLHandshakeException("Invalid cert"));
+        doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode();
     }
 
     private void set302(HttpURLConnection connection, String location) throws IOException {
         setStatus(connection, 302);
-        when(connection.getHeaderField(LOCATION_HEADER)).thenReturn(location);
+        doReturn(location).when(connection).getHeaderField(LOCATION_HEADER);
     }
 
     private void setPortal302(HttpURLConnection connection) throws IOException {
@@ -472,7 +598,7 @@
     }
 
     private void setStatus(HttpURLConnection connection, int status) throws IOException {
-        when(connection.getResponseCode()).thenReturn(status);
+        doReturn(status).when(connection).getResponseCode();
     }
 }
 
diff --git a/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java b/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java
new file mode 100644
index 0000000..07ad3123
--- /dev/null
+++ b/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.util.SharedLog;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SharedLogTest {
+    private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}";
+    private static final String TIMESTAMP = "HH:MM:SS";
+
+    @Test
+    public void testBasicOperation() {
+        final SharedLog logTop = new SharedLog("top");
+        logTop.mark("first post!");
+
+        final SharedLog logLevel2a = logTop.forSubComponent("twoA");
+        final SharedLog logLevel2b = logTop.forSubComponent("twoB");
+        logLevel2b.e("2b or not 2b");
+        logLevel2b.e("No exception", null);
+        logLevel2b.e("Wait, here's one", new Exception("Test"));
+        logLevel2a.w("second post?");
+
+        final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
+        logTop.log("still logging");
+        logLevel3.log("3 >> 2");
+        logLevel2a.mark("ok: last post");
+
+        final String[] expected = {
+            " - MARK first post!",
+            " - [twoB] ERROR 2b or not 2b",
+            " - [twoB] ERROR No exception",
+            // No stacktrace in shared log, only in logcat
+            " - [twoB] ERROR Wait, here's one: Test",
+            " - [twoA] WARN second post?",
+            " - still logging",
+            " - [twoA.three] 3 >> 2",
+            " - [twoA] MARK ok: last post",
+        };
+        // Verify the logs are all there and in the correct order.
+        verifyLogLines(expected, logTop);
+
+        // In fact, because they all share the same underlying LocalLog,
+        // every subcomponent SharedLog's dump() is identical.
+        verifyLogLines(expected, logLevel2a);
+        verifyLogLines(expected, logLevel2b);
+        verifyLogLines(expected, logLevel3);
+    }
+
+    private static void verifyLogLines(String[] expected, SharedLog log) {
+        final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+        final PrintWriter pw = new PrintWriter(ostream, true);
+        log.dump(null, pw, null);
+
+        final String dumpOutput = ostream.toString();
+        assertTrue(dumpOutput != null);
+        assertTrue(!"".equals(dumpOutput));
+
+        final String[] lines = dumpOutput.split("\n");
+        assertEquals(expected.length, lines.length);
+
+        for (int i = 0; i < expected.length; i++) {
+            String got = lines[i];
+            String want = expected[i];
+            assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want));
+            assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP),
+                    got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP));
+        }
+    }
+}
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index 933ff3f..9b57186 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -90,7 +90,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsæt"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Indstillinger"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installerer/afinstallerer Wear-apps"</string>
-    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Underretning om appinstallation"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notifikation om appinstallation"</string>
     <string name="notification_installation_success_message" msgid="6450467996056038442">"Appen er installeret"</string>
     <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" er installeret"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index 2182631..4deab35 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -59,7 +59,7 @@
     <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अ‍ॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अ‍ॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
     <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g> आकाराच्या संबंधित मीडिया फायलीदेखील काढा."</string>
-    <string name="uninstall_keep_data" msgid="7002379587465487550">"अॅप डेटा पैकी <xliff:g id="SIZE">%1$s</xliff:g> ठेवा."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"ॲप डेटा पैकी <xliff:g id="SIZE">%1$s</xliff:g> ठेवा."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"अनइंस्टॉल रन होत आहेत"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"अनइंस्टॉल करता आले नाही"</string>
     <string name="uninstalling" msgid="8709566347688966845">"अनइंस्टॉल करत आहे…"</string>
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index eed66e9..d332bac 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -77,7 +77,7 @@
  */
 public class BarChartPreference extends Preference {
 
-    static final int MAXIMUM_BAR_VIEWS = 4;
+    public static final int MAXIMUM_BAR_VIEWS = 4;
     private static final String TAG = "BarChartPreference";
     private static final int[] BAR_VIEWS = {
             R.id.bar_view1,
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1dd7838..ac34a99e 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nuwe rugsteunwagwoord ingestel"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nuwe wagwoord en bevestiging stem nie ooreen nie"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Rugsteunwagwoord kon nie ingestel word nie"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Laai tans …"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Lewendig (verstek)"</item>
     <item msgid="8446070607501413455">"Natuurlik"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vra elke keer"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Totdat jy dit afskakel"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sopas"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aansluitingprogram om opgedateerde grafikadrywer in ontwikkeling te gebruik"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Foonluidspreker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 2ce5f44..9405b7d 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"አዲስ የምትኬ ይለፍ ቃል ተዋቅሯል"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"አዲሱ የይለፍ ቃል እና ማረጋገጫው አይዛመዱም"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"የምትኬ ይለፍ ቃል ማዋቀር አልተሳካም"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"በመጫን ላይ…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"ነዛሪ (ነባሪ)"</item>
     <item msgid="8446070607501413455">"ተፈጥሯዊ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ሁልጊዜ ጠይቅ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"እስኪያጠፉት ድረስ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ልክ አሁን"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"በግንባታ ላይ የተዘመነ የግራፊክስ ነጂን ለመጠቀም መተግበሪያን መርጠው ያስገቡ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"የስልክ ድምጽ ማጉያ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 23f3a12..8ffd7a9 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"تم تعيين كلمة مرور احتياطية جديدة"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"كلمة المرور الجديدة وتأكيدها لا يتطابقان"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"تعذّر تعيين كلمة مرور احتياطية"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"جارٍ التحميل…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"نابض بالحياة (تلقائي)"</item>
     <item msgid="8446070607501413455">"طبيعي"</item>
@@ -453,6 +454,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"الطلب في كل مرة"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"إلى أن توقف الوضع يدويًا"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"للتو"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"فعِّل التطبيق لاستخدام برنامج تشغيل الرسومات المُحدَّث في تطوير البرامج."</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"مكبر صوت الهاتف"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index 39c0b00..c0a2179 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -40,7 +40,7 @@
     <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>ৰ সৈতে সংযোগ কৰি থকা হৈছে…"</item>
     <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>ৰ জৰিয়তে সত্যাপন কৰি থকা হৈছে…"</item>
     <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>ৰ আইপি ঠিকনা পৰা সংগ্ৰহ কৰি থকা হৈছে…"</item>
-    <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>ৰ সৈতে সংযোগ কৰা হ\'ল"</item>
+    <item msgid="8937994881315223448">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>ৰ সৈতে সংযোগ কৰা হ’ল"</item>
     <item msgid="1330262655415760617">"স্থগিত"</item>
     <item msgid="7698638434317271902">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>ৰ পৰা সংযোগ বিচ্ছিন্ন কৰি থকা হৈছে…"</item>
     <item msgid="197508606402264311">"সংযোগ বিচ্ছিন্ন"</item>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 86d1459..3ad13bd 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -32,11 +32,11 @@
     <string name="wifi_cant_connect_to_ap" msgid="1222553274052685331">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'ৰ সৈতে সংযোগ কৰিব পৰা নাই"</string>
     <string name="wifi_check_password_try_again" msgid="516958988102584767">"পাছৱৰ্ড পৰীক্ষা কৰি আকৌ চেষ্টা কৰক"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"পৰিসৰৰ ভিতৰত নাই"</string>
-    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ\'ব"</string>
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string>
     <string name="wifi_no_internet" msgid="4663834955626848401">"ইণ্টাৰনেট সংযোগ নাই"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string>
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string>
-    <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ\'ল"</string>
+    <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
     <string name="wifi_connected_no_internet" msgid="8202906332837777829">"সংযোজিত, ইণ্টাৰনেট নাই"</string>
@@ -55,16 +55,16 @@
     <string name="bluetooth_disconnected" msgid="6557104142667339895">"সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string>
     <string name="bluetooth_disconnecting" msgid="8913264760027764974">"সংযোগ বিচ্ছিন্ন কৰি থকা হৈছে…"</string>
     <string name="bluetooth_connecting" msgid="8555009514614320497">"সংযোগ কৰি থকা হৈছে…"</string>
-    <string name="bluetooth_connected" msgid="5427152882755735944">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> সংযোগ কৰা হ\'ল"</string>
+    <string name="bluetooth_connected" msgid="5427152882755735944">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> সংযোগ কৰা হ’ল"</string>
     <string name="bluetooth_pairing" msgid="1426882272690346242">"যোৰা লগোৱা হৈছে…"</string>
-    <string name="bluetooth_connected_no_headset" msgid="616068069034994802">"সংযোগ কৰা হ\'ল (ফ\'ন নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"সংযোগ কৰা হ\'ল (মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"সংযোগ কৰা হ\'ল (বাৰ্তাত প্ৰৱেশাধিকাৰ নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2047403011284187056">"সংযোগ কৰা হ\'ল (কোনো ফ\'ন বা মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_battery_level" msgid="5162924691231307748">"সংযোগ কৰা হ\'ল, বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_battery_level" msgid="1610296229139400266">"সংযোগ কৰা হ\'ল (ফ\'ন নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="3908466636369853652">"সংযোগ কৰা হ\'ল (মিডিয়া নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="1163440823807659316">"সংযোগ কৰা হ\'ল (কোনো ফ\'ন বা মিডিয়া নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset" msgid="616068069034994802">"সংযোগ কৰা হ’ল (ফ\'ন নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"সংযোগ কৰা হ’ল (মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"সংযোগ কৰা হ’ল (বাৰ্তাত প্ৰৱেশাধিকাৰ নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2047403011284187056">"সংযোগ কৰা হ’ল (কোনো ফ\'ন বা মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_battery_level" msgid="5162924691231307748">"সংযোগ কৰা হ’ল, বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_battery_level" msgid="1610296229139400266">"সংযোগ কৰা হ’ল (ফ\'ন নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="3908466636369853652">"সংযোগ কৰা হ’ল (মিডিয়া নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="1163440823807659316">"সংযোগ কৰা হ’ল (কোনো ফ\'ন বা মিডিয়া নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="3149689299296462009">"সক্ৰিয়, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰি"</string>
     <string name="bluetooth_battery_level" msgid="1447164613319663655">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰি"</string>
     <string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"সক্ৰিয়"</string>
@@ -81,9 +81,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"এইচ্ছডি অডিঅ’"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"শ্ৰৱণ যন্ত্ৰ"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"শ্ৰৱণ যন্ত্ৰৰ লগত সংযোগ কৰা হ\'ল"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"শ্ৰৱণ যন্ত্ৰৰ লগত সংযোগ কৰা হ’ল"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"মিডিয়া অডিঅ’লৈ সংযোগ হৈছে"</string>
-    <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফোন অডিঅ\'ৰ লগত সংযোগ কৰা হ\'ল"</string>
+    <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফ’ন অডিঅ\'ৰ লগত সংযোগ কৰা হ’ল"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ফাইল ট্ৰান্সফাৰ ছাৰ্ভাৰৰ সৈতে সংযোজিত হৈ আছে"</string>
     <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"মেপৰ সৈতে সংযোগ কৰক"</string>
     <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAPৰ সৈতে সংযোজিত হৈ আছে"</string>
@@ -206,10 +206,10 @@
     <string name="mock_location_app_not_set" msgid="809543285495344223">"কোনো নকল অৱস্থান এপ্ নিৰ্ধাৰণ কৰা হোৱা নাই"</string>
     <string name="mock_location_app_set" msgid="8966420655295102685">"নকল অৱস্থানৰ এপ্: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="7044075693643009662">"নেটৱৰ্কিং"</string>
-    <string name="wifi_display_certification" msgid="8611569543791307533">"বেতাঁৰ ডিছপ্লে প্ৰমাণীকৰণ"</string>
+    <string name="wifi_display_certification" msgid="8611569543791307533">"বেতাঁৰ ডিছপ্লে’ প্ৰমাণীকৰণ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"ৱাই-ফাই ভাৰ্ব\'ছ লগিং সক্ষম কৰক"</string>
     <string name="wifi_connected_mac_randomization" msgid="3168165236877957767">"সংযুক্ত MAC যাদৃচ্ছিকৰণ"</string>
-    <string name="mobile_data_always_on" msgid="8774857027458200434">"ম\'বাইল ডেটা সদা-সক্ৰিয়"</string>
+    <string name="mobile_data_always_on" msgid="8774857027458200434">"ম’বাইল ডেটা সদা-সক্ৰিয়"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"টেডাৰিং হাৰ্ডৱেৰ ত্বৰণ"</string>
     <string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"পূৰ্ণ মাত্ৰাৰ ভলিউম অক্ষম কৰক"</string>
@@ -233,7 +233,7 @@
     <string name="private_dns_mode_provider" msgid="8354935160639360804">"ব্যক্তিগত ডিএনএছ প্ৰদানকাৰীৰ হোষ্টনাম"</string>
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ডিএনএছ সেৱা যোগানকাৰীৰ হ\'ষ্টনাম দিয়ক"</string>
     <string name="private_dns_mode_provider_failure" msgid="231837290365031223">"সংযোগ কৰিব পৰা নগ\'ল"</string>
-    <string name="wifi_display_certification_summary" msgid="1155182309166746973">"বেতাঁৰ ডিছপ্লে প্ৰমাণপত্ৰৰ বাবে বিকল্পসমূহ দেখুৱাওক"</string>
+    <string name="wifi_display_certification_summary" msgid="1155182309166746973">"বেতাঁৰ ডিছপ্লে’ প্ৰমাণপত্ৰৰ বাবে বিকল্পসমূহ দেখুৱাওক"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ৱাই-ফাই লগিঙৰ মাত্ৰা বঢ়াওক, Wi‑Fi পিকাৰত প্ৰতি SSID RSSI দেখুৱাওক"</string>
     <string name="wifi_connected_mac_randomization_summary" msgid="1743059848752201485">"ৱাই-ফাই নেটৱৰ্কৰ লগত সংযোগ কৰি থকাৰ সময়ত MAC ঠিকনা যাদৃচ্ছিক কৰক"</string>
     <string name="wifi_metered_label" msgid="4514924227256839725">"নিৰিখ-নিৰ্দিষ্ট"</string>
@@ -249,7 +249,7 @@
     <string name="allow_mock_location" msgid="2787962564578664888">"নকল অৱস্থানৰ অনুমতি দিয়ক"</string>
     <string name="allow_mock_location_summary" msgid="317615105156345626">"নকল অৱস্থানৰ অনুমতি দিয়ক"</string>
     <string name="debug_view_attributes" msgid="6485448367803310384">"দৃশ্যৰ গুণাগুণ নিৰীক্ষণ সক্ষম কৰক"</string>
-    <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ৱাই-ফাই থকা সময়তো সদায় ম\'বাইল ডেটা সক্ৰিয় ৰাখক (খৰতকীয়াকৈ নেটৱৰ্ক সলনি কৰিবৰ বাবে)।"</string>
+    <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ৱাই-ফাই থকা সময়তো সদায় ম’বাইল ডেটা সক্ৰিয় ৰাখক (খৰতকীয়াকৈ নেটৱৰ্ক সলনি কৰিবৰ বাবে)।"</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"যদিহে উপলব্ধ হয় তেন্তে টেডাৰিং হাৰ্ডৱেৰ ত্বৰণ ব্যৱহাৰ কৰক"</string>
     <string name="adb_warning_title" msgid="6234463310896563253">"ইউএছবি ডিবাগিঙৰ অনুমতি দিয়েনে?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"ইউএছবি ডিবাগ কৰা কাৰ্য কেৱল বিকাশৰ উদ্দেশ্যৰেহে কৰা হৈছে৷ আপোনাৰ কম্পিউটাৰ আৰু আপোনাৰ ডিভাইচৰ মাজত ডেটা প্ৰতিলিপি কৰিবলৈ এইটো ব্যৱহাৰ কৰক, কোনো জাননী নিদিয়াকৈয়ে আপোনাৰ ডিভাইচত এপ্‌সমূহ ইনষ্টল কৰক আৰু লগ ডেটা পঢ়ক৷"</string>
@@ -285,7 +285,7 @@
     <string name="show_touches_summary" msgid="6101183132903926324">"টিপিলে দৃশ্যায়িত ফীডবেক দিয়ক"</string>
     <string name="show_screen_updates" msgid="5470814345876056420">"পৃষ্ঠভাগৰ আপডেইট দেখুৱাওক"</string>
     <string name="show_screen_updates_summary" msgid="2569622766672785529">"আপডেইট হওতে গোটেই ৱিণ্ড পৃষ্ঠসমূহ ফ্লাশ্ব কৰক"</string>
-    <string name="show_hw_screen_updates" msgid="4117270979975470789">"আপডেট চাওক দেখুৱাওক"</string>
+    <string name="show_hw_screen_updates" msgid="4117270979975470789">"আপডে’ট চাওক দেখুৱাওক"</string>
     <string name="show_hw_screen_updates_summary" msgid="6506943466625875655">"অঁকাৰ সময়ত ৱিণ্ড\'ৰ ভিতৰত ফ্লাশ্ব দৰ্শন"</string>
     <string name="show_hw_layers_updates" msgid="5645728765605699821">"হাৰ্ডৱেৰৰ তৰপৰ আপডেইট দেখুৱাওক"</string>
     <string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"হাৰ্ডৱেৰ লেয়াৰ আপডেইট হওতে সিঁহতক সেউজীয়া ৰঙেৰে ফ্লাশ্ব কৰক"</string>
@@ -327,9 +327,10 @@
     <string name="local_backup_password_title" msgid="3860471654439418822">"ডেস্কটপ বেকআপ পাছৱৰ্ড"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ডেস্কটপৰ পূৰ্ণ বেকআপ এতিয়ালৈকে সংৰক্ষিত অৱস্থাত নাই"</string>
     <string name="local_backup_password_summary_change" msgid="5376206246809190364">"ডেস্কটপ সম্পূৰ্ণ বেকআপৰ বাবে পাছৱৰ্ডটো সলনি কৰিবলৈ বা আঁতৰাবলৈ টিপক"</string>
-    <string name="local_backup_password_toast_success" msgid="582016086228434290">"নতুন বেকআপ পাছৱৰ্ড ছেট কৰা হ\'ল"</string>
+    <string name="local_backup_password_toast_success" msgid="582016086228434290">"নতুন বেকআপ পাছৱৰ্ড ছেট কৰা হ’ল"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"নতুন পাছৱৰ্ডটোৰ লগত নিশ্চিত কৰা পাছৱৰ্ডটো মিলা নাই"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"বেকআপ পাছৱৰ্ড নিৰ্ধাৰণ কৰিব পৰা নহ\'ল"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ল’ড হৈ আছে…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"জীৱন্ত (ডিফ\'ল্ট)"</item>
     <item msgid="8446070607501413455">"প্ৰাকৃতিক"</item>
@@ -422,15 +423,15 @@
     <string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"ছিষ্টেমৰ ভাষা ব্যৱহাৰ কৰক"</string>
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>ৰ ছেটিংবিলাক খুলিব পৰা নগ\'ল"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"এই ইনপুট পদ্ধতিটোৱে আপুনি টাইপ কৰা আপোনাৰ ব্যক্তিগত ডেটা যেনে পাছৱৰ্ডসমূহ আৰু ক্ৰেডিট কাৰ্ডৰ নম্বৰসমূহকে ধৰি সকলো পাঠ সংগ্ৰহ কৰিবলৈ সক্ষম হ\'ব পাৰে। <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> এপটোৰ লগত ই সংলগ্ন। এই ইনপুট পদ্ধতিটো ব্যৱহাৰ কৰেনে?"</string>
-    <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"টোকা: ৰিবুট কৰাৰ পিছত আপুনি ফ\'নটো আনলক নকৰালৈকে এই এপটো ষ্টাৰ্ট নহ\'ব"</string>
+    <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"টোকা: ৰিবুট কৰাৰ পিছত আপুনি ফ\'নটো আনলক নকৰালৈকে এই এপটো ষ্টাৰ্ট নহ’ব"</string>
     <string name="ims_reg_title" msgid="7609782759207241443">"আইএমএছ পঞ্জীয়ন স্থিতি"</string>
     <string name="ims_reg_status_registered" msgid="933003316932739188">"পঞ্জীকৃত"</string>
     <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"পঞ্জীকৃত নহয়"</string>
     <string name="status_unavailable" msgid="7862009036663793314">"উপলব্ধ নহয়"</string>
     <string name="wifi_status_mac_randomized" msgid="5589328382467438245">"MAC ক্ৰমানুসৰি ছেট কৰা হোৱা নাই"</string>
     <plurals name="wifi_tether_connected_summary" formatted="false" msgid="3871603864314407780">
-      <item quantity="one">%1$dটা ডিভাইচ সংযোগ হ\'ল</item>
-      <item quantity="other">%1$dটা ডিভাইচ সংযোগ হ\'ল</item>
+      <item quantity="one">%1$dটা ডিভাইচ সংযোগ হ’ল</item>
+      <item quantity="other">%1$dটা ডিভাইচ সংযোগ হ’ল</item>
     </plurals>
     <string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"অধিক সময়।"</string>
     <string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"কম সময়।"</string>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্ৰতিবাৰতে সোধক"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"আপুনি অফ নকৰা পর্যন্ত"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"এই মাত্ৰ"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"বিকাশকাৰ্য চলি থকা আপডে\'টেড গ্ৰাফিক ড্ৰাইভাৰ ব্যৱহাৰ কৰিবলৈ এপ্ অপ্ট ইন কৰক"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফ’নৰ স্পীকাৰ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 17d0ca6..daa9c8f 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Yeni rezerv parolu ayarlandı"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Yeni parol və parolun təkrarı uyğun gəlmir"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Yedəkləmə parolu xətası"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Yüklənir…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Canlı (defolt)"</item>
     <item msgid="8446070607501413455">"Təbii"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Hər dəfə soruşun"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Deaktiv edənə qədər"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"İndicə"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Qrafik drayverdən istifadə etmək üçün tətbiq seçin"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon spikeri"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 56052d2..44f14df 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Postavljena je nova lozinka rezervne kopije"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nova lozinka i njena potvrda se ne podudaraju"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Postavljanje lozinke rezervne kopije nije uspelo"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Učitava se…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Živopisan (podrazumevano)"</item>
     <item msgid="8446070607501413455">"Prirodan"</item>
@@ -450,6 +451,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uvek pitaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogući aplikaciju za korišćenje upravljačkog programa grafičke katice u razvoju"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index f2c2046..2dcdd37 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Усталяваны новы пароль для рэзервовага капіявання"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Новы пароль і яго пацвярджэнне не супадаюць"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Збой пры ўсталёўцы паролю для рэзервовага капіявання"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Ідзе загрузка…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Сочны (па змаўчанні)"</item>
     <item msgid="8446070607501413455">"Натуральны"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Заўсёды пытацца"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Пакуль не выключыце"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Зараз"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Выбраная праграма, якая выкарыстоўвае абноўлены драйвер графічнай сістэмы (падчас распрацоўкі)"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Дынамік тэлефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 5b8d6b8..1e2f0f4 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Зададена е нова парола за резервно копие"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Новата парола и въведената за потвърждаване не съвпадат"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Задаването на парола за резервно копие не бе успешно"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Зарежда се…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Ярък (по подразбиране)"</item>
     <item msgid="8446070607501413455">"Естествен"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Да се пита винаги"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"До изключване"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Току-що"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Включване на приложението за използване на актуализирания графичен драйвер в разработка"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Високоговорител на телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 7efad99..4ec893b 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"নতুন ব্যাকআপ পাসওয়ার্ড সেট করুন"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"নতুন পাসওয়ার্ড এবং নিশ্চিতকরণ মিলছে না"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ব্যাকআপ পাসওয়ার্ড সেট করা ব্যর্থ"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"লোড হচ্ছে…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"ভাইব্রেন্ট (ডিফল্ট)"</item>
     <item msgid="8446070607501413455">"প্রাকৃতিক"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্রতিবার জিজ্ঞেস করা হবে"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"এখনই"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ডেভলপমেন্টে আপডেট হওয়া গ্রাফিক্স ড্রাইভার ব্যবহার করতে অ্যাপ বেছে নিন"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফেনের স্পিকার"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 92435f7..a0a0e2a 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova lozinka za sigurnosnu kopiju je postavljena"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nova lozinka i potvrda se ne podudaraju"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nije uspjelo postavljanje lozinke za sigurnosnu kopiju"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Učitavanje…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Živopisno (zadano)"</item>
     <item msgid="8446070607501413455">"Prirodan"</item>
@@ -450,6 +451,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prijavi aplikaciju za korištenje ažuriranog grafičkog drajvera u razvoju"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 3182f16..0bd6a1b 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"S\'ha definit una contrasenya de seguretat nova"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"La contrasenya nova i la confirmació no coincideixen"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Error en definir la contrasenya de seguretat"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Carregant…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrant (predeterminat)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pregunta sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Fins que no ho desactivis"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ara mateix"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicació activada per utilitzar el controlador de gràfics actualitzat en desenvolupament"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altaveu del telèfon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 3785433..69c22ec 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nové heslo pro zálohy je nastaveno"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nové heslo se neshoduje s potvrzením hesla."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nastavení hesla pro zálohy selhalo"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Načítání…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Jasné (výchozí)"</item>
     <item msgid="8446070607501413455">"Přirozené"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pokaždé se zeptat"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokud tuto funkci nevypnete"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Právě teď"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Přihlaste aplikaci k použití vyvíjeného aktualizovaného grafického ovladače"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefonu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index eeeccee..077258a 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -73,8 +73,8 @@
     <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Filoverførsel"</string>
     <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Inputenhed"</string>
     <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internetadgang"</string>
-    <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Deling af kontaktpersoner"</string>
-    <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Brug til deling af kontaktpersoner"</string>
+    <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Deling af kontakter"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Brug til deling af kontakter"</string>
     <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling af internetforbindelse"</string>
     <string name="bluetooth_profile_map" msgid="1019763341565580450">"Sms-beskeder"</string>
     <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-adgang"</string>
@@ -102,7 +102,7 @@
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Par"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ACCEPTÉR PARRING"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuller"</string>
-    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Parring giver adgang til dine kontaktpersoner og din opkaldshistorik, når enhederne er forbundet."</string>
+    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Parring giver adgang til dine kontakter og din opkaldshistorik, når enhederne er forbundet."</string>
     <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Der kunne ikke parres med <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Der kunne ikke parres med <xliff:g id="DEVICE_NAME">%1$s</xliff:g> på grund af en forkert pinkode eller adgangsnøgle."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Der kan ikke kommunikeres med <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
@@ -252,7 +252,7 @@
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Hold altid mobildata aktiveret, selv når Wi-Fi er aktiveret (for at skifte hurtigt mellem netværk)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Brug hardwareacceleration ved netdeling, hvis det er muligt"</string>
     <string name="adb_warning_title" msgid="6234463310896563253">"Vil du tillade USB-fejlretning?"</string>
-    <string name="adb_warning_message" msgid="7316799925425402244">"USB-fejlretning er kun beregnet til udvikling og kan bruges til at kopiere data mellem din computer og enheden, installere apps på enheden uden underretning og læse logdata."</string>
+    <string name="adb_warning_message" msgid="7316799925425402244">"USB-fejlretning er kun beregnet til udvikling og kan bruges til at kopiere data mellem din computer og enheden, installere apps på enheden uden notifikation og læse logdata."</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vil du ophæve adgangen til USB-fejlfinding for alle computere, du tidligere har godkendt?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Vil du tillade udviklingsindstillinger?"</string>
     <string name="dev_settings_warning_message" msgid="2298337781139097964">"Disse indstillinger er kun beregnet til brug i forbindelse med udvikling. De kan forårsage, at din enhed og dens apps går ned eller ikke fungerer korrekt."</string>
@@ -316,8 +316,8 @@
     <string name="app_process_limit_title" msgid="4280600650253107163">"Grænse for baggrundsprocesser"</string>
     <string name="show_all_anrs" msgid="4924885492787069007">"Vis ANR-fejl i baggrunden"</string>
     <string name="show_all_anrs_summary" msgid="6636514318275139826">"Vis dialogboksen \"Appen svarer ikke\" for baggrundsapps"</string>
-    <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Vis advarsler om underretningskanal"</string>
-    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Viser en advarsel, når en app sender en underretning uden en gyldig kanal"</string>
+    <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Vis advarsler om notifikationskanal"</string>
+    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Viser en advarsel, når en app sender en notifikation uden en gyldig kanal"</string>
     <string name="force_allow_on_external" msgid="3215759785081916381">"Gennemtving tilladelse til eksternt lager"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"Gør det muligt at overføre enhver app til et eksternt lager uafhængigt af manifestværdier"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Tving aktiviteter til at kunne tilpasses"</string>
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Ny adgangskode til sikkerhedskopi er angivet"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Ny adgangskode og bekræftelse matcher ikke"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Fejl ved angivelse af adgangskode til sikkerhedskopi"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Indlæser…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Klare (standard)"</item>
     <item msgid="8446070607501413455">"Naturlige"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spørg hver gang"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Indtil du deaktiverer"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Lige nu"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Tilvælg en app, der skal bruge den opdaterede grafikdriver under udvikling"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonens højttaler"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 95f7416..c4e03ad 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Neues Sicherungspasswort festgelegt"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Das neue Passwort und die Bestätigung stimmen nicht überein."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Fehler beim Festlegen des Sicherungspassworts"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Wird geladen…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Brillant (Standardeinstellung)"</item>
     <item msgid="8446070607501413455">"Natürlich"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Jedes Mal fragen"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Bis zur Deaktivierung"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Gerade eben"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App aktivieren, um den aktualisierten Grafiktreiber in der Entwicklung zu verwenden"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon-Lautsprecher"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 310b6cf..642e862 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Ορίστηκε νέος εφεδρικός κωδικός πρόσβασης"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Ο νέος κωδικός πρόσβασης και η επιβεβαίωση δεν ταιριάζουν"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Αποτυχία κατά τον ορισμό εφεδρικού κωδικού πρόσβασης"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Φόρτωση…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Ζωντανό (προεπιλογή)"</item>
     <item msgid="8446070607501413455">"Φυσικό"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Να ερωτώμαι κάθε φορά"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Μέχρι την απενεργοποίηση"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Μόλις τώρα"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Επιλέξτε μια εφαρμογή για τη χρήση του ενημερωμένου προγράμματος οδήγησης γραφικών σε ανάπτυξη"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Ηχείο τηλεφώνου"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 5b11d63..0cdfaf2 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"New backup password set"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"New password and confirmation don\'t match"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Failure setting backup password"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Loading…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrant (default)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 5b11d63..0cdfaf2 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"New backup password set"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"New password and confirmation don\'t match"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Failure setting backup password"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Loading…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrant (default)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 5b11d63..0cdfaf2 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"New backup password set"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"New password and confirmation don\'t match"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Failure setting backup password"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Loading…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrant (default)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 5b11d63..0cdfaf2 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"New backup password set"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"New password and confirmation don\'t match"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Failure setting backup password"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Loading…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrant (default)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index d7ed49a..eda4d1b 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎New backup password set‎‏‎‎‏‎"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎New password and confirmation don’t match‎‏‎‎‏‎"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎Failure setting backup password‎‏‎‎‏‎"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‎‎Loading…‎‏‎‎‏‎"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎Vibrant (default)‎‏‎‎‏‎"</item>
     <item msgid="8446070607501413455">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎Natural‎‏‎‎‏‎"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎Ask every time‎‏‎‎‏‎"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎Until you turn off‎‏‎‎‏‎"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎Just now‎‏‎‎‏‎"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎Opt in app to use updated graphcis driver in developement‎‏‎‎‏‎"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎Phone speaker‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index ecec63c..bd3d1b7 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nueva contraseña de copia de seguridad definida"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"La nueva contraseña y la de confirmación no coinciden."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Error al definir contraseña de copia de seguridad"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Cargando…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrante (predeterminado)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que lo desactives"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Recién"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar app para que use el controlador de gráficos actualizado en el desarrollo"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index b8fdf30..94f88eb 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nueva contraseña de seguridad establecida"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"La nueva contraseña y la de confirmación no coinciden"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Error al establecer la contraseña de seguridad"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Cargando…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrante (predeterminado)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que se desactive"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Justo ahora"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar aplicación para usar controlador de gráficos actualizado en desarrollo"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 1246626..31527dc 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Uus varuparool on määratud"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Uus parool ja kinnitus ei ühti"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Varuparooli määramine ebaõnnestus"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Laadimine …"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Ere (vaikeseade)"</item>
     <item msgid="8446070607501413455">"Loomulikud"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Küsi iga kord"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kuni välja lülitate"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsja"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lubage rakendus, et kasutada arenduses olevat värskendatud graafikadraiverit"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoni kõlar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 68b4840..dd77284 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Babeskopiaren pasahitz berria ezarri da"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Pasahitz berria eta berrespena ez datoz bat"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Ezin izan da babeskopiaren pasahitza ezarri"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Kargatzen…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Bizia (balio lehenetsia)"</item>
     <item msgid="8446070607501413455">"Naturala"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Galdetu beti"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Desaktibatu arte"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Oraintxe"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Hautatu zein aplikaziorekin erabili nahi duzun garatze-prozesuan dagoen grafikoen kontrolatzaile eguneratua"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonoaren bozgorailua"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 7882b056..57f8960 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"گذرواژه جدید نسخهٔ پشتیبان تنظیم شد"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"گذرواژه جدید و تأیید آن با یکدیگر مطابقت ندارند"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"گذرواژه پشتیبان‌گیری تنظیم نشد"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"بارگیری…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"پرطروات (پیش‌فرض)"</item>
     <item msgid="8446070607501413455">"طبیعی"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"هربار پرسیده شود"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"تا زمانی‌که آن را خاموش کنید"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"هم‌اکنون"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"برنامه انتخاب‌شده برای استفاده از درایور گرافیک به‌روزرسانی‌شده در برنامه‌نویس"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"بلندگوی تلفن"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 1904c4d..922d8d3 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Uusi varasalasana asetettiin"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Uusi salasana ja vahvistus eivät täsmää"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Varasalasanan asetus epäonnistui"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Ladataan…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Voimakas (oletus)"</item>
     <item msgid="8446070607501413455">"Luonnollinen"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Kysy aina"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kunnes poistat sen käytöstä"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsken"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lisää sovellus käyttämään päivitettyä grafiikkaohjainta kehitysvaiheessa"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Puhelimen kaiutin"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 45b2872..765a80f 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Le nouveau mot de passe de secours a bien été défini."</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Le nouveau mot de passe et sa confirmation ne correspondent pas."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Échec de la définition du mot de passe de secours."</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Chargement en cours…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrantes (par défaut)"</item>
     <item msgid="8446070607501413455">"Naturelles"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionnez l\'application pour utiliser le pilote graphique mis à jour en mode de conception"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 13361d7..198b922 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Le nouveau mot de passe de secours a bien été défini."</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Le nouveau mot de passe et sa confirmation ne correspondent pas."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Échec de la définition du mot de passe de secours."</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Chargement…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Couleurs éclatantes (par défaut)"</item>
     <item msgid="8446070607501413455">"Couleurs naturelles"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionner une application pour le développement de laquelle utiliser le pilote graphique mis à jour"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index e8cf009..de5e7e41 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Novo contrasinal da copia de seguranza definido"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"O contrasinal novo e a confirmación non coinciden"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Erro ao definir un contrasinal da copia de seguranza"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Cargando…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Brillante (predeterminado)"</item>
     <item msgid="8446070607501413455">"Naturais"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Ata a desactivación"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Subscribirse á aplicación para utilizar o controlador de gráficos actualizado en desenvolvemento"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altofalante do teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 918920d..993eed1 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"નવો બેકઅપ પાસવર્ડ સેટ કર્યો છે"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"નવો પાસવર્ડ અને પુષ્ટિકરણ મેળ ખાતા નથી"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"નિષ્ફળતા સેટિંગ બેકઅપ પાસવર્ડ"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"લોડ થઈ રહ્યું છે…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"વાઇબ્રન્ટ (ડિફોલ્ટ)"</item>
     <item msgid="8446070607501413455">"કુદરતી"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"દર વખતે પૂછો"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"તમે બંધ ન કરો ત્યાં સુધી"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"હમણાં જ"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"અપડેટ કરેલ ગ્રાફિક્સ ડ્રાઇવરનો ઉપયોગ કરવા માટે અ‍ૅપ પસંદ કરો"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ફોન સ્પીકર"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 890d036..faf551e 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"नया बैकअप पासवर्ड सेट किया गया"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"नया पासवर्ड तथा पुष्टि मेल नही खाते"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"सुरक्षित पासवर्ड सेट करने में विफल रहा"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"लोड हो रहा है…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"चमकीला (डिफ़ॉल्ट)"</item>
     <item msgid="8446070607501413455">"स्वाभाविक"</item>
@@ -416,7 +417,7 @@
     <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"कस्टम (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"मेन्यू"</string>
     <string name="retail_demo_reset_message" msgid="118771671364131297">"डेमो मोड में फ़ैक्टरी रीसेट के लिए पासवर्ड डालें"</string>
-    <string name="retail_demo_reset_next" msgid="8356731459226304963">"आगे"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"आगे बढ़ें"</string>
     <string name="retail_demo_reset_title" msgid="696589204029930100">"पासवर्ड आवश्यक"</string>
     <string name="active_input_method_subtypes" msgid="3596398805424733238">"टाइप करने की सक्रीय पद्धतियां"</string>
     <string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"सिस्टम की भाषाओं का उपयोग करें"</string>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"हर बार पूछें"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"जब तक आप इसे बंद नहीं करते"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"अभी-अभी"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"डेवलपमेंट में अपडेट किए गए ग्राफ़िक्स ड्राइवर का इस्तेमाल करने के लिए ऐप्लिकेशन में ऑप्ट इन करें"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फ़ोन स्पीकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 1464b91..5f04b65 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova zaporka za sigurnosnu kopiju postavljena"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nova zaporka i potvrda ne odgovaraju"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nije uspjelo postavljanje zaporke za sigurnosnu kopiju"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Učitavanje…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Živopisno (zadano)"</item>
     <item msgid="8446070607501413455">"Prirodno"</item>
@@ -450,6 +451,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sad"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uključi aplikaciju za upotrebu ažuriranog upravljačkog programa u razvoju"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index c9fdd45..7469c1a 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Új mentési jelszó beállítva"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Az új jelszó és a megerősítése nem egyezik."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Hiba a mentési jelszó beállítása során"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Betöltés…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Élénk (alapértelmezett)"</item>
     <item msgid="8446070607501413455">"Természetes"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Mindig kérdezzen rá"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kikapcsolásig"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az imént"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"A frissített, fejlesztés alatt álló grafikus drivert használja a választott alkalmazás"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hangszórója"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 3b13ed1..b27bb7e 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Պահուստավորման նոր գաղտնաբառը սահմանված է"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Նոր գաղտնաբառը և հաստատումը չեն համընկնում"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Ձախողում գաղտնաբառի պահուստավորման կարգավորման ընթացքում"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Բեռնում…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Պայծառ (կանխադրված)"</item>
     <item msgid="8446070607501413455">"Բնական"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Հարցնել ամեն անգամ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Մինչև չանջատեք"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Հենց նոր"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ընտրված հավելվածը, որը պետք է օգտագործի թարմացված գրաֆիկական սարքավարը մշակման ժամանակ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Հեռախոսի բարձրախոս"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 4d1b2df..342a505 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Sandi cadangan baru telah disetel"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Sandi baru dan konfirmasinya tidak cocok."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Gagal menyetel sandi cadangan"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Memuat…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Terang (default)"</item>
     <item msgid="8446070607501413455">"Alami"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Selalu tanya"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Sampai Anda menonaktifkannya"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Baru saja"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ikut sertakan aplikasi untuk menggunakan driver grafis yang diupdate dalam pengembangan"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ponsel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 600ffa4..8f1b9a1 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nýtt aðgangsorð fyrir afritun valið"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nýja aðgangsorðið og staðfestingaraðgangsorðið eru ekki eins"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Villa við að velja aðgangsorð fyrir afritun"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Hleður…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Líflegir (sjálfgefið)"</item>
     <item msgid="8446070607501413455">"Náttúrulegir"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spyrja í hvert skipti"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Þar til þú slekkur"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Rétt í þessu"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velja að nota uppfærðan myndefnisrekil í þróun í forriti"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Símahátalari"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 068aa91..6600ca2 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nuova password di backup impostata"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Le password inserite non corrispondono"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Impossibile impostare la password di backup"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Caricamento…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vivaci (opzione predefinita)"</item>
     <item msgid="8446070607501413455">"Naturali"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Chiedi ogni volta"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Fino alla disattivazione"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Adesso"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Attiva l\'app per utilizzare il driver grafico aggiornato nella versione di sviluppo"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlante telefono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index fa38f77..a8da0b1 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"הוגדרה סיסמת גיבוי חדשה"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"הסיסמה החדשה והאישור אינם תואמים"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"הגדרת סיסמת גיבוי נכשלה"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"טוען…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"דינמי (ברירת מחדל)"</item>
     <item msgid="8446070607501413455">"טבעי"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"שאל בכל פעם"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"עד הכיבוי"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"הרגע"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"האפליקציה שנבחרה לשימוש במנהל ההתקן המעודכן לגרפיקה שבפיתוח"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"רמקול של טלפון"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 9927654..ea62637 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"新しいバックアップパスワードが設定されました"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"新しいパスワードと確認用のパスワードが一致しません。"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"バックアップパスワードの設定に失敗しました"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"読み込んでいます…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"鮮やか(既定)"</item>
     <item msgid="8446070607501413455">"自然"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"毎回確認"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"OFF にするまで"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"たった今"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"更新したグラフィックス ドライバを開発に使用するオプトイン アプリ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"スマートフォンのスピーカー"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 7ba0153..bd88435 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"ახალი სარეზერვო პაროლის დაყენება"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"ახალი და დადასტურებული პაროლები არ შეესატყვისება ერთმანეთს"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"სარეზერვო პაროლის დაყენება ვერ მოხერხდა"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"მიმდინარეობს ჩატვირთვა…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"მკვეთრი (ნაგულისხმევი)"</item>
     <item msgid="8446070607501413455">"ბუნებრივი"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ყოველთვის მკითხეთ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"გამორთვამდე"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ახლახან"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"გააქტიურების აპი, რომელიც გამოიყენებს შემუშავების პროცესში მყოფ, განახლებულ გრაფიკულ დრაივერს"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ტელეფონის დინამიკი"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index d5b4441..d22b139 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Жаңа сақтық кілтсөзі тағайындалды"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Жаңа кілтсөз және растау сәйкес емес"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Сақтық кілтсөзі тағайындалмады"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Жүктелуде…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Жарқын (әдепкі)"</item>
     <item msgid="8446070607501413455">"Табиғи"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Әрдайым сұрау"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Өшірілгенге дейін"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дәл қазір"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Әзірлеу барысында қолданба жаңартылған графика драйверін пайдаланады"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефон динамигі"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index d1d1c76..d516db9 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"កំណត់​ពាក្យ​សម្ងាត់​បម្រុង​ទុក​ថ្មី"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"ពាក្យ​សម្ងាត់​ថ្មី និង​ការ​បញ្ជាក់​​មិន​ដូច​គ្នា"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"បរាជ័យ​ក្នុង​ការ​កំណត់​ពាក្យ​សម្ងាត់​បម្រុងទុក"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"កំពុងផ្ទុក…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"រស់រវើក (លំនាំដើម)"</item>
     <item msgid="8446070607501413455">"ធម្មជាតិ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"សួរគ្រប់ពេល"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"រហូតទាល់តែ​អ្នកបិទ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"អម្បាញ់មិញ"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ភ្ជាប់កម្មវិធី​ ដើម្បី​ប្រើដ្រាយវើ​ក្រាហ្វិក​ដែលបាន​ដំឡើងជំនាន់​សម្រាប់​ការ​អភិវឌ្ឍ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ឧបករណ៍​បំពង​សំឡេង​ទូរសព្ទ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 46759f5..94a711c 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"ಹೊಸ ಬ್ಯಾಕಪ್ ಪಾಸ್‌ವರ್ಡ್‌ ಹೊಂದಿಸಲಾಗಿದೆ"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"ಹೊಸ ಪಾಸ್‌ವರ್ಡ್‌ ಮತ್ತು ದೃಢೀಕರಣ ಹೊಂದಾಣಿಕೆಯಾಗುತ್ತಿಲ್ಲ"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ಬ್ಯಾಕಪ್‌ ಪಾಸ್‌ವರ್ಡ್‌ ಹೊಂದಿಕೆ ವಿಫಲಗೊಂಡಿದೆ"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ಲೋಡ್ ಆಗುತ್ತಿದೆ…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"ಸ್ಪಂದನಾತ್ಮಕ (ಡೀಫಾಲ್ಟ್)"</item>
     <item msgid="8446070607501413455">"ಪ್ರಾಕೃತಿಕ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ಇದೀಗ"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ಅಭಿವೃದ್ಧಿಯಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾದ ಗ್ರಾಫಿಕ್‌ಗಳ ಡ್ರೈವರ್ ಬಳಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 067175b..0b4125f 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"새 백업 비밀번호가 설정되었습니다."</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"새 비밀번호와 확인한 비밀번호가 일치하지 않습니다."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"백업 비밀번호를 설정하지 못했습니다."</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"로드 중…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"생동감(기본값)"</item>
     <item msgid="8446070607501413455">"내츄럴"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"항상 확인"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"사용 중지할 때까지"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"조금 전"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"개발 중인 업데이트된 그래픽 드라이버를 사용할 앱을 선택하세요."</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"휴대전화 스피커"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 8e994da..42dc97e 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Жаңы бэкапка сырсөз коюулду"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Жаңы сырсөз жана анын ырастоосу дал келген жок"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Жаңы бэкапка сырсөз коюлган жок"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Жүктөлүүдө…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Толкундуу (демейки)"</item>
     <item msgid="8446070607501413455">"Табигый"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ар дайым суралсын"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Бул функция өчүрүлгөнгө чейин"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Азыр эле"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Иштеп чыгууда жаңыртылган графикалык драйверлерди пайдалануу үчүн колдонмону кошуңуз"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефондун динамиги"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 7bf46c0..0505a5d 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"ຕັ້ງລະຫັດສຳຮອງໃໝ່ແລ້ວ"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"ລະຫັດຜ່ານໃໝ່ ແລະລະຫັດຢືນຢັນບໍ່ກົງກັນ"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ການຕັ້ງລະຫັດສຳຮອງຂໍ້ມູນລົ້ມເຫລວ"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ກຳລັງໂຫລດ…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"ຕົວ​ສັ່ນ (ມາດ​ຕະ​ຖານ)"</item>
     <item msgid="8446070607501413455">"ທຳ​ມະ​ຊາດ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ຖາມທຸກເທື່ອ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ຈົນກວ່າທ່ານຈະປິດ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ຕອນນີ້"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ເຂົ້າຮ່ວມແອັບເພື່ອໃຊ້ໄດຣເວີກຣາຟິກທີ່ອັບເດດແລ້ວໃນການພັດທະນາ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ລຳໂພງໂທລະສັບ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 9da3d52..e1412e7 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nustatytas naujas atsarginės kopijos slaptažodis"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Naujas slaptažodis ir patvirtinimas neatitinka"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nustatant atsarginės kopijos slaptažodį įvyko klaida"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Įkeliama…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Ryškus (numatytasis)"</item>
     <item msgid="8446070607501413455">"Natūralus"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Klausti kaskart"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kol išjungsite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ką tik"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Pasirinkti programą, kuri bus naudojama su atnaujinta kuriama grafikos tvarkykle"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefono garsiakalbis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 2233468..5082775 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Jaunā dublējuma parole ir iestatīta."</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Jaunā parole un apstiprinājums neatbilst."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Iestatot dublējuma paroli, radās kļūme."</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Notiek ielāde…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Spilgts (noklusējums)"</item>
     <item msgid="8446070607501413455">"Dabisks"</item>
@@ -450,6 +451,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vaicāt katru reizi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Līdz brīdim, kad izslēgsiet"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Tikko"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Izvēlēties izmantot atjaunināto grafikas dzini šīs lietotnes izstrādē"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Tālruņa skaļrunis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 534d154..99cf468 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Подесена нова лозинка на резервна копија"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Новата лозинка и потврдата не се исти"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Неуспешно подесување лозинка на резервна копија"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Се вчитува…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Динамично (стандардно)"</item>
     <item msgid="8446070607501413455">"Природно"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Секогаш прашувај"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Додека не го исклучите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Неодамнешни"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Прифатете ја апликацијата за да се користи ажурираниот драјвер за графика во програмирање"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефонски звучник"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 7a335cd..a0222ad 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"പുതിയ ബാക്കപ്പ് പാസ്‌വേഡ് സജ്ജമാക്കി"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"പുതിയ പാസ്‌വേഡും സ്ഥിരീകരണവും പൊരുത്തപ്പെടുന്നില്ല"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ബാക്കപ്പ് പാസ്‌വേഡ് സജ്ജമാക്കുന്നതിൽ പരാജയപ്പെട്ടു"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ലോഡ് ചെയ്യുന്നു…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"വൈബ്രന്റ് (സ്ഥിരമായത്)"</item>
     <item msgid="8446070607501413455">"സ്വാഭാവികം"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"എപ്പോഴും ചോദിക്കുക"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ഇപ്പോൾ"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"വികസനത്തിൽ, അപ്ഡേറ്റ് ചെയ്‌ത ഗ്രാഫിക്‌സ് ഡ്രൈവർ ഉപയോഗിക്കാൻ ഓപ്റ്റ് ഇൻ ചെയ്യുക"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ഫോൺ സ്‌പീക്കർ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 4816643..5b2acac 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Нөөцлөлтийн шинэ нууц үг тохирууллаа"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Шинэ нууц үг болон баталгаажуулалт таарахгүй байна"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Нөөшлөлтийн нууц үгийг тохируулахад алдаа гарлаа"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Ачаалж байна…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Хурц (үндсэн)"</item>
     <item msgid="8446070607501413455">"Байгалийн"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Тухай бүрт асуух"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Таныг унтраах хүртэл"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дөнгөж сая"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Хөгжүүлэлтэд буй шинэчилсэн график драйверийг ашиглахын тулд аппад нэгдэх"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Утасны чанга яригч"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 5b09b29..891ada8 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"नवीन बॅक अप पासवर्ड सेट झाला"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"नवीन पासवर्ड आणि पुष्टीकरण जुळत नाही"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"बॅक अप पासवर्ड सेट करणे अयशस्वी"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"लोड करत आहे…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"सशक्त (डीफॉल्ट)"</item>
     <item msgid="8446070607501413455">"नैसर्गिक"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक वेळी विचारा"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"तुम्ही बंद करेपर्यंत"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"आत्ताच"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"अपडेट केलेले ग्राफिक ड्राइव्हर डेव्हलमेंटमध्ये वापरण्यासाठी अ‍ॅप निवडा"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनचा स्पीकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 3593882..8030c3e 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Kata laluan sandaran baharu telah ditetapkan"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Kata laluan baharu dan pengesahan tidak sepadan"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Gagal menetapkan kata laluan sandaran"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Memuatkan…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Terang (lalai)"</item>
     <item msgid="8446070607501413455">"Semula Jadi"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Tanya setiap kali"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Sehingga anda matikan"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sebentar tadi"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sertakan apl untuk menggunakan pemacu grafik yang dikemas kini dalam pembangunan"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Pembesar suara telefon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 7ac3742..c543974 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"အရန်သိမ်းဆည်းခြင်းအတွက် စကားဝှက်အသစ်ကို သတ်မှတ်ပြီးပြီ။"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"စကားဝှက်အသစ်နှင့် အတည်ပြုချက် ကွဲလွဲနေသည်။"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"အရန်သိမ်းဆည်းခြင်းအတွက် စကားဝှက်သတ်မှတ်ချက် မအောင်မြင်ပါ။"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ဖွင့်နေသည်…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"တက်ကြွခြင်း (မူလ)"</item>
     <item msgid="8446070607501413455">"သဘာဝ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"အမြဲမေးပါ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"သင်ပိတ်လိုက်သည် အထိ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ယခုလေးတင်"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ဆော့ဖ်ဝဲရေးဆွဲမှုအတွင်း အပ်ဒိတ်လုပ်ထားသော ဂရပ်ဖစ်ဒရိုင်ဗာကို အသုံးပြုရန် အက်ပ်ကို ရွေးချယ်ပါ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ဖုန်းစပီကာ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index e810ba2..c4ad033 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nytt passord for sikkerhetskopiering er angitt."</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Gjentakelsen av passordet er ikke identisk med det første du skrev inn"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Kunne ikke angi nytt passord for sikkerhetskopiering"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Laster inn …"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Krystallklar (standard)"</item>
     <item msgid="8446070607501413455">"Naturlig"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spør hver gang"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Til du slår av"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nå nettopp"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velg app for å bruke en oppdatert grafikkdriver som er under utvikling"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonhøyttaler"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 0b4510fd..f67e05c 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -231,7 +231,7 @@
     <string name="private_dns_mode_off" msgid="8236575187318721684">"निष्क्रिय छ"</string>
     <string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"स्वचालित"</string>
     <string name="private_dns_mode_provider" msgid="8354935160639360804">"निजी DNS प्रदायकको होस्टनाम"</string>
-    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS प्रदायकको होस्टनाम प्रविष्ट गर्नुहोस्"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS प्रदायकको होस्टनाम प्रविष्टि गर्नुहोस्"</string>
     <string name="private_dns_mode_provider_failure" msgid="231837290365031223">"जडान गर्न सकिएन"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"नयाँ जगेडा पासवर्ड सेट गर्नुहोस्"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"नयाँ पासवर्ड र पुष्टिकरण मेल खाँदैनन्"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"विफलता सेटिङ ब्याकअप पासवर्ड"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"लोड गर्दै…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"जोसिलो (पूर्व निर्धारित)"</item>
     <item msgid="8446070607501413455">"प्राकृतिक"</item>
@@ -415,7 +416,7 @@
     <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सबैभन्दा ठूलो"</string>
     <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"आफू अनुकूल (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"मेनु"</string>
-    <string name="retail_demo_reset_message" msgid="118771671364131297">"डेमो मोडमा फ्याक्ट्री रिसेट गर्न पासवर्ड प्रविष्ट गर्नुहोस्"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"डेमो मोडमा फ्याक्ट्री रिसेट गर्न पासवर्ड प्रविष्टि गर्नुहोस्"</string>
     <string name="retail_demo_reset_next" msgid="8356731459226304963">"अर्को"</string>
     <string name="retail_demo_reset_title" msgid="696589204029930100">"पासवर्ड आवश्यक छ"</string>
     <string name="active_input_method_subtypes" msgid="3596398805424733238">"आगत विधिहरू सक्रिय गर्नुहोस्"</string>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक पटक सोध्नुहोस्"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"तपाईंले निष्क्रिय नपार्दासम्म"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"अहिले भर्खरै"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"विकासको क्रममा अद्यावधिक गरिएको ग्राफिक ड्राइभर प्रयोग गर्न अप्ट इन गर्नुहोस्"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनको स्पिकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f227e1c..d2fd4e24 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -236,7 +236,7 @@
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Opties weergeven voor certificering van draadloze weergave"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Logniveau voor wifi verhogen, weergeven per SSID RSSI in wifi-kiezer"</string>
     <string name="wifi_connected_mac_randomization_summary" msgid="1743059848752201485">"Een willekeurig MAC-adres bij het maken van verbinding met wifi-netwerken"</string>
-    <string name="wifi_metered_label" msgid="4514924227256839725">"Betaald"</string>
+    <string name="wifi_metered_label" msgid="4514924227256839725">"Met datalimiet"</string>
     <string name="wifi_unmetered_label" msgid="6124098729457992931">"Gratis"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger-buffergrootten"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Kies Logger-grootten per logbuffer"</string>
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nieuw back-upwachtwoord ingesteld"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nieuw wachtwoord en bevestiging komen niet overeen."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Instellen van back-upwachtwoord is mislukt"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Laden…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Levendig (standaard)"</item>
     <item msgid="8446070607501413455">"Natuurlijk"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Altijd vragen"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Totdat je uitschakelt"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Zojuist"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Meld een app aan om het geüpdatete grafische stuurprogramma in ontwikkeling te gebruiken"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoonluidspreker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index bf8493a..bed22a7 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"ନୂଆ ବ୍ୟାକ୍‌ଅପ୍‌ ପାସ୍‌ୱର୍ଡ ସେଟ୍‌ କରିଦିଆଗଲା"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"ନୂଆ ପାସ୍‌ୱର୍ଡ ଓ ସୁନିଶ୍ଚିତତା ମେଳ ହେଉନାହିଁ"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ବ୍ୟାକ୍‌ଅପ୍‌ ପାସ୍‌ୱର୍ଡ ସେଟିଙ୍ଗ ବିଫଳ ହୋଇଛି"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ଲୋଡ୍ ହେଉଛି…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"ଜୀବନ୍ତ (ପୂର୍ବ-ନିର୍ଦ୍ଧାରିତ)"</item>
     <item msgid="8446070607501413455">"ପ୍ରାକୃତିକ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ DND ଅନ୍‌ ରହିବ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ଏହିକ୍ଷଣି"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ଡେଭଲପ୍‍‍‍‍‍‍‍‍ମେଣ୍ଟରେ ଅପ୍‍‍‍‍‍‍‍ଡେଟ୍‍ ଗ୍ରାଫିକ୍ସ ଡ୍ରାଇଭର୍‍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍ଟ ଇ‍ନ୍‍ ଅପ୍ଲିକେସନ୍‍"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ଫୋନ୍‍ ସ୍ପିକର୍‍"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f948a7e..1c2a947 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"ਨਵਾਂ ਬੈਕਅੱਪ ਪਾਸਵਰਡ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"ਨਵਾਂ ਪਾਸਵਰਡ ਅਤੇ ਪੁਸ਼ਟੀ ਮੇਲ ਨਹੀਂ ਖਾਂਦੀ"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ਬੈਕਅੱਪ ਪਾਸਵਰਡ ਸੈੱਟ ਕਰਨ ਵਿੱਚ ਅਸਫਲਤਾ"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"ਚਮਕੀਲਾ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="8446070607501413455">"ਕੁਦਰਤੀ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ਹੁਣੇ ਹੀ"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ਅੱਪਡੇਟ ਕੀਤੇ ਵਿਕਾਸ-ਅਧੀਨ ਗ੍ਰਾਫਿਕਸ ਡਰਾਈਵਰ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਐਪ ਦੀ ਚੋਣ ਕਰੋ"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index c75a894..1a0a30d 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nowe hasło kopii zapasowej zostało ustawione"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nowe hasła nie pasują do siebie"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nie udało się ustawić hasła kopii zapasowej"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Ładuję…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Żywe (domyślnie)"</item>
     <item msgid="8446070607501413455">"Naturalne"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Zawsze pytaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dopóki nie wyłączysz"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Przed chwilą"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Wybierz aplikację, która ma używać opracowywanego sterownika grafiki"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Głośnik telefonu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index c273f59..2795d47 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova senha de backup definida"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"A nova senha e a confirmação não coincidem."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Falha ao definir a senha de backup"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Carregando…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrante (padrão)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 26e4729..fd15210 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova palavra-passe da cópia de segurança definida"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"A nova palavra-passe e a confirmação não coincidem"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Falha na definição da palavra-passe da cópia de segurança"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"A carregar…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrante (predefinição)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até ser desativado"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Optar pela aplicação para utilizar a placa gráfica atualizada em desenvolvimento"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altifalante do telemóvel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index c273f59..2795d47 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova senha de backup definida"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"A nova senha e a confirmação não coincidem."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Falha ao definir a senha de backup"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Carregando…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrante (padrão)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 94f4842..ecdd003 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"A fost setată o parolă de rezervă nouă"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Parola nouă și confirmarea acesteia nu se potrivesc."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Setarea parolei de rezervă a eșuat"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Se încarcă…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrant (prestabilit)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -450,6 +451,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Întreabă de fiecare dată"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Până când dezactivați"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Chiar acum"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicația pentru înscriere pentru a folosi driverul actualizat al plăcii grafice este în dezvoltare"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Difuzorul telefonului"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 1c331d8..74075c5 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Новый пароль для резервной копии установлен"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Пароли не совпадают"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Не удалось установить пароль для резервной копии"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Загрузка…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Насыщенный (по умолчанию)"</item>
     <item msgid="8446070607501413455">"Естественный"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Всегда спрашивать"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Пока вы не отключите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Только что"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Приложение будет использовать обновленный драйвер графической системы (на стадии разработки)"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Встроенный динамик"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 608ff7f..fc90db5 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"නව උපස්ථ මුරපදය සකසන ලදි"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"නව මුරපදය සහ සත්‍යාපනය නොගැළපුනි"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"උපස්ථ මුරපදය පිහිටුවීම අසාර්ථකය"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"පූරණය වේ…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"දීප්තිමත් (පෙරනිමිය)"</item>
     <item msgid="8446070607501413455">"ස්වභාවික"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"සෑම විටම ඉල්ලන්න"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ඔබ ක්‍රියාවිරහිත කරන තුරු"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"මේ දැන්"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"වැඩිදියුණු වෙමින් ඇති යාවත්කාලීන කළ චිත්‍රක ධාවකය භාවිත කිරීමට යෙදුමට ඇතුළු වන්න"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"දුරකථන ස්පීකරය"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 2f76ef9..ed51f55 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nové heslo pre zálohy je nastavené"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nové heslo a potvrdenie sa nezhodujú"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nastavenie hesla pre zálohy zlyhalo"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Načítava sa…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Sýty (predvolený)"</item>
     <item msgid="8446070607501413455">"Prirodzený"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vždy sa opýtať"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokiaľ túto funkciu nevypnete"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Teraz"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prihlásiť aplikáciu, ktorá má používať aktualizovaný ovládač grafickej karty vo vývoji"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefónu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 64124cb..ad47f75 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Novo geslo je nastavljeno"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Novo geslo in potrditev se ne ujemata."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nastavitev gesla ni uspela"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Nalaganje …"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Živo (privzeto)"</item>
     <item msgid="8446070607501413455">"Naravno"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vedno vprašaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokler ne izklopite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pravkar"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogočena aplikacija za uporabo posodobljenega grafičnega gonilnika pri razvoju"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvočnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 25d575e..f8f491b 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Fjalëkalimi i ri u vendos"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Fjalëkalimi i ri dhe konfirmimi nuk përputhen"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Vendosja e fjalëkalimit dështoi"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Po ngarkohet…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Të gjalla (parazgjedhja)"</item>
     <item msgid="8446070607501413455">"Natyrale"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pyet çdo herë"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Deri sa ta çaktivizosh"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pikërisht tani"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prano aplikacionin për të përdorur drejtuesin e përditësuar të grafikës që është në zhvillim"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlanti i telefonit"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index b58b47c..3fe40f9 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Постављена је нова лозинка резервне копије"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Нова лозинка и њена потврда се не подударају"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Постављање лозинке резервне копије није успело"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Учитава се…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Живописан (подразумевано)"</item>
     <item msgid="8446070607501413455">"Природан"</item>
@@ -450,6 +451,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Увек питај"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Док не искључите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Управо"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Омогући апликацију за коришћење управљачког програма графичке катице у развоју"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Звучник телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 7198b84..186395e 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Ett nytt lösenord har angetts"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Det nya lösenordet och bekräftelsen stämmer inte överens"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Det gick inte att ange lösenordet"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Läser in …"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Starka (standard)"</item>
     <item msgid="8446070607501413455">"Naturliga"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Fråga varje gång"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Tills du inaktiverar funktionen"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nyss"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Välj om appen ska använda den uppdaterade grafikdrivrutinen under utveckling"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Mobilens högtalare"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 657b54f..03e0c3a 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nenosiri jipya la hifadhi rudufu limewekwa"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nenosiri jipya na uthibitisho havioani"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Imeshindwa kuweka nenosiri la hifadhi rudufu"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Inapakia…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Maridadi (chaguomsingi)"</item>
     <item msgid="8446070607501413455">"Asili"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uliza kila wakati"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hadi utakapoizima"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sasa hivi"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chagua programu itakayotumia kiendeshaji cha michoro kilichosasishwa katika hatua ya kusanidi"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Spika ya simu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 59b42d8..0efd285 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"புதிய காப்புப் பிரதியின் கடவுச்சொல் அமைக்கப்பட்டது"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"புதிய கடவுச்சொல்லும், உறுதிப்படுத்தலுக்கான கடவுச்சொல்லும் பொருந்தவில்லை"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"காப்புப் பிரதி கடவுச்சொல்லை அமைப்பதில் தோல்வி"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"ஏற்றுகிறது…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"வைபிரன்ட் (இயல்பு)"</item>
     <item msgid="8446070607501413455">"இயற்கை வண்ணம்"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ஒவ்வொரு முறையும் கேள்"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ஆஃப் செய்யும் வரை"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"சற்றுமுன்"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"உருவாக்கத்திலுள்ள புதுப்பிக்கப்பட்ட கிராஃபிக்ஸ் டிரைவரைப் பயன்படுத்த ஆப்ஸைத் தேர்ந்தெடுக்கவும்"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"மொபைல் ஸ்பீக்கர்"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index af233da..2caa2ef 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"కొత్త బ్యాకప్ పాస్‌వర్డ్‌ను సెట్ చేసారు"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"కొత్త పాస్‌వర్డ్ మరియు నిర్ధారణ సరిపోలడం లేదు"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"బ్యాకప్ పాస్‌వర్డ్‌ను సెట్ చేయడంలో వైఫల్యం"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"లోడ్ చేస్తోంది…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"సచేతనం (డిఫాల్ట్)"</item>
     <item msgid="8446070607501413455">"సహజం"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ప్రతిసారి అడుగు"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"మీరు ఆఫ్‌ చేసే వరకు"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ఇప్పుడే"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"అభివృద్దిలో అప్‌డేట్‌ చేసిన గ్రాఫిక్ డ్రైవర్‌ను ఉపయోగించడానికి యాప్‌ని ప్రారంభించండి"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ఫోన్ స్పీకర్"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 636c3f2..03bf354 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"ตั้งรหัสผ่านสำหรับการสำรองข้อมูลใหม่แล้ว"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"รหัสผ่านใหม่และการพิมพ์ยืนยันไม่ตรงกัน"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ไม่สามารถตั้งรหัสผ่านสำหรับการสำรองข้อมูล"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"กำลังโหลด…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"สด (ค่าเริ่มต้น)"</item>
     <item msgid="8446070607501413455">"ธรรมชาติ"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ถามทุกครั้ง"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"จนกว่าคุณจะปิด"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"เมื่อสักครู่"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"เลือกใช้แอปเพื่อใช้ไดรเวอร์กราฟิกที่อัปเดตในการพัฒนา"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ลำโพงโทรศัพท์"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 1bcc36a..3f5cca2 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Naitakda ang bagong backup na password"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Hindi tugma ang password at kumpirmasyon"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nabigo sa pagtatakda ng backup na password"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Naglo-load…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Vibrant (default)"</item>
     <item msgid="8446070607501413455">"Natural"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Magtanong palagi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hanggang sa i-off mo"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ngayon lang"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App sa pag-opt in para magamit ang na-update na graphics driver na ginagawa"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ng telepono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index ff669f6..b0a782a 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Yeni yedekleme şifresi ayarlandı"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Yeni şifre ve onayı eşleşmiyor."</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Yedekleme şifresi ayarlanamadı"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Yükleniyor…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Canlı (varsayılan)"</item>
     <item msgid="8446070607501413455">"Doğal"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Her zaman sor"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Siz kapatana kadar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az önce"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Güncellenmiş grafik sürücüsünü geliştirme ortamında kullanmak için uygulamayı kaydedin"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hoparlörü"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 2ded8c1..2b63573 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Новий пароль резервної копії встановлено"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Паролі не збігаються"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Не вдалося зберегти пароль"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Завантаження…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Насичений (за умовчанням)"</item>
     <item msgid="8446070607501413455">"Природний"</item>
@@ -451,6 +452,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Запитувати щоразу"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Доки ви не вимкнете"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Щойно"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Вибраний додаток, який використовуватиме оновлений графічний драйвер під час розробки"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Динамік телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 7dc4690..d7123a6 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"بیک اپ کا نیا پاس ورڈ سیٹ کر دیا گیا"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"نیا پاس ورڈ اور تصدیق مماثل نہیں ہے"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"بیک اپ پاس ورڈ ترتیب دینے میں ناکامی"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"لوڈ ہو رہی ہے…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"پرجوش (ڈیفالٹ)"</item>
     <item msgid="8446070607501413455">"قدرتی"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ہر بار پوچھیں"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"یہاں تک کہ آپ آف کر دیں"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ابھی ابھی"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ڈیولپمنٹ میں اپ ڈیٹ کردہ گرافکس ڈرائیور کو استعمال کرنے کے لیے ایپ آپٹ ان کریں"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"فون اسپیکر"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 5f92c9a..51156d7 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Yangi zaxira paroli o‘rnatildi"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Parollar bir-biriga mos kelmadi"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Zaxira parolini o‘rnatib bo‘lmadi"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Yuklanmoqda…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Yorqin (birlamchi)"</item>
     <item msgid="8446070607501413455">"Tabiiy"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Har safar so‘ralsin"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Bekor qilinmaguncha"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Hozir"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ilova yangilangan grafik drayverdan (hali ishlov jarayonida) foydalanadi"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon karnayi"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index eb10fb1..13a9322 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Đã đặt mật khẩu sao lưu mới"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Mật khẩu mới và xác nhận không khớp"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Đặt mật khẩu sao lưu không thành công"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Đang tải…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Sống động (mặc định)"</item>
     <item msgid="8446070607501413455">"Tự nhiên"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Luôn hỏi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Cho đến khi bạn tắt"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Vừa xong"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chọn ứng dụng để sử dụng trình điều khiển đồ họa được cập nhật trong giai đoạn phát triển"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Loa điện thoại"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 7b14138..866c143 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"已设置了新的备份密码"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"新密码和确认密码不一致"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"设置备份密码失败"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"正在加载…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"鲜亮(默认)"</item>
     <item msgid="8446070607501413455">"自然"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都询问"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直到您将其关闭"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"刚刚"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"为应用启用更新后的显卡驱动,以在开发过程中使用"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手机扬声器"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 48b6959..f63ba3a 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"已設定新備份密碼"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"新密碼與確認密碼不符"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"無法設定備份密碼"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"正在載入…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"生動 (預設)"</item>
     <item msgid="8446070607501413455">"自然"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直至您關閉為止"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"請選取應用程式,以在開發階段使用更新的顯示卡驅動程式"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 684569b..93592a79 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"已設定新備份密碼"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"新密碼與確認密碼不符。"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"無法設定備份密碼"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"載入中…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"鮮活 (預設)"</item>
     <item msgid="8446070607501413455">"自然"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直到你關閉為止"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"選取要在開發階段使用最新版繪圖驅動程式的應用程式"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index d107a5a..dd2f3d8 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -330,6 +330,7 @@
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Iphasiwedi entsha eyisipele isethiwe"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Iphasiwedi entsha nokuqinisekisa akufani"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Ukungaphumeleli kokusetha iphasiwedi eyisipele"</string>
+    <string name="loading_injected_setting_summary" msgid="4095178591461231376">"Iyalayisha…"</string>
   <string-array name="color_mode_names">
     <item msgid="2425514299220523812">"Dlidlizela (okuzenzakalelayo)"</item>
     <item msgid="8446070607501413455">"Kwemvelo"</item>
@@ -449,6 +450,5 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Buza njalo"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Uze uvale isikrini"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Khona manje"</string>
-    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uhlelo lokusebenza lokukhetha ukungena olungasebenzisa idrayivu yamagrafikhi ekuthuthukiseni"</string>
     <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Isipikha sefoni"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7dcc3ac..16bfcc9 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -99,8 +99,12 @@
     <string name="connected_via_network_scorer_default">Automatically connected via network rating provider</string>
     <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
     <string name="connected_via_passpoint">Connected via %1$s</string>
+    <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
+    <string name="ssid_by_passpoint_provider"><xliff:g id="ssid" example="Cafe Wifi">%1$s</xliff:g> by <xliff:g id="passpointProvider" example="Passpoint Provider">%2$s</xliff:g></string>
     <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] -->
     <string name="available_via_passpoint">Available via %1$s</string>
+    <!-- Status message of OSU Provider network when not connected. [CHAR LIMIT=NONE] -->
+    <string name="tap_to_set_up">Tap to set up</string>
     <!-- Package name for Settings app-->
     <string name="settings_package" translatable="false">com.android.settings</string>
     <!-- Package name for Certinstaller app-->
@@ -123,6 +127,79 @@
     <!-- Status message of Wi-Fi when an available network is a carrier network. [CHAR LIMIT=NONE] -->
     <string name="available_via_carrier">Available via %1$s</string>
 
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_AP_CONNECTION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_ap_connection">Connection failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_URL_INVALID. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_server_url_invalid">Invalid OSU server URL</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_CONNECTION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_server_connection">OSU server connection failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_VALIDATION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_server_validation">OSU server validation failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_service_provider_verification">Invalid OSU server certificate</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_ABORTED. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_provisioning_aborted">Provisioning aborted</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_NOT_AVAILABLE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_provisioning_not_available">Provisioning not available</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_INVALID_SERVER_URL. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_invalid_server_url">Invalid OSU server URL</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_COMMAND_TYPE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_unexpected_command_type">Unexpected command type</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_unexpected_soap_message_type">Unexpected SOAP message type</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SOAP_MESSAGE_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_soap_message_exchange">SOAP message exchange failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_START_REDIRECT_LISTENER. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_start_redirect_listener">Redirect listener failed to start</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_timed_out_redirect_listener">Timed out waiting for redirect</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_OSU_ACTIVITY_FOUND. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_osu_activity_found">No OSU activity found</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_unexpected_soap_message_status">Unexpected SOAP message status</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_PPS_MO. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_pps_mo">Failed to find PPS-MO</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_aaa_server_trust_root_node">Failed to find trust root node for AAA server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_remediation_server_trust_root_node">Failed to find trust root node for remediation server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_policy_server_trust_root_node">Failed to find trust root node for policy server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_retrieve_trust_root_certificates">Failed to retrieve trust root certificates</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_aaa_trust_root_certificate">Failed to find trust root certificate for AAA server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_add_passpoint_configuration">Failed to add PassPoint configuration</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_OSU_PROVIDER_NOT_FOUND. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_osu_provider_not_found">Failed to find an OSU provider</string>
+
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTING. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_ap_connecting">Connecting</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_ap_connected">Connected</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTING. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_server_connecting">Connecting to OSU server</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_VALIDATED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_server_validated">OSU server validated</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_server_connected">Connected to OSU server</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_INIT_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_init_soap_exchange">Initial SOAP exchange</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_waiting_for_redirect_response">Waiting for redirect response</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_REDIRECT_RESPONSE_RECEIVED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_redirect_response_received">Received redirect response</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SECOND_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_second_soap_exchange">Second SOAP exchange</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_THIRD_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_third_soap_exchange">Third SOAP exchange</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_retrieving_trust_root_certs">Retrieving trust root certificates</string>
+
+    <!-- Status message of OSU Provider on completing provisioning. [CHAR LIMIT=NONE] -->
+    <string name="osu_provisioning_complete">Provisioning complete</string>
+
     <!-- Speed label for very slow network speed -->
     <string name="speed_label_very_slow">Very Slow</string>
     <!-- Speed label for slow network speed -->
@@ -1137,9 +1214,6 @@
     <!-- The notice header of Third-party licenses. not translatable -->
     <string name="notice_header" translatable="false"></string>
 
-    <!-- UI debug setting: opt in to use updated graphics driver? [CHAR LIMIT=100] -->
-    <string name="gup_dev_opt_in_app_summary">Opt in app to use Game Update Package in developement</string>
-
     <!-- Name of the phone device [CHAR LIMIT=NONE] -->
     <string name="media_transfer_phone_device_name">Phone speaker</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 6abe76a..c751c39 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,5 +1,7 @@
 package com.android.settingslib;
 
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+
 import android.annotation.ColorInt;
 import android.content.Context;
 import android.content.Intent;
@@ -36,8 +38,8 @@
     private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
     private static final String NEW_MODE_KEY = "NEW_MODE";
     @VisibleForTesting
-    static final String STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY =
-            "ro.storage_manager.show_opt_in";
+    static final String STORAGE_MANAGER_ENABLED_PROPERTY =
+            "ro.storage_manager.enabled";
 
     private static Signature[] sSystemSignature;
     private static String sPermissionControllerPackageName;
@@ -371,8 +373,7 @@
     public static boolean isStorageManagerEnabled(Context context) {
         boolean isDefaultOn;
         try {
-            // Turn off by default if the opt-in was shown.
-            isDefaultOn = !SystemProperties.getBoolean(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, true);
+            isDefaultOn = SystemProperties.getBoolean(STORAGE_MANAGER_ENABLED_PROPERTY, false);
         } catch (Resources.NotFoundException e) {
             isDefaultOn = false;
         }
@@ -429,12 +430,14 @@
         // and do not support voice service, and on these SIM cards, we
         // want to show signal bars for data service as well as the "no
         // service" or "emergency calls only" text that indicates that voice
-        // is not available.
+        // is not available. Note that we ignore the IWLAN service state
+        // because that state indicates the use of VoWIFI and not cell service
         int state = serviceState.getState();
         int dataState = serviceState.getDataRegState();
         if (state == ServiceState.STATE_OUT_OF_SERVICE
                 || state == ServiceState.STATE_EMERGENCY_ONLY) {
-            if (dataState == ServiceState.STATE_IN_SERVICE) {
+            if (dataState == ServiceState.STATE_IN_SERVICE
+                    && serviceState.getDataNetworkType() != RIL_RADIO_TECHNOLOGY_IWLAN) {
                 return ServiceState.STATE_IN_SERVICE;
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 58feef5..24d7011 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -35,7 +35,6 @@
 
 public class A2dpProfile implements LocalBluetoothProfile {
     private static final String TAG = "A2dpProfile";
-    private static boolean V = false;
 
     private Context mContext;
 
@@ -60,7 +59,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothA2dp) proxy;
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -79,7 +77,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -302,7 +299,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 988062d..873dd1a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -55,7 +55,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothA2dpSink) proxy;
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -74,7 +73,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 62507f5..6b6df9b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHeadset) proxy;
             // We just bound to the service, so refresh the UI for any connected HFP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG,"Bluetooth service disconnected");
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index adb5ab3..577d98d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -51,7 +51,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHearingAid) proxy;
             // We just bound to the service, so refresh the UI for any connected HearingAid devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -77,7 +76,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -234,7 +232,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEARING_AID,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4879144..c6bb2b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -59,7 +59,6 @@
 
         @Override
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothHeadsetClient) proxy;
             // We just bound to the service, so refresh the UI for any connected HFP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
 
         @Override
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 61e5b6b..4dc050c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected :-), profile:" + profile);
             mService = (BluetoothHidDevice) proxy;
             // We just bound to the service, so refresh the UI for any connected HID devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -78,7 +77,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 75d16db..ca840d9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -33,7 +33,6 @@
  */
 public class HidProfile implements LocalBluetoothProfile {
     private static final String TAG = "HidProfile";
-    private static boolean V = true;
 
     private BluetoothHidHost mService;
     private boolean mIsProfileReady;
@@ -51,7 +50,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHidHost) proxy;
             // We just bound to the service, so refresh the UI for any connected HID devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -70,7 +68,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -186,7 +183,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_HOST,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 1e22f44..6acdcac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -59,7 +59,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothMapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +80,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 7582024..28975d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothMap) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 7b81162..2d0a090 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -34,7 +34,6 @@
  */
 public class PanProfile implements LocalBluetoothProfile {
     private static final String TAG = "PanProfile";
-    private static boolean V = true;
 
     private BluetoothPan mService;
     private boolean mIsProfileReady;
@@ -53,13 +52,11 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothPan) proxy;
             mIsProfileReady=true;
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -173,7 +170,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.PAN, mService);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 1f15601..4672393 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -55,7 +55,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothPbapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected PBAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -74,7 +73,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index adef0841..1b3c453 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -33,7 +33,6 @@
  */
 public class PbapServerProfile implements LocalBluetoothProfile {
     private static final String TAG = "PbapServerProfile";
-    private static boolean V = true;
 
     private BluetoothPbap mService;
     private boolean mIsProfileReady;
@@ -56,13 +55,11 @@
             implements BluetoothPbap.ServiceListener {
 
         public void onServiceConnected(BluetoothPbap proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothPbap) proxy;
             mIsProfileReady=true;
         }
 
         public void onServiceDisconnected() {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -142,7 +139,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 mService.close();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index b4acc48..ea2ebde 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -57,7 +57,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothSap) proxy;
             // We just bound to the service, so refresh the UI for any connected SAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -79,7 +78,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index aed02a2..8090169 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -41,10 +41,6 @@
     private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
     private long mVisibleTimestamp;
 
-    private VisibilityLoggerMixin() {
-        mMetricsCategory = METRICS_CATEGORY_UNKNOWN;
-    }
-
     public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) {
         mMetricsCategory = metricsCategory;
         mMetricsFeature = metricsFeature;
@@ -81,12 +77,4 @@
                 MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
                 MetricsProto.MetricsEvent.VIEW_UNKNOWN);
     }
-
-    /** Returns elapsed time since onResume() */
-    public long elapsedTimeSinceVisible() {
-        if (mVisibleTimestamp == 0) {
-            return 0;
-        }
-        return SystemClock.elapsedRealtime() - mVisibleTimestamp;
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 12b8efb..4ab9a9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -55,6 +55,12 @@
             "com.android.settings.category.ia.privacy";
     public static final String CATEGORY_ENTERPRISE_PRIVACY =
             "com.android.settings.category.ia.enterprise_privacy";
+    public static final String CATEGORY_ABOUT_LEGAL =
+            "com.android.settings.category.ia.about_legal";
+    public static final String CATEGORY_MY_DEVICE_INFO =
+            "com.android.settings.category.ia.my_device_info";
+    public static final String CATEGORY_BATTERY_SAVER_SETTINGS =
+            "com.android.settings.category.ia.battery_saver_settings";
 
     public static final Map<String, String> KEY_COMPAT_MAP;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 4f81daf..27dc628 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -41,7 +41,9 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.ProvisioningCallback;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -182,6 +184,10 @@
 
     public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE;
 
+    public static final String KEY_PREFIX_AP = "AP:";
+    public static final String KEY_PREFIX_FQDN = "FQDN:";
+    public static final String KEY_PREFIX_OSU = "OSU:";
+
     private final Context mContext;
 
     private String ssid;
@@ -204,9 +210,6 @@
     @Speed private int mSpeed = Speed.NONE;
     private boolean mIsScoredNetworkMetered = false;
 
-    // used to co-relate internal vs returned accesspoint.
-    int mId;
-
     /**
      * Information associated with the {@link PasspointConfiguration}.  Only maintaining
      * the relevant info to preserve spaces.
@@ -215,6 +218,13 @@
     private String mProviderFriendlyName;
 
     private boolean mIsCarrierAp = false;
+
+    private OsuProvider mOsuProvider;
+
+    private String mOsuStatus;
+    private String mOsuFailure;
+    private boolean mOsuProvisioningComplete = false;
+
     /**
      * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
      */
@@ -280,14 +290,18 @@
         // Calculate required fields
         updateKey();
         updateRssi();
-
-        mId = sLastId.incrementAndGet();
     }
 
+    /**
+     * Creates an AccessPoint with only a WifiConfiguration. This is used for the saved networks
+     * page.
+     *
+     * Passpoint Credential AccessPoints should be created with this.
+     * Make sure to call setScanResults after constructing with this.
+     */
     public AccessPoint(Context context, WifiConfiguration config) {
         mContext = context;
         loadConfig(config);
-        mId = sLastId.incrementAndGet();
     }
 
     /**
@@ -298,7 +312,17 @@
         mContext = context;
         mFqdn = config.getHomeSp().getFqdn();
         mProviderFriendlyName = config.getHomeSp().getFriendlyName();
-        mId = sLastId.incrementAndGet();
+    }
+
+    /**
+     * Initialize an AccessPoint object for a Passpoint OSU Provider.
+     * Make sure to call setScanResults after constructing with this.
+     */
+    public AccessPoint(Context context, OsuProvider provider) {
+        mContext = context;
+        mOsuProvider = provider;
+        ssid = provider.getFriendlyName();
+        updateKey();
     }
 
     AccessPoint(Context context, Collection<ScanResult> results) {
@@ -324,33 +348,27 @@
         mIsCarrierAp = firstResult.isCarrierAp;
         mCarrierApEapType = firstResult.carrierApEapType;
         mCarrierName = firstResult.carrierName;
-
-        mId = sLastId.incrementAndGet();
     }
 
     @VisibleForTesting void loadConfig(WifiConfiguration config) {
         ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
         bssid = config.BSSID;
         security = getSecurity(config);
-        updateKey();
         networkId = config.networkId;
         mConfig = config;
+        updateKey();
     }
 
     /** Updates {@link #mKey} and should only called upon object creation/initialization. */
     private void updateKey() {
         // TODO(sghuman): Consolidate Key logic on ScanResultMatchInfo
-
-        StringBuilder builder = new StringBuilder();
-
-        if (TextUtils.isEmpty(getSsidStr())) {
-            builder.append(getBssid());
-        } else {
-            builder.append(getSsidStr());
+        if (isPasspoint()) {
+            mKey = getKey(mConfig);
+        } else if (isOsuProvider()) {
+            mKey = getKey(mOsuProvider);
+        } else { // Non-Passpoint AP
+            mKey = getKey(getSsidStr(), getBssid(), getSecurity());
         }
-
-        builder.append(',').append(getSecurity());
-        mKey = builder.toString();
     }
 
     /**
@@ -394,8 +412,8 @@
             return difference;
         }
 
-        // Sort by ssid.
-        difference = getSsidStr().compareToIgnoreCase(other.getSsidStr());
+        // Sort by title.
+        difference = getTitle().compareToIgnoreCase(other.getTitle());
         if (difference != 0) {
             return difference;
         }
@@ -591,28 +609,46 @@
     }
 
     public static String getKey(ScanResult result) {
-        StringBuilder builder = new StringBuilder();
-
-        if (TextUtils.isEmpty(result.SSID)) {
-            builder.append(result.BSSID);
-        } else {
-            builder.append(result.SSID);
-        }
-
-        builder.append(',').append(getSecurity(result));
-        return builder.toString();
+        return getKey(result.SSID, result.BSSID, getSecurity(result));
     }
 
+    /**
+     * Returns the AccessPoint key for a WifiConfiguration.
+     * This will return a special Passpoint key if the config is for Passpoint.
+     */
     public static String getKey(WifiConfiguration config) {
-        StringBuilder builder = new StringBuilder();
-
-        if (TextUtils.isEmpty(config.SSID)) {
-            builder.append(config.BSSID);
+        if (config.isPasspoint()) {
+            return new StringBuilder()
+                    .append(KEY_PREFIX_FQDN)
+                    .append(config.FQDN).toString();
         } else {
-            builder.append(removeDoubleQuotes(config.SSID));
+            return getKey(config.SSID, config.BSSID, getSecurity(config));
         }
+    }
 
-        builder.append(',').append(getSecurity(config));
+    /**
+     * Returns the AccessPoint key corresponding to the OsuProvider.
+     */
+    public static String getKey(OsuProvider provider) {
+        return new StringBuilder()
+                .append(KEY_PREFIX_OSU)
+                .append(provider.getFriendlyName())
+                .append(',')
+                .append(provider.getServerUri()).toString();
+    }
+
+    /**
+     * Returns the AccessPoint key for a normal non-Passpoint network by ssid/bssid and security.
+     */
+    private static String getKey(String ssid, String bssid, int security) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(KEY_PREFIX_AP);
+        if (TextUtils.isEmpty(ssid)) {
+            builder.append(bssid);
+        } else {
+            builder.append(ssid);
+        }
+        builder.append(',').append(security);
         return builder.toString();
     }
 
@@ -621,9 +657,10 @@
     }
 
     public boolean matches(WifiConfiguration config) {
-        if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
-            return ssid.equals(removeDoubleQuotes(config.SSID)) && config.FQDN.equals(mConfig.FQDN);
+        if (config.isPasspoint()) {
+            return (isPasspoint() && config.FQDN.equals(mConfig.FQDN));
         } else {
+            // Normal non-Passpoint network
             return ssid.equals(removeDoubleQuotes(config.SSID))
                     && security == getSecurity(config)
                     && (mConfig == null || mConfig.shared == config.shared);
@@ -828,86 +865,111 @@
         return "";
     }
 
+    /**
+     * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title.
+     */
+    public String getTitle() {
+        if (isPasspoint()) {
+            return mConfig.providerFriendlyName;
+        } else if (isOsuProvider()) {
+            return mOsuProvider.getFriendlyName();
+        } else {
+            return getSsidStr();
+        }
+    }
+
     public String getSummary() {
-        return getSettingsSummary(mConfig);
+        return getSettingsSummary();
     }
 
     public String getSettingsSummary() {
-        return getSettingsSummary(mConfig);
-    }
-
-    private String getSettingsSummary(WifiConfiguration config) {
         // Update to new summary
         StringBuilder summary = new StringBuilder();
 
-        if (isActive() && config != null && config.isPasspoint()) {
-            // This is the active connection on passpoint
-            summary.append(getSummary(mContext, getDetailedState(),
-                    false, config.providerFriendlyName));
-        } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED
-                && mIsCarrierAp) {
-            summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName));
-        } else if (isActive()) {
-            // This is the active connection on non-passpoint network
-            summary.append(getSummary(mContext, getDetailedState(),
-                    mInfo != null && mInfo.isEphemeral()));
-        } else if (config != null && config.isPasspoint()
-                && config.getNetworkSelectionStatus().isNetworkEnabled()) {
-            String format = mContext.getString(R.string.available_via_passpoint);
-            summary.append(String.format(format, config.providerFriendlyName));
-        } else if (config != null && config.hasNoInternetAccess()) {
-            int messageID = config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
-                    ? R.string.wifi_no_internet_no_reconnect
-                    : R.string.wifi_no_internet;
-            summary.append(mContext.getString(messageID));
-        } else if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) {
-            WifiConfiguration.NetworkSelectionStatus networkStatus =
-                    config.getNetworkSelectionStatus();
-            switch (networkStatus.getNetworkSelectionDisableReason()) {
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
-                    summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
-                    summary.append(mContext.getString(R.string.wifi_check_password_try_again));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
-                    summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
-                    summary.append(mContext.getString(R.string.wifi_disabled_generic));
-                    break;
+        if (isOsuProvider()) {
+            if (mOsuProvisioningComplete) {
+                summary.append(mContext.getString(R.string.osu_provisioning_complete));
+            } else if (mOsuFailure != null) {
+                summary.append(mOsuFailure);
+            } else if (mOsuStatus != null) {
+                summary.append(mOsuStatus);
+            } else {
+                summary.append(mContext.getString(R.string.tap_to_set_up));
             }
-        } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) {
-            summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider));
-        } else if (mIsCarrierAp) {
-            summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName));
-        } else if (!isReachable()) { // Wifi out of range
-            summary.append(mContext.getString(R.string.wifi_not_in_range));
-        } else { // In range, not disabled.
-            if (config != null) { // Is saved network
-                // Last attempt to connect to this failed. Show reason why
-                switch (config.recentFailure.getAssociationStatus()) {
-                    case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
-                        summary.append(mContext.getString(
-                                R.string.wifi_ap_unable_to_handle_new_sta));
+        } else if (isActive()) {
+            if (isPasspoint()) {
+                // This is the active connection on passpoint
+                summary.append(getSummary(mContext, ssid, getDetailedState(),
+                        false, mConfig.providerFriendlyName));
+            } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED
+                    && mIsCarrierAp) {
+                // This is the active connection on a carrier AP
+                summary.append(String.format(mContext.getString(R.string.connected_via_carrier),
+                        mCarrierName));
+            } else {
+                // This is the active connection on non-passpoint network
+                summary.append(getSummary(mContext, getDetailedState(),
+                        mInfo != null && mInfo.isEphemeral()));
+            }
+        } else { // not active
+            if (mConfig != null && mConfig.hasNoInternetAccess()) {
+                int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
+                        ? R.string.wifi_no_internet_no_reconnect
+                        : R.string.wifi_no_internet;
+                summary.append(mContext.getString(messageID));
+            } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
+                WifiConfiguration.NetworkSelectionStatus networkStatus =
+                        mConfig.getNetworkSelectionStatus();
+                switch (networkStatus.getNetworkSelectionDisableReason()) {
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
                         break;
-                    default:
-                        // "Saved"
-                        summary.append(mContext.getString(R.string.wifi_remembered));
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
+                        summary.append(mContext.getString(R.string.wifi_check_password_try_again));
                         break;
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
+                        break;
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
+                        summary.append(mContext.getString(R.string.wifi_disabled_generic));
+                        break;
+                }
+            } else if (mConfig != null && mConfig.getNetworkSelectionStatus().isNotRecommended()) {
+                summary.append(mContext.getString(
+                        R.string.wifi_disabled_by_recommendation_provider));
+            } else if (mIsCarrierAp) {
+                summary.append(String.format(mContext.getString(
+                        R.string.available_via_carrier), mCarrierName));
+            } else if (!isReachable()) { // Wifi out of range
+                summary.append(mContext.getString(R.string.wifi_not_in_range));
+            } else { // In range, not disabled.
+                if (mConfig != null) { // Is saved network
+                    // Last attempt to connect to this failed. Show reason why
+                    switch (mConfig.recentFailure.getAssociationStatus()) {
+                        case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+                            summary.append(mContext.getString(
+                                    R.string.wifi_ap_unable_to_handle_new_sta));
+                            break;
+                        default:
+                            // "Saved"
+                            summary.append(mContext.getString(R.string.wifi_remembered));
+                            break;
+                    }
                 }
             }
         }
 
+
+
         if (isVerboseLoggingEnabled()) {
-            summary.append(WifiUtils.buildLoggingSummary(this, config));
+            summary.append(WifiUtils.buildLoggingSummary(this, mConfig));
         }
 
-        if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) {
+        if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) {
             return mContext.getResources().getString(
                     R.string.preference_summary_default_combination,
-                    WifiUtils.getMeteredLabel(mContext, config),
+                    WifiUtils.getMeteredLabel(mContext, mConfig),
                     summary.toString());
         }
 
@@ -960,11 +1022,33 @@
     }
 
     /**
+     * Return true if this AccessPoint represents an OSU Provider.
+     */
+    public boolean isOsuProvider() {
+        return mOsuProvider != null;
+    }
+
+    /**
+     * Starts the OSU Provisioning flow.
+     */
+    public void startOsuProvisioning() {
+        mContext.getSystemService(WifiManager.class).startSubscriptionProvisioning(
+                mOsuProvider,
+                new AccessPointProvisioningCallback(),
+                ThreadUtils.getUiThreadHandler()
+        );
+    }
+
+    /**
      * Return whether the given {@link WifiInfo} is for this access point.
      * If the current AP does not have a network Id then the config is used to
      * match based on SSID and security.
      */
     private boolean isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info) {
+        if (info.isOsuAp()) {
+            return (mOsuStatus != null);
+        }
+
         if (isPasspoint() == false && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
             return networkId == info.getNetworkId();
         } else if (config != null) {
@@ -1048,18 +1132,21 @@
      */
     void setScanResults(Collection<ScanResult> scanResults) {
 
-        // Validate scan results are for current AP only
-        String key = getKey();
-        for (ScanResult result : scanResults) {
-            String scanResultKey = AccessPoint.getKey(result);
-            if (!mKey.equals(scanResultKey)) {
-                throw new IllegalArgumentException(
-                        String.format("ScanResult %s\nkey of %s did not match current AP key %s",
-                                      result, scanResultKey, key));
+        // Validate scan results are for current AP only by matching SSID/BSSID
+        // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
+        if (!isPasspoint() && !isOsuProvider()) {
+            String key = getKey();
+            for (ScanResult result : scanResults) {
+                String scanResultKey = AccessPoint.getKey(result);
+                if (!mKey.equals(scanResultKey)) {
+                    throw new IllegalArgumentException(
+                            String.format(
+                                    "ScanResult %s\nkey of %s did not match current AP key %s",
+                                    result, scanResultKey, key));
+                }
             }
         }
 
-
         int oldLevel = getLevel();
         mScanResults.clear();
         mScanResults.addAll(scanResults);
@@ -1100,7 +1187,17 @@
         }
     }
 
-    /** Attempt to update the AccessPoint and return true if an update occurred. */
+    /**
+     * Attempt to update the AccessPoint with the current connection info.
+     * This is used to set an AccessPoint to the active one if the connection info matches, or
+     * conversely to set an AccessPoint to inactive if the connection info does not match. The RSSI
+     * is also updated upon a match. Listeners will be notified if an update occurred.
+     *
+     * This is called in {@link WifiTracker#updateAccessPoints} as well as in callbacks for handling
+     * NETWORK_STATE_CHANGED_ACTION, RSSI_CHANGED_ACTION, and onCapabilitiesChanged in WifiTracker.
+     *
+     * Returns true if an update occurred.
+     */
     public boolean update(
             @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
 
@@ -1149,6 +1246,9 @@
 
     void update(@Nullable WifiConfiguration config) {
         mConfig = config;
+        if (mConfig != null) {
+            ssid = removeDoubleQuotes(mConfig.SSID);
+        }
         networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
         ThreadUtils.postOnMainThread(() -> {
             if (mAccessPointListener != null) {
@@ -1224,11 +1324,11 @@
 
     public static String getSummary(Context context, String ssid, DetailedState state,
             boolean isEphemeral, String passpointProvider) {
-        if (state == DetailedState.CONNECTED && ssid == null) {
-            if (TextUtils.isEmpty(passpointProvider) == false) {
+        if (state == DetailedState.CONNECTED) {
+            if (!TextUtils.isEmpty(passpointProvider)) {
                 // Special case for connected + passpoint networks.
-                String format = context.getString(R.string.connected_via_passpoint);
-                return String.format(format, passpointProvider);
+                String format = context.getString(R.string.ssid_by_passpoint_provider);
+                return String.format(format, ssid, passpointProvider);
             } else if (isEphemeral) {
                 // Special case for connected + ephemeral networks.
                 final NetworkScoreManager networkScoreManager = context.getSystemService(
@@ -1421,4 +1521,166 @@
     private static boolean isVerboseLoggingEnabled() {
         return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
     }
+
+    /**
+     * Callbacks relaying changes to the OSU provisioning status started in startOsuProvisioning().
+     *
+     * All methods are invoked on the Main Thread
+     */
+    private class AccessPointProvisioningCallback extends ProvisioningCallback {
+        // TODO: Remove logs and implement summary changing logic for these provisioning callbacks.
+        @Override
+        @MainThread public void onProvisioningFailure(int status) {
+            switch (status) {
+                case OSU_FAILURE_AP_CONNECTION:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_ap_connection);
+                    break;
+                case OSU_FAILURE_SERVER_URL_INVALID:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_server_url_invalid);
+                    break;
+                case OSU_FAILURE_SERVER_CONNECTION:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_server_connection);
+                    break;
+                case OSU_FAILURE_SERVER_VALIDATION:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_server_validation);
+                    break;
+                case OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_service_provider_verification);
+                    break;
+                case OSU_FAILURE_PROVISIONING_ABORTED:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_provisioning_aborted);
+                    break;
+                case OSU_FAILURE_PROVISIONING_NOT_AVAILABLE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_provisioning_not_available);
+                    break;
+                case OSU_FAILURE_INVALID_SERVER_URL:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_invalid_server_url);
+                    break;
+                case OSU_FAILURE_UNEXPECTED_COMMAND_TYPE:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_unexpected_command_type);
+                    break;
+                case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_unexpected_soap_message_type);
+                    break;
+                case OSU_FAILURE_SOAP_MESSAGE_EXCHANGE:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_soap_message_exchange);
+                    break;
+                case OSU_FAILURE_START_REDIRECT_LISTENER:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_start_redirect_listener);
+                    break;
+                case OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_timed_out_redirect_listener);
+                    break;
+                case OSU_FAILURE_NO_OSU_ACTIVITY_FOUND:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_no_osu_activity_found);
+                    break;
+                case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_unexpected_soap_message_status);
+                    break;
+                case OSU_FAILURE_NO_PPS_MO:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_pps_mo);
+                    break;
+                case OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_aaa_server_trust_root_node);
+                    break;
+                case OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_remediation_server_trust_root_node);
+                    break;
+                case OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_policy_server_trust_root_node);
+                    break;
+                case OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_retrieve_trust_root_certificates);
+                    break;
+                case OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_aaa_trust_root_certificate);
+                    break;
+                case OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_add_passpoint_configuration);
+                    break;
+                case OSU_FAILURE_OSU_PROVIDER_NOT_FOUND:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_osu_provider_not_found);
+                    break;
+            }
+            mOsuStatus = null;
+            mOsuProvisioningComplete = false;
+            ThreadUtils.postOnMainThread(() -> {
+                if (mAccessPointListener != null) {
+                    mAccessPointListener.onAccessPointChanged(AccessPoint.this);
+                }
+            });
+        }
+
+        @Override
+        @MainThread public void onProvisioningStatus(int status) {
+            switch (status) {
+                case OSU_STATUS_AP_CONNECTING:
+                    mOsuStatus = mContext.getString(R.string.osu_status_ap_connecting);
+                    break;
+                case OSU_STATUS_AP_CONNECTED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_ap_connected);
+                    break;
+                case OSU_STATUS_SERVER_CONNECTING:
+                    mOsuStatus = mContext.getString(R.string.osu_status_server_connecting);
+                    break;
+                case OSU_STATUS_SERVER_VALIDATED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_server_validated);
+                    break;
+                case OSU_STATUS_SERVER_CONNECTED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_server_connected);
+                    break;
+                case OSU_STATUS_INIT_SOAP_EXCHANGE:
+                    mOsuStatus = mContext.getString(R.string.osu_status_init_soap_exchange);
+                    break;
+                case OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE:
+                    mOsuStatus = mContext.getString(
+                            R.string.osu_status_waiting_for_redirect_response);
+                    break;
+                case OSU_STATUS_REDIRECT_RESPONSE_RECEIVED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_redirect_response_received);
+                    break;
+                case OSU_STATUS_SECOND_SOAP_EXCHANGE:
+                    mOsuStatus = mContext.getString(R.string.osu_status_second_soap_exchange);
+                    break;
+                case OSU_STATUS_THIRD_SOAP_EXCHANGE:
+                    mOsuStatus = mContext.getString(R.string.osu_status_third_soap_exchange);
+                    break;
+                case OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS:
+                    mOsuStatus = mContext.getString(
+                            R.string.osu_status_retrieving_trust_root_certs);
+                    break;
+            }
+            mOsuFailure = null;
+            mOsuProvisioningComplete = false;
+            ThreadUtils.postOnMainThread(() -> {
+                if (mAccessPointListener != null) {
+                    mAccessPointListener.onAccessPointChanged(AccessPoint.this);
+                }
+            });
+        }
+
+        @Override
+        @MainThread public void onProvisioningComplete() {
+            mOsuProvisioningComplete = true;
+            mOsuFailure = null;
+            mOsuStatus = null;
+            ThreadUtils.postOnMainThread(() -> {
+                if (mAccessPointListener != null) {
+                    mAccessPointListener.onAccessPointChanged(AccessPoint.this);
+                }
+            });
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index db364a3..1fa7083 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -266,7 +266,7 @@
         if (savedNetworks) {
             preference.setTitle(ap.getConfigName());
         } else {
-            preference.setTitle(ap.getSsidStr());
+            preference.setTitle(ap.getTitle());
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index e47ca32..b9a5f23 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -35,6 +35,7 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.WifiNetworkScoreCache.CacheListener;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
@@ -45,6 +46,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.widget.Toast;
 
 import androidx.annotation.GuardedBy;
@@ -66,6 +68,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -573,9 +576,72 @@
                 accessPoints.add(accessPoint);
             }
 
+            List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
+
+            // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN.
+            List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans =
+                    mWifiManager.getAllMatchingWifiConfigs(cachedScanResults);
+            Set<String> seenFQDNs = new ArraySet<>();
+            for (Pair<WifiConfiguration,
+                    Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) {
+                WifiConfiguration config = pairing.first;
+
+                List<ScanResult> scanResults = new ArrayList<>();
+
+                List<ScanResult> homeScans =
+                        pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
+                List<ScanResult> roamingScans =
+                        pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK);
+
+                if (homeScans == null) {
+                    homeScans = new ArrayList<>();
+                }
+                if (roamingScans == null) {
+                    roamingScans = new ArrayList<>();
+                }
+
+                // TODO(b/118705403): Differentiate home network vs roaming network for summary info
+                if (!homeScans.isEmpty()) {
+                    scanResults.addAll(homeScans);
+                } else {
+                    scanResults.addAll(roamingScans);
+                }
+
+                if (seenFQDNs.add(config.FQDN)) {
+                    int bestRssi = Integer.MIN_VALUE;
+                    for (ScanResult result : scanResults) {
+                        if (result.level >= bestRssi) {
+                            bestRssi = result.level;
+                            config.SSID = AccessPoint.convertToQuotedString(result.SSID);
+                        }
+                    }
+
+                    AccessPoint accessPoint =
+                            getCachedOrCreatePasspoint(scanResults, cachedAccessPoints, config);
+                    accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+                    accessPoints.add(accessPoint);
+                }
+            }
+
+            // Add Passpoint OSU Provider AccessPoints
+            Map<OsuProvider, List<ScanResult>> providersAndScans =
+                    mWifiManager.getMatchingOsuProviders(cachedScanResults);
+            Set<OsuProvider> alreadyProvisioned = mWifiManager
+                    .getMatchingPasspointConfigsForOsuProviders(
+                            providersAndScans.keySet()).keySet();
+            for (OsuProvider provider : providersAndScans.keySet()) {
+                if (!alreadyProvisioned.contains(provider)) {
+                    AccessPoint accessPointOsu =
+                            getCachedOrCreateOsu(providersAndScans.get(provider),
+                                    cachedAccessPoints, provider);
+                    accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+                    accessPoints.add(accessPointOsu);
+                }
+            }
+
+
             // If there were no scan results, create an AP for the currently connected network (if
             // it exists).
-            // TODO(b/b/73076869): Add support for passpoint (ephemeral) networks
             if (accessPoints.isEmpty() && connectionConfig != null) {
                 AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
                 activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
@@ -623,16 +689,49 @@
     AccessPoint getCachedOrCreate(
             List<ScanResult> scanResults,
             List<AccessPoint> cache) {
-        final int N = cache.size();
-        for (int i = 0; i < N; i++) {
-            if (cache.get(i).getKey().equals(AccessPoint.getKey(scanResults.get(0)))) {
-                AccessPoint ret = cache.remove(i);
-                ret.setScanResults(scanResults);
-                return ret;
+        AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0)));
+        if (accessPoint == null) {
+            accessPoint = new AccessPoint(mContext, scanResults);
+        } else {
+            accessPoint.setScanResults(scanResults);
+        }
+        return accessPoint;
+    }
+
+    private AccessPoint getCachedOrCreatePasspoint(
+            List<ScanResult> scanResults,
+            List<AccessPoint> cache,
+            WifiConfiguration config) {
+        AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(config));
+        if (accessPoint == null) {
+            accessPoint = new AccessPoint(mContext, config);
+        }
+        accessPoint.setScanResults(scanResults);
+        return accessPoint;
+    }
+
+    private AccessPoint getCachedOrCreateOsu(
+            List<ScanResult> scanResults,
+            List<AccessPoint> cache,
+            OsuProvider provider) {
+        AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(provider));
+        if (accessPoint == null) {
+            accessPoint = new AccessPoint(mContext, provider);
+        }
+        accessPoint.setScanResults(scanResults);
+        return accessPoint;
+    }
+
+    private AccessPoint getCachedByKey(List<AccessPoint> cache, String key) {
+        ListIterator<AccessPoint> lit = cache.listIterator();
+        while (lit.hasNext()) {
+            AccessPoint currentAccessPoint = lit.next();
+            if (currentAccessPoint.getKey().equals(key)) {
+                lit.remove();
+                return currentAccessPoint;
             }
         }
-        final AccessPoint accessPoint = new AccessPoint(mContext, scanResults);
-        return accessPoint;
+        return null;
     }
 
     private void updateNetworkInfo(NetworkInfo networkInfo) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 594d767..92ebe44 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -17,7 +17,7 @@
 
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
-import static com.android.settingslib.Utils.STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY;
+import static com.android.settingslib.Utils.STORAGE_MANAGER_ENABLED_PROPERTY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -159,7 +159,7 @@
 
     @Test
     public void testIsStorageManagerEnabled_UsesSystemProperties() {
-        SystemProperties.set(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, "false");
+        SystemProperties.set(STORAGE_MANAGER_ENABLED_PROPERTY, "true");
         assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue();
     }
 
@@ -247,6 +247,15 @@
     }
 
     @Test
+    public void isInService_voiceOutOfServiceDataInServiceOnIwLan_returnFalse() {
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState.getDataNetworkType())
+                .thenReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+        when(mServiceState.getDataRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        assertThat(Utils.isInService(mServiceState)).isFalse();
+    }
+
+    @Test
     public void isInService_voiceOutOfServiceDataOutOfService_returnFalse() {
         when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
         when(mServiceState.getDataRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index e853399..ef90dc9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -28,8 +28,8 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteStatement;
-import android.media.AudioSystem;
 import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.net.ConnectivityManager;
 import android.os.Build;
 import android.os.Environment;
@@ -1104,9 +1104,7 @@
         }
 
         if (upgradeVersion == 77) {
-            // Introduce "vibrate when ringing" setting
-            loadVibrateWhenRingingSetting(db);
-
+            // "vibrate when ringing" setting moved to SettingsProvider version 168
             upgradeVersion = 78;
         }
 
@@ -2223,8 +2221,6 @@
         } finally {
             if (stmt != null) stmt.close();
         }
-
-        loadVibrateWhenRingingSetting(db);
     }
 
     private void loadVibrateSetting(SQLiteDatabase db, boolean deleteOld) {
@@ -2250,24 +2246,6 @@
         }
     }
 
-    private void loadVibrateWhenRingingSetting(SQLiteDatabase db) {
-        // The default should be off. VIBRATE_SETTING_ONLY_SILENT should also be ignored here.
-        // Phone app should separately check whether AudioManager#getRingerMode() returns
-        // RINGER_MODE_VIBRATE, with which the device should vibrate anyway.
-        int vibrateSetting = getIntValueFromSystem(db, Settings.System.VIBRATE_ON,
-                AudioManager.VIBRATE_SETTING_OFF);
-        boolean vibrateWhenRinging = ((vibrateSetting & 3) == AudioManager.VIBRATE_SETTING_ON);
-
-        SQLiteStatement stmt = null;
-        try {
-            stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
-                    + " VALUES(?,?);");
-            loadSetting(stmt, Settings.System.VIBRATE_WHEN_RINGING, vibrateWhenRinging ? 1 : 0);
-        } finally {
-            if (stmt != null) stmt.close();
-        }
-    }
-
     private void loadSettings(SQLiteDatabase db) {
         loadSystemSettings(db);
         loadSecureSettings(db);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 526efcb..e843eb4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -705,11 +705,17 @@
                 Settings.Global.GPU_DEBUG_LAYERS_GLES,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES);
         dumpSetting(s, p,
+                Settings.Global.GUP_DEV_ALL_APPS,
+                GlobalSettingsProto.Gpu.GUP_DEV_ALL_APPS);
+        dumpSetting(s, p,
                 Settings.Global.GUP_DEV_OPT_IN_APPS,
                 GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS);
         dumpSetting(s, p,
-                Settings.Global.GUP_BLACK_LIST,
-                GlobalSettingsProto.Gpu.GUP_BLACK_LIST);
+                Settings.Global.GUP_DEV_OPT_OUT_APPS,
+                GlobalSettingsProto.Gpu.GUP_DEV_OPT_OUT_APPS);
+        dumpSetting(s, p,
+                Settings.Global.GUP_BLACKLIST,
+                GlobalSettingsProto.Gpu.GUP_BLACKLIST);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
@@ -1554,6 +1560,10 @@
                 Settings.Global.ZRAM_ENABLED,
                 GlobalSettingsProto.ZRAM_ENABLED);
 
+        dumpSetting(s, p,
+                Global.APP_OPS_CONSTANTS,
+                GlobalSettingsProto.APP_OPS_CONSTANTS);
+
         p.end(token);
         // Please insert new settings using the same order as in GlobalSettingsProto.
 
@@ -1872,6 +1882,9 @@
         dumpSetting(s, p,
                 Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
                 SecureSettingsProto.Doze.PULSE_ON_DOUBLE_TAP);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
+                SecureSettingsProto.Doze.PULSE_ON_TAP);
         p.end(dozeToken);
 
         dumpSetting(s, p,
@@ -2356,6 +2369,14 @@
                 SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED);
         p.end(zenToken);
 
+        dumpSetting(s, p,
+                Settings.Secure.SKIP_GESTURE,
+                SecureSettingsProto.SKIP_GESTURE_ENABLED);
+
+        dumpSetting(s, p,
+                Settings.Secure.SILENCE_GESTURE,
+                SecureSettingsProto.SILENCE_GESTURE_ENABLED);
+
         // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index bce5593..5105ff4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -84,6 +84,8 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 
+import com.google.android.collect.Sets;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -957,12 +959,12 @@
         UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
         userManager.addUserRestrictionsListener((int userId, Bundle newRestrictions,
                 Bundle prevRestrictions) -> {
+            Set<String> changedRestrictions = getRestrictionDiff(prevRestrictions, newRestrictions);
             // We are changing the settings affected by restrictions to their current
             // value with a forced update to ensure that all cross profile dependencies
             // are taken into account. Also make sure the settings update to.. the same
             // value passes the security checks, so clear binder calling id.
-            if (newRestrictions.getBoolean(UserManager.DISALLOW_SHARE_LOCATION)
-                    != prevRestrictions.getBoolean(UserManager.DISALLOW_SHARE_LOCATION)) {
+            if (changedRestrictions.contains(UserManager.DISALLOW_SHARE_LOCATION)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
@@ -976,11 +978,8 @@
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            if (newRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
-                    != prevRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) ||
-                    newRestrictions.getBoolean(
-                            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)
-                    != prevRestrictions.getBoolean(
+            if (changedRestrictions.contains(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
+                    || changedRestrictions.contains(
                             UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
@@ -994,8 +993,7 @@
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            if (newRestrictions.getBoolean(UserManager.DISALLOW_DEBUGGING_FEATURES)
-                    != prevRestrictions.getBoolean(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
+            if (changedRestrictions.contains(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
@@ -1008,8 +1006,7 @@
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            if (newRestrictions.getBoolean(UserManager.ENSURE_VERIFY_APPS)
-                    != prevRestrictions.getBoolean(UserManager.ENSURE_VERIFY_APPS)) {
+            if (changedRestrictions.contains(UserManager.ENSURE_VERIFY_APPS)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
@@ -1028,8 +1025,7 @@
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            if (newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)
-                    != prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
+            if (changedRestrictions.contains(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
@@ -1046,13 +1042,27 @@
         });
     }
 
+    private static Set<String> getRestrictionDiff(Bundle prevRestrictions, Bundle newRestrictions) {
+        Set<String> restrictionNames = Sets.newArraySet();
+        restrictionNames.addAll(prevRestrictions.keySet());
+        restrictionNames.addAll(newRestrictions.keySet());
+        Set<String> diff = Sets.newArraySet();
+        for (String restrictionName : restrictionNames) {
+            if (prevRestrictions.getBoolean(restrictionName) != newRestrictions.getBoolean(
+                    restrictionName)) {
+                diff.add(restrictionName);
+            }
+        }
+        return diff;
+    }
+
     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());
+        // enforceReadPermission(READ_DEVICE_CONFIG);
 
         // Get the value.
         synchronized (mLock) {
@@ -1088,8 +1098,9 @@
 
     private boolean mutateConfigSetting(String name, String value, String prefix,
             boolean makeDefault, int operation, int mode) {
-        // TODO(b/117663715): check the new permission when it's added.
-        // enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+
+        // TODO(b/117663715): Ensure the caller can access the setting.
+        // enforceReadPermission(WRITE_DEVICE_CONFIG);
 
         // Perform the mutation.
         synchronized (mLock) {
diff --git a/packages/SettingsProvider/test/AndroidManifest.xml b/packages/SettingsProvider/test/AndroidManifest.xml
index 87a4f60..ebdf9b1 100644
--- a/packages/SettingsProvider/test/AndroidManifest.xml
+++ b/packages/SettingsProvider/test/AndroidManifest.xml
@@ -20,6 +20,8 @@
     <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
 
     <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
     <uses-permission android:name="android.permission.MANAGE_USERS"/>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index bff2c84..b903142 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -57,6 +57,8 @@
     <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
     <!-- Development tool permissions granted to the shell. -->
@@ -129,11 +131,14 @@
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+    <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
     <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
     <uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
     <uses-permission android:name="android.permission.MANAGE_CONTENT_CAPTURE" />
+    <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" />
+    <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.SET_TIME" />
@@ -162,6 +167,8 @@
     <uses-permission android:name="android.permission.SUSPEND_APPS" />
     <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
 
+    <uses-permission android:name="android.permission.MANAGE_APPOPS" />
+
     <application android:label="@string/app_label"
                  android:defaultToDeviceProtectedStorage="true"
                  android:directBootAware="true">
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 2530abc..2d7471d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -56,6 +56,7 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.annotation.MainThread;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.AlertDialog;
 import android.app.Notification;
@@ -799,6 +800,18 @@
             Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
             return;
         }
+        final int max = intent.getIntExtra(EXTRA_MAX, -1);
+        final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+        final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+        final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
+        onBugreportFinished(id, bugreportFile, screenshotFile, shareTitle, shareDescription, max);
+    }
+
+    /**
+     * Wraps up bugreport generation and triggers a notification to share the bugreport.
+     */
+    private void onBugreportFinished(int id, File bugreportFile, @Nullable File screenshotFile,
+        String shareTitle, String shareDescription, int max) {
         mInfoDialog.onBugreportFinished();
         BugreportInfo info = getInfo(id);
         if (info == null) {
@@ -809,22 +822,17 @@
         }
         info.renameScreenshots(mScreenshotsDir);
         info.bugreportFile = bugreportFile;
+        if (screenshotFile != null) {
+            info.addScreenshot(screenshotFile);
+        }
 
-        final int max = intent.getIntExtra(EXTRA_MAX, -1);
         if (max != -1) {
             MetricsLogger.histogram(this, "dumpstate_duration", max);
             info.max = max;
         }
 
-        final File screenshot = getFileExtra(intent, EXTRA_SCREENSHOT);
-        if (screenshot != null) {
-            info.addScreenshot(screenshot);
-        }
-
-        final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
         if (!TextUtils.isEmpty(shareTitle)) {
             info.title = shareTitle;
-            final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
             if (!TextUtils.isEmpty(shareDescription)) {
                 info.shareDescription= shareDescription;
             }
@@ -1944,6 +1952,22 @@
         }
 
         @Override
+        public void onProgress(int progress) throws RemoteException {
+            updateProgressInfo(progress, 100 /* progress is already a percentage; so max = 100 */);
+        }
+
+        @Override
+        public void onError(int errorCode) throws RemoteException {
+            // TODO(b/111441001): implement
+        }
+
+        @Override
+        public void onFinished(long durationMs, String title, String description)
+                throws RemoteException {
+            // TODO(b/111441001): implement
+        }
+
+        @Override
         public void onProgressUpdated(int progress) throws RemoteException {
             /*
              * Checks whether the progress changed in a way that should be displayed to the user:
@@ -1964,21 +1988,7 @@
             }
 
             if (newPercentage > oldPercentage) {
-                if (DEBUG) {
-                    if (progress != info.progress) {
-                        Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
-                                + ") from " + info.progress + " to " + progress);
-                    }
-                    if (max != info.max) {
-                        Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
-                                + ") from " + info.max + " to " + max);
-                    }
-                }
-                info.progress = progress;
-                info.max = max;
-                info.lastUpdate = System.currentTimeMillis();
-
-                updateProgress(info);
+                updateProgressInfo(progress, max);
             }
         }
 
@@ -2000,5 +2010,23 @@
         public void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("token: "); pw.println(token);
         }
+
+        private void updateProgressInfo(int progress, int max) {
+            if (DEBUG) {
+                if (progress != info.progress) {
+                    Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
+                            + ") from " + info.progress + " to " + progress);
+                }
+                if (max != info.max) {
+                    Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
+                            + ") from " + info.max + " to " + max);
+                }
+            }
+            info.progress = progress;
+            info.max = max;
+            info.lastUpdate = System.currentTimeMillis();
+
+            updateProgress(info);
+        }
     }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1c1a140..b4f2711 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
     <uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+    <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml
new file mode 100644
index 0000000..d3c3a51
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="200dp"
+    android:width="200dp"
+    android:viewportHeight="100"
+    android:viewportWidth="100">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M50.082,14.199m-13.985,0a13.985,13.985 0,1 1,27.97 0a13.985,13.985 0,1 1,-27.97 0"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml
new file mode 100644
index 0000000..a4417fb
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="200dp"
+    android:width="200dp"
+    android:viewportHeight="100"
+    android:viewportWidth="100" >
+    <path
+        android:fillColor="#000000"
+        android:pathData="M50.082,0.025L50.082,0.025A13.985,15.63 0,0 1,64.067 15.656L64.067,49.029A13.985,15.63 0,0 1,50.082 64.659L50.082,64.659A13.985,15.63 0,0 1,36.097 49.029L36.097,15.656A13.985,15.63 0,0 1,50.082 0.025z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml
new file mode 100644
index 0000000..6f7f398
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<com.android.keyguard.clock.ClockLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+  >
+  <include
+      android:id="@+id/digital_clock"
+      layout="@layout/text_clock"
+  />
+  <com.android.keyguard.clock.ImageClock
+      android:id="@+id/analog_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+  >
+    <ImageView
+        android:id="@+id/minute_hand"
+        android:layout_width="300dp"
+        android:layout_height="300dp"
+        android:src="@drawable/bubble_minute_hand"
+        android:tint="@color/bubbleMinuteHandColor"
+    />
+    <ImageView
+        android:id="@+id/hour_hand"
+        android:layout_width="300dp"
+        android:layout_height="300dp"
+        android:src="@drawable/bubble_hour_hand"
+        android:tint="@color/bubbleHourHandColor"
+    />
+  </com.android.keyguard.clock.ImageClock>
+</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
new file mode 100644
index 0000000..e88e2c9
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 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.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:layout_alignParentTop="true">
+  <include
+      android:id="@+id/lock_screen_clock"
+      layout="@layout/text_clock"
+  />
+</FrameLayout>
+
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index d52866f..463367b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -30,17 +30,9 @@
          android:layout_height="wrap_content"
          android:layout_gravity="center_horizontal"
          android:layout_alignParentTop="true">
-        <TextClock
+        <include
              android:id="@+id/default_clock_view"
-             android:layout_width="wrap_content"
-             android:layout_height="wrap_content"
-             android:layout_gravity="center_horizontal"
-             android:letterSpacing="0.03"
-             android:textColor="?attr/wallpaperTextColor"
-             android:singleLine="true"
-             style="@style/widget_big"
-             android:format12Hour="@string/keyguard_widget_12_hours_format"
-             android:format24Hour="@string/keyguard_widget_24_hours_format" />
+             layout="@layout/text_clock" />
     </FrameLayout>
     <include layout="@layout/keyguard_status_area"
         android:id="@+id/keyguard_status_area"
diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml
new file mode 100644
index 0000000..64b676f5
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<com.android.keyguard.clock.ClockLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+  >
+  <include
+      android:id="@+id/digital_clock"
+      layout="@layout/text_clock"
+  />
+  <com.android.keyguard.clock.StretchAnalogClock
+      android:id="@+id/analog_clock"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+  />
+</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml
new file mode 100644
index 0000000..b61ad9c
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 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.
+  -->
+<TextClock
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:letterSpacing="0.03"
+    android:textColor="?attr/wallpaperTextColor"
+    android:singleLine="true"
+    style="@style/widget_big"
+    android:format12Hour="@string/keyguard_widget_12_hours_format"
+    android:format24Hour="@string/keyguard_widget_24_hours_format"
+    android:elegantTextHeight="false"
+/>
diff --git a/packages/SystemUI/res-keyguard/layout/type_clock.xml b/packages/SystemUI/res-keyguard/layout/type_clock.xml
new file mode 100644
index 0000000..21c64e9
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/type_clock.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<com.android.keyguard.clock.ClockLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+  >
+  <com.android.keyguard.clock.TypographicClock
+      android:id="@+id/type_clock"
+      android:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+    >
+    <TextView
+        android:id="@+id/header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="50dp"
+        style="@style/widget_big"
+        android:textColor="@color/typeClockAccentColor"
+        android:text="@string/type_clock_header"
+        android:textSize="40dp"
+    />
+    <TextView
+        android:id="@+id/hour"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="50dp"
+        style="@style/widget_big"
+        android:textSize="40dp"
+    />
+    <TextView
+        android:id="@+id/minute"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="50dp"
+        style="@style/widget_big"
+        android:textSize="40dp"
+    />
+  </com.android.keyguard.clock.TypographicClock>
+</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/values/colors.xml b/packages/SystemUI/res-keyguard/values/colors.xml
new file mode 100644
index 0000000..74ee7ff
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+  <!-- Default color for hour hand of Bubble clock. -->
+  <color name="bubbleHourHandColor">#C97343</color>
+  <!-- Default color for minute hand of Bubble clock. -->
+  <color name="bubbleMinuteHandColor">#F5C983</color>
+  <!-- Accent color for Typographic clock. -->
+  <color name="typeClockAccentColor">#F5C983</color>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 9245c30..e2ba23e 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -47,8 +47,6 @@
     <dimen name="bottom_text_spacing_digital">0dp</dimen>
     <!-- Slice subtitle -->
     <dimen name="widget_label_font_size">16dp</dimen>
-    <!-- Slice offset when pulsing -->
-    <dimen name="widget_pulsing_bottom_padding">48dp</dimen>
     <!-- Clock without header -->
     <dimen name="widget_big_font_size">64dp</dimen>
     <!-- Clock with header -->
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 1d5aa6d..7432f9c 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -402,4 +402,87 @@
 number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
     </plurals>
 
+    <!-- Header for typographic clock face. [CHAR LIMIT=8] -->
+    <string name="type_clock_header">It\u2019s</string>
+
+    <!-- Hour displayed in words on the typographic clock face. [CHAR LIMIT=8] -->
+    <string-array name="type_clock_hours">
+        <item>Twelve</item>
+        <item>One</item>
+        <item>Two</item>
+        <item>Three</item>
+        <item>Four</item>
+        <item>Five</item>
+        <item>Six</item>
+        <item>Seven</item>
+        <item>Eight</item>
+        <item>Nine</item>
+        <item>Ten</item>
+        <item>Eleven</item>
+    </string-array>
+
+    <!-- Minutes displayed in words on the typographic clock face. [CHAR LIMIT=16] -->
+    <string-array name="type_clock_minutes">
+        <item>O\u2019Clock</item>
+        <item>O\u2019One</item>
+        <item>O\u2019Two</item>
+        <item>O\u2019Three</item>
+        <item>O\u2019Four</item>
+        <item>O\u2019Five</item>
+        <item>O\u2019Six</item>
+        <item>O\u2019Seven</item>
+        <item>O\u2019Eight</item>
+        <item>O\u2019Nine</item>
+        <item>Ten</item>
+        <item>Eleven</item>
+        <item>Twelve</item>
+        <item>Thirteen</item>
+        <item>Fourteen</item>
+        <item>Fifteen</item>
+        <item>Sixteen</item>
+        <item>Seventeen</item>
+        <item>Eighteen</item>
+        <item>Nineteen</item>
+        <item>Twenty</item>
+        <item>Twenty\nOne</item>
+        <item>Twenty\nTwo</item>
+        <item>Twenty\nThree</item>
+        <item>Twenty\nFour</item>
+        <item>Twenty\nFive</item>
+        <item>Twenty\nSix</item>
+        <item>Twenty\nSeven</item>
+        <item>Twenty\nEight</item>
+        <item>Twenty\nNine</item>
+        <item>Thirty</item>
+        <item>Thirty\nOne</item>
+        <item>Thirty\nTwo</item>
+        <item>Thirty\nThree</item>
+        <item>Thirty\nFour</item>
+        <item>Thirty\nFive</item>
+        <item>Thirty\nSix</item>
+        <item>Thirty\nSeven</item>
+        <item>Thirty\nEight</item>
+        <item>Thirty\nNine</item>
+        <item>Forty</item>
+        <item>Forty\nOne</item>
+        <item>Forty\nTwo</item>
+        <item>Forty\nThree</item>
+        <item>Forty\nFour</item>
+        <item>Forty\nFive</item>
+        <item>Forty\nSix</item>
+        <item>Forty\nSeven</item>
+        <item>Forty\nEight</item>
+        <item>Forty\nNine</item>
+        <item>Fifty</item>
+        <item>Fifty\nOne</item>
+        <item>Fifty\nTwo</item>
+        <item>Fifty\nThree</item>
+        <item>Fifty\nFour</item>
+        <item>Fifty\nFive</item>
+        <item>Fifty\nSix</item>
+        <item>Fifty\nSeven</item>
+        <item>Fifty\nEight</item>
+        <item>Fifty\nNine</item>
+    </string-array>
+
 </resources>
diff --git a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
index d041556..0c6d57d 100644
--- a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
+++ b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
@@ -18,7 +18,7 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="?android:attr/colorBackgroundFloating" />
-    <corners android:radius="1dp"
+    <corners
         android:topLeftRadius="@dimen/biometric_dialog_corner_size"
         android:topRightRadius="@dimen/biometric_dialog_corner_size"
         android:bottomLeftRadius="@dimen/biometric_dialog_corner_size"
diff --git a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
new file mode 100644
index 0000000..26bf981
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
@@ -0,0 +1,32 @@
+<?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
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="?android:attr/colorBackgroundFloating"/>
+            <corners
+                    android:topLeftRadius="@dimen/corner_size"
+                    android:topRightRadius="@dimen/corner_size"/>
+        </shape>
+    </item>
+    <item android:gravity="bottom">
+        <shape>
+            <size android:height="1dp"/>
+            <solid android:color="?android:attr/textColorSecondary" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index e52aa14..b9b1bb1 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -22,7 +22,7 @@
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         android:textColor="?android:attr/textColorPrimary"
         android:gravity="center_vertical|start"
         android:paddingStart="@dimen/battery_level_padding_start"
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index b2307e7..1aeb52c 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -20,11 +20,23 @@
     android:layout_width="match_parent"
     android:id="@+id/bubble_expanded_view">
 
-    <!-- TODO: header -->
-
     <View
         android:id="@+id/pointer_view"
         android:layout_width="@dimen/bubble_pointer_width"
         android:layout_height="@dimen/bubble_pointer_height"
     />
+
+    <TextView
+        android:id="@+id/bubble_content_header"
+        android:background="@drawable/bubble_expanded_header_bg"
+        android:textAppearance="@*android:style/TextAppearance.Material.Title"
+        android:textSize="18sp"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/bubble_expanded_header_height"
+        android:gravity="start|center_vertical"
+        android:singleLine="true"
+        android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding"
+        android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding"
+    />
+
 </com.android.systemui.bubbles.BubbleExpandedViewContainer>
diff --git a/packages/SystemUI/res/layout/bubble_view.xml b/packages/SystemUI/res/layout/bubble_view.xml
new file mode 100644
index 0000000..204408cd
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_view.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<com.android.systemui.bubbles.BubbleView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:id="@+id/bubble_view">
+
+    <com.android.systemui.bubbles.BadgedImageView
+        android:id="@+id/bubble_image"
+        android:layout_width="@dimen/bubble_size"
+        android:layout_height="@dimen/bubble_size"
+        android:padding="@dimen/bubble_view_padding"
+        android:clipToPadding="false"/>
+
+    <TextView
+        android:id="@+id/message_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="@dimen/bubble_message_min_width"
+        android:maxWidth="@dimen/bubble_message_max_width"
+        android:padding="@dimen/bubble_message_padding"/>
+
+</com.android.systemui.bubbles.BubbleView>
diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml
index 7f4e0d2..f932303 100644
--- a/packages/SystemUI/res/layout/global_actions_wrapped.xml
+++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <com.android.systemui.HardwareUiLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_gravity="top|right"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
index ecfbfb4..c8e0845 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -25,12 +25,18 @@
     android:focusable="true"
     android:layout_gravity="center_vertical">
 
-    <ImageView
-        android:id="@+id/app_icon"
+    <FrameLayout
         android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
         android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
-        android:layout_gravity="start|center_vertical"
-    />
+        android:layout_gravity="start|center_vertical">
+
+        <ImageView
+            android:id="@+id/app_icon"
+            android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
+            android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
+            android:layout_gravity="center"
+            />
+    </FrameLayout>
 
     <TextView
         android:id="@+id/app_name"
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 2000104..74002ac 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -51,11 +51,4 @@
         android:layout_width="wrap_content"
         android:paddingEnd="2dp" />
 
-    <TextView
-        android:id="@+id/batteryRemainingText"
-        android:textAppearance="@style/TextAppearance.QS.TileLabel"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:gravity="center_vertical" />
-
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
index 307b538..5bcc1b3 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
@@ -39,7 +39,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="@string/monitoring_title_device_owned"
-                style="@android:style/TextAppearance.Material.Title"
+                style="@style/TextAppearance.DeviceManagementDialog.Title"
                 android:textColor="?android:attr/textColorPrimary"
                 android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
             />
@@ -64,7 +64,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="@string/monitoring_subtitle_ca_certificate"
-                style="@android:style/TextAppearance.Material.Title"
+                style="@style/TextAppearance.DeviceManagementDialog.Title"
                 android:textColor="?android:attr/textColorPrimary"
                 android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
             />
@@ -89,7 +89,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="@string/monitoring_subtitle_network_logging"
-                style="@android:style/TextAppearance.Material.Title"
+                style="@style/TextAppearance.DeviceManagementDialog.Title"
                 android:textColor="?android:attr/textColorPrimary"
                 android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
             />
@@ -114,7 +114,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="@string/monitoring_subtitle_vpn"
-                style="@android:style/TextAppearance.Material.Title"
+                style="@style/TextAppearance.DeviceManagementDialog.Title"
                 android:textColor="?android:attr/textColorPrimary"
                 android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
             />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 4e0cbe0..ed18dc7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -24,14 +24,13 @@
     android:layout_gravity="@integer/notification_panel_layout_gravity"
     android:background="@android:color/transparent"
     android:baselineAligned="false"
-    android:clickable="true"
+    android:clickable="false"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:paddingTop="0dp"
     android:paddingEnd="0dp"
     android:paddingStart="0dp"
-    android:elevation="4dp"
-    android:importantForAccessibility="no" >
+    android:elevation="4dp" >
 
     <include layout="@layout/quick_status_bar_header_system_icons" />
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 22b8d2f..cd9f780 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -24,6 +24,7 @@
     android:clipToPadding="false"
     android:gravity="center"
     android:orientation="horizontal"
+    android:clickable="true"
     android:paddingStart="@dimen/status_bar_padding_start"
     android:paddingEnd="@dimen/status_bar_padding_end" >
 
@@ -63,11 +64,5 @@
 
     <include layout="@layout/ongoing_privacy_chip" />
 
-    <com.android.systemui.BatteryMeterView
-        android:id="@+id/battery"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:gravity="center_vertical|end"
-        android:layout_gravity="center_vertical|end" />
     </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml
deleted file mode 100644
index cfa372b..0000000
--- a/packages/SystemUI/res/layout/signal_cluster_view.xml
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2011, 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.
-*/
--->
-<!-- extends LinearLayout -->
-<com.android.systemui.statusbar.SignalClusterView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/signal_cluster"
-    android:layout_height="match_parent"
-    android:layout_width="wrap_content"
-    android:gravity="center_vertical"
-    android:orientation="horizontal"
-    android:paddingEnd="@dimen/signal_cluster_battery_padding"
-    >
-    <ImageView
-        android:id="@+id/vpn"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:paddingEnd="6dp"
-        android:src="@drawable/stat_sys_vpn_ic"
-        android:tint="@color/background_protect_secondary"
-        android:contentDescription="@string/accessibility_vpn_on"
-        />
-    <FrameLayout
-        android:id="@+id/ethernet_combo"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        >
-        <com.android.systemui.statusbar.AlphaOptimizedImageView
-            android:theme="?attr/lightIconTheme"
-            android:id="@+id/ethernet"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            />
-        <com.android.systemui.statusbar.AlphaOptimizedImageView
-            android:theme="?attr/darkIconTheme"
-            android:id="@+id/ethernet_dark"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:alpha="0.0"
-            />
-    </FrameLayout>
-    <FrameLayout
-        android:layout_height="17dp"
-        android:layout_width="wrap_content">
-        <ImageView
-            android:id="@+id/wifi_in"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:src="@drawable/ic_activity_down"
-            android:visibility="gone"
-            android:paddingEnd="2dp"
-            />
-        <ImageView
-            android:id="@+id/wifi_out"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:src="@drawable/ic_activity_up"
-            android:paddingEnd="2dp"
-            android:visibility="gone"
-            />
-    </FrameLayout>
-    <FrameLayout
-        android:id="@+id/wifi_combo"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        >
-        <com.android.systemui.statusbar.AlphaOptimizedImageView
-            android:theme="?attr/lightIconTheme"
-            android:id="@+id/wifi_signal"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            />
-        <com.android.systemui.statusbar.AlphaOptimizedImageView
-            android:theme="?attr/darkIconTheme"
-            android:id="@+id/wifi_signal_dark"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:alpha="0.0"
-            />
-        <ImageView
-            android:id="@+id/wifi_inout"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            />
-    </FrameLayout>
-    <View
-        android:id="@+id/wifi_signal_spacer"
-        android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
-        android:layout_height="4dp"
-        android:visibility="gone"
-        />
-    <ViewStub
-        android:id="@+id/connected_device_signals_stub"
-        android:layout="@layout/connected_device_signal"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-    <LinearLayout
-        android:id="@+id/mobile_signal_group"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        >
-    </LinearLayout>
-    <View
-        android:id="@+id/wifi_airplane_spacer"
-        android:layout_width="@dimen/status_bar_airplane_spacer_width"
-        android:layout_height="4dp"
-        android:visibility="gone"
-        />
-    <ImageView
-        android:id="@+id/airplane"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        />
-</com.android.systemui.statusbar.SignalClusterView>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 34c208a..02062bb 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -43,6 +43,14 @@
                    android:visibility="invisible" />
     </com.android.systemui.statusbar.BackDropView>
 
+    <com.android.systemui.wallpaper.AodMaskView
+        android:id="@+id/aod_mask"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        android:visibility="invisible"
+        sysui:ignoreRightInset="true" />
+
     <com.android.systemui.statusbar.ScrimView
         android:id="@+id/scrim_behind"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index eed44dd..de2a950 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors is af"</string>
     <string name="device_services" msgid="1191212554435440592">"Toesteldienste"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Titelloos"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7c09112..4154643 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"ዳሳሾች ጠፍተዋል"</string>
     <string name="device_services" msgid="1191212554435440592">"የመሣሪያ አገልግሎቶች"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"ርዕስ የለም"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 16abb68..579964d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -914,6 +914,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"إيقاف أجهزة الاستشعار"</string>
     <string name="device_services" msgid="1191212554435440592">"خدمات الأجهزة"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"بلا عنوان"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 25f7b5b..8113504 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>টা অন্য এপ্</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"ছেন্সৰ অফ হৈ আছে"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"ডিভাইচ সেৱা"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"কোনো শিৰোনাম নাই"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 4cc91d2..6a27dbb 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_no_sim" msgid="8274017118472455155">"Няма SIM-карты."</string>
     <string name="accessibility_cell_data" msgid="5326139158682385073">"Мабільная перадача даных"</string>
     <string name="accessibility_cell_data_on" msgid="5927098403452994422">"Мабільная перадача даных уключана"</string>
-    <string name="cell_data_off_content_description" msgid="4356113230238585072">"Мабільны інтэрнэт выключаны"</string>
+    <string name="cell_data_off_content_description" msgid="4356113230238585072">"Мабільная перадача даных выключана"</string>
     <string name="cell_data_off" msgid="1051264981229902873">"Выключаны"</string>
     <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Сувязь па Bluetooth."</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"Рэжым палёту."</string>
@@ -865,7 +865,7 @@
     <string name="qs_dnd_replace" msgid="8019520786644276623">"Замяніць"</string>
     <string name="running_foreground_services_title" msgid="381024150898615683">"Праграмы, якія працуюць у фонавым рэжыме"</string>
     <string name="running_foreground_services_msg" msgid="6326247670075574355">"Дакраніцеся, каб даведацца пра выкарыстанне трафіка і акумулятара"</string>
-    <string name="mobile_data_disable_title" msgid="1068272097382942231">"Адключыць мабільны інтэрнэт?"</string>
+    <string name="mobile_data_disable_title" msgid="1068272097382942231">"Выключыць мабільную перадачу даных?"</string>
     <string name="mobile_data_disable_message" msgid="4756541658791493506">"У вас не будзе доступу да даных ці інтэрнэту праз аператара <xliff:g id="CARRIER">%s</xliff:g>. Інтэрнэт будзе даступны толькі праз Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6078110473451946831">"ваш аператар"</string>
     <string name="touch_filtered_warning" msgid="8671693809204767551">"Праграма хавае запыт на дазвол, таму ваш адказ немагчыма спраўдзіць у Наладах."</string>
@@ -904,6 +904,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчыкі выкл."</string>
     <string name="device_services" msgid="1191212554435440592">"Сэрвісы прылады"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Без назвы"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index ede7af9..e9effcf 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Сензорите са изключени"</string>
     <string name="device_services" msgid="1191212554435440592">"Услуги за устройството"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Няма заглавие"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 76a344f..0724e37 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"সেন্সর বন্ধ"</string>
     <string name="device_services" msgid="1191212554435440592">"ডিভাইস সংক্রান্ত পরিষেবা"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"কোনও শীর্ষক নেই"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 69550b0..4e8e833 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors desactivats"</string>
     <string name="device_services" msgid="1191212554435440592">"Serveis per a dispositius"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sense títol"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index cf116e3..30bf1de 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -902,6 +902,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzory jsou vypnuty"</string>
     <string name="device_services" msgid="1191212554435440592">"Služby zařízení"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Bez názvu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ce9a821..ad4afcf 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"System-UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ryd"</string>
-    <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen underretninger"</string>
+    <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen notifikationer"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"I gang"</string>
-    <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Underretninger"</string>
+    <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifikationer"</string>
     <string name="battery_low_title" msgid="9187898087363540349">"Enheden løber muligvis snart tør for batteri"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> tilbage"</string>
     <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Der er <xliff:g id="PERCENTAGE">%1$s</xliff:g> tilbage eller ca. <xliff:g id="TIME">%2$s</xliff:g>, alt efter hvordan du bruger enheden"</string>
@@ -41,7 +41,7 @@
     <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Automatisk skærmrotation"</string>
     <string name="status_bar_settings_mute_label" msgid="554682549917429396">"LYDLØS"</string>
     <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"AUTO"</string>
-    <string name="status_bar_settings_notifications" msgid="397146176280905137">"Underretninger"</string>
+    <string name="status_bar_settings_notifications" msgid="397146176280905137">"Notifikationer"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Netdeling via Bluetooth anvendt"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfigurer inputmetoder"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysisk tastatur"</string>
@@ -71,7 +71,7 @@
     <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Screenshottet kan ikke gemmes, fordi der er begrænset lagerplads"</string>
     <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Appen eller din organisation tillader ikke, at du tager screenshots"</string>
     <string name="screenrecord_name" msgid="4196719243134204796">"Skærmoptagelse"</string>
-    <string name="screenrecord_channel_description" msgid="4630777331970993858">"Konstant underretning om skærmoptagelse"</string>
+    <string name="screenrecord_channel_description" msgid="4630777331970993858">"Konstant notifikation om skærmoptagelse"</string>
     <string name="screenrecord_start_label" msgid="5177739269492196055">"Start optagelse"</string>
     <string name="screenrecord_mic_label" msgid="4522870600914810019">"Optag voiceover"</string>
     <string name="screenrecord_taps_label" msgid="1776467076607964790">"Vis tryk"</string>
@@ -187,9 +187,9 @@
     <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> %%."</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>
-    <string name="accessibility_remove_notification" msgid="3603099514902182350">"Ryd underretning."</string>
+    <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifikationer."</string>
+    <string name="accessibility_overflow_action" msgid="5681882033274783311">"Se alle notifikationer"</string>
+    <string name="accessibility_remove_notification" msgid="3603099514902182350">"Ryd notifikation."</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"GPS aktiveret."</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"GPS samler data."</string>
     <string name="accessibility_tty_enabled" msgid="4613200365379426561">"TeleTypewriter aktiveret."</string>
@@ -199,8 +199,8 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Underretningen er annulleret."</string>
-    <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Underretningspanel."</string>
+    <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifikationen er annulleret."</string>
+    <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notifikationspanel."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Hurtige indstillinger."</string>
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Låseskærm."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Indstillinger"</string>
@@ -265,14 +265,14 @@
     <string name="gps_notification_searching_text" msgid="8574247005642736060">"Søger efter GPS"</string>
     <string name="gps_notification_found_text" msgid="4619274244146446464">"Placeringen er angivet ved hjælp af GPS"</string>
     <string name="accessibility_location_active" msgid="2427290146138169014">"Aktive placeringsanmodninger"</string>
-    <string name="accessibility_clear_all" msgid="5235938559247164925">"Ryd alle underretninger."</string>
+    <string name="accessibility_clear_all" msgid="5235938559247164925">"Ryd alle notifikationer."</string>
     <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g> mere"</string>
     <string name="notification_group_overflow_indicator_ambient" msgid="879560382990377886">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, +<xliff:g id="OVERFLOW">%2$s</xliff:g>"</string>
     <plurals name="notification_group_overflow_description" formatted="false" msgid="4579313201268495404">
-      <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> underretning mere i gruppen.</item>
-      <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> underretninger mere i gruppen.</item>
+      <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> notifikation mere i gruppen.</item>
+      <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> notifikationer mere i gruppen.</item>
     </plurals>
-    <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Underretningsindstillinger"</string>
+    <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Notifikationsindstillinger"</string>
     <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Indstillinger for <xliff:g id="APP_NAME">%s</xliff:g>"</string>
     <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skærmen roterer automatisk."</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skærmen er nu låst i liggende retning."</string>
@@ -347,7 +347,7 @@
       <item quantity="one">%d enhed</item>
       <item quantity="other">%d enheder</item>
     </plurals>
-    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Underretninger"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifikationer"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lommelygte"</string>
     <string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobildata"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Dataforbrug"</string>
@@ -382,7 +382,7 @@
     <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"Dette blokerer ALLE lyde og vibrationer, bl.a. fra alarmer, musik, videoer og spil. Du vil stadig kunne foretage telefonopkald."</string>
     <string name="zen_silence_introduction" msgid="3137882381093271568">"Dette blokerer ALLE lyde og vibrationer, bl.a. fra alarmer, musik, videoer og spil."</string>
     <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
-    <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende underretninger nedenfor"</string>
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende notifikationer nedenfor"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tryk igen for at åbne"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Stryg opad for at låse op"</string>
     <string name="do_disclosure_generic" msgid="5615898451805157556">"Denne enhed administreres af din organisation"</string>
@@ -439,9 +439,9 @@
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Vis ikke igen"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Ryd alt"</string>
     <string name="manage_notifications_text" msgid="2386728145475108753">"Administrer"</string>
-    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Underretninger er sat på pause af Forstyr ikke"</string>
+    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifikationer er sat på pause af Forstyr ikke"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Start nu"</string>
-    <string name="empty_shade_text" msgid="708135716272867002">"Ingen underretninger"</string>
+    <string name="empty_shade_text" msgid="708135716272867002">"Ingen notifikationer"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"Profilen kan overvåges"</string>
     <string name="vpn_footer" msgid="2388611096129106812">"Netværket kan være overvåget"</string>
     <string name="branded_vpn_footer" msgid="2168111859226496230">"Netværket kan være overvåget"</string>
@@ -501,7 +501,7 @@
     <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"Låst op for <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> kører"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Enheden vil forblive låst, indtil du manuelt låser den op"</string>
-    <string name="hidden_notifications_title" msgid="7139628534207443290">"Modtag underretninger hurtigere"</string>
+    <string name="hidden_notifications_title" msgid="7139628534207443290">"Modtag notifikationer hurtigere"</string>
     <string name="hidden_notifications_text" msgid="2326409389088668981">"Se dem, før du låser op"</string>
     <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nej tak"</string>
     <string name="hidden_notifications_setup" msgid="41079514801976810">"Konfigurer"</string>
@@ -530,7 +530,7 @@
     <string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
     <string name="stream_music" msgid="9086982948697544342">"Medie"</string>
     <string name="stream_alarm" msgid="5209444229227197703">"Alarm"</string>
-    <string name="stream_notification" msgid="2563720670905665031">"Underretning"</string>
+    <string name="stream_notification" msgid="2563720670905665031">"Notifikation"</string>
     <string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="2447177903892477915">"Tonesignalfrekvens (DTMF)"</string>
     <string name="stream_accessibility" msgid="301136219144385106">"Hjælpefunktioner"</string>
@@ -549,7 +549,7 @@
     <string name="volume_ringer_hint_unmute" msgid="6602880133293060368">"slå lyden til"</string>
     <string name="volume_ringer_hint_vibrate" msgid="4036802135666515202">"vibrer"</string>
     <string name="volume_dialog_title" msgid="7272969888820035876">"%s lydstyrkeknapper"</string>
-    <string name="volume_dialog_ringer_guidance_ring" msgid="3360373718388509040">"Der afspilles lyd ved opkald og underretninger (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+    <string name="volume_dialog_ringer_guidance_ring" msgid="3360373718388509040">"Der afspilles lyd ved opkald og notifikationer (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
     <string name="output_title" msgid="5355078100792942802">"Medieafspilning"</string>
     <string name="output_calls_title" msgid="8717692905017206161">"Udgang til telefonopkald"</string>
     <string name="output_none_found" msgid="5544982839808921091">"Der blev ikke fundet nogen enheder"</string>
@@ -595,25 +595,25 @@
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vil du slå Bluetooth til?"</string>
     <string name="enable_bluetooth_message" msgid="9106595990708985385">"Bluetooth skal være slået til, før du kan knytte dit tastatur til din tablet."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Slå til"</string>
-    <string name="show_silently" msgid="6841966539811264192">"Vis underretninger lydløst"</string>
-    <string name="block" msgid="2734508760962682611">"Bloker alle underretninger"</string>
+    <string name="show_silently" msgid="6841966539811264192">"Vis notifikationer lydløst"</string>
+    <string name="block" msgid="2734508760962682611">"Bloker alle notifikationer"</string>
     <string name="do_not_silence" msgid="6878060322594892441">"Skal ikke sættes på lydløs"</string>
     <string name="do_not_silence_block" msgid="4070647971382232311">"Skal ikke sættes på lydløs eller blokeres"</string>
-    <string name="tuner_full_importance_settings" msgid="3207312268609236827">"Kontrolelementer til underretning om strøm"</string>
+    <string name="tuner_full_importance_settings" msgid="3207312268609236827">"Kontrolelementer til notifikation om strøm"</string>
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Til"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Fra"</string>
-    <string name="power_notification_controls_description" msgid="4372459941671353358">"Med kontrolelementer til underretninger om strøm kan du konfigurere et vigtighedsniveau fra 0 til 5 for en apps underretninger. \n\n"<b>"Niveau 5"</b>\n"- Vis øverst på listen over underretninger \n- Tillad afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 4"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 3"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n\n"<b>"Niveau 2"</b>\n"- Ingen afbrydelse af fuld skærm \n Se aldrig smugkig \n- Ingen lyd og vibration \n\n"<b>"Niveau 1"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n- Ingen lyd eller vibration \n- Skjul fra låseskærm og statusbjælke \n- Vis nederst på listen over underretninger \n\n"<b>"Niveau 0"</b>\n"- Bloker alle underretninger fra appen."</string>
-    <string name="notification_header_default_channel" msgid="7506845022070889909">"Underretninger"</string>
-    <string name="notification_channel_disabled" msgid="344536703863700565">"Du får ikke længere vist disse underretninger"</string>
-    <string name="notification_channel_minimized" msgid="1664411570378910931">"Disse underretninger minimeres"</string>
-    <string name="notification_channel_silenced" msgid="2877199534497961942">"Disse underretninger vises lydløst"</string>
-    <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Disse underretninger underretter dig"</string>
-    <string name="inline_blocking_helper" msgid="3055064577771478591">"Du afviser som regel disse underretninger. \nVil du blive ved med at se dem?"</string>
-    <string name="inline_keep_showing" msgid="8945102997083836858">"Vil du fortsætte med at se disse underretninger?"</string>
-    <string name="inline_stop_button" msgid="4172980096860941033">"Stop underretninger"</string>
+    <string name="power_notification_controls_description" msgid="4372459941671353358">"Med kontrolelementer til notifikationer om strøm kan du konfigurere et vigtighedsniveau fra 0 til 5 for en apps notifikationer. \n\n"<b>"Niveau 5"</b>\n"- Vis øverst på listen over notifikationer \n- Tillad afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 4"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 3"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n\n"<b>"Niveau 2"</b>\n"- Ingen afbrydelse af fuld skærm \n Se aldrig smugkig \n- Ingen lyd og vibration \n\n"<b>"Niveau 1"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n- Ingen lyd eller vibration \n- Skjul fra låseskærm og statusbjælke \n- Vis nederst på listen over notifikationer \n\n"<b>"Niveau 0"</b>\n"- Bloker alle notifikationer fra appen."</string>
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifikationer"</string>
+    <string name="notification_channel_disabled" msgid="344536703863700565">"Du får ikke længere vist disse notifikationer"</string>
+    <string name="notification_channel_minimized" msgid="1664411570378910931">"Disse notifikationer minimeres"</string>
+    <string name="notification_channel_silenced" msgid="2877199534497961942">"Disse notifikationer vises lydløst"</string>
+    <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Disse notifikationer underretter dig"</string>
+    <string name="inline_blocking_helper" msgid="3055064577771478591">"Du afviser som regel disse notifikationer. \nVil du blive ved med at se dem?"</string>
+    <string name="inline_keep_showing" msgid="8945102997083836858">"Vil du fortsætte med at se disse notifikationer?"</string>
+    <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifikationer"</string>
     <!-- no translation found for inline_block_button (8735843688021655065) -->
     <skip />
-    <string name="inline_keep_button" msgid="6665940297019018232">"Fortsæt med at vise underretninger"</string>
+    <string name="inline_keep_button" msgid="6665940297019018232">"Fortsæt med at vise notifikationer"</string>
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimer"</string>
     <string name="inline_silent_button_silent" msgid="4411510650503783646">"Vis lydløst"</string>
     <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
@@ -622,8 +622,8 @@
     <skip />
     <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
     <skip />
-    <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsætte med at se underretninger fra denne app?"</string>
-    <string name="notification_unblockable_desc" msgid="1037434112919403708">"Disse underretninger kan ikke deaktiveres"</string>
+    <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsætte med at se notifikationer fra denne app?"</string>
+    <string name="notification_unblockable_desc" msgid="1037434112919403708">"Disse notifikationer kan ikke deaktiveres"</string>
     <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Denne app anvender kameraet."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Denne app anvender mikrofonen."</string>
@@ -634,15 +634,15 @@
     <string name="appops_camera_mic_overlay" msgid="6718768197048030993">"Denne app vises over andre apps på din skærm og anvender mikrofonen og kameraet."</string>
     <string name="notification_appops_settings" msgid="1028328314935908050">"Indstillinger"</string>
     <string name="notification_appops_ok" msgid="1156966426011011434">"OK"</string>
-    <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Styring af underretninger for <xliff:g id="APP_NAME">%1$s</xliff:g> blev åbnet"</string>
-    <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Styring af underretninger for <xliff:g id="APP_NAME">%1$s</xliff:g> blev lukket"</string>
-    <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Tillad underretninger fra denne kanal"</string>
+    <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Styring af notifikationer for <xliff:g id="APP_NAME">%1$s</xliff:g> blev åbnet"</string>
+    <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Styring af notifikationer for <xliff:g id="APP_NAME">%1$s</xliff:g> blev lukket"</string>
+    <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Tillad notifikationer fra denne kanal"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Flere indstillinger"</string>
     <string name="notification_app_settings" msgid="420348114670768449">"Tilpas"</string>
     <string name="notification_done" msgid="5279426047273930175">"Udfør"</string>
     <string name="inline_undo" msgid="558916737624706010">"Fortryd"</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">"kontrolelementer til underretninger"</string>
+    <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrolelementer til notifikationer"</string>
     <string name="notification_menu_snooze_description" msgid="3653669438131034525">"Indstillinger for udsættelse"</string>
     <string name="notification_menu_snooze_action" msgid="1112254519029621372">"Udsæt"</string>
     <string name="snooze_undo" msgid="6074877317002985129">"FORTRYD"</string>
@@ -689,7 +689,7 @@
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Start"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Seneste"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Tilbage"</string>
-    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Underretninger"</string>
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifikationer"</string>
     <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Tastaturgenveje"</string>
     <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"Skift tastaturlayout"</string>
     <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applikationer"</string>
@@ -759,7 +759,7 @@
     <item msgid="2139628951880142927">"Vis procent ved opladning (standard)"</item>
     <item msgid="3327323682209964956">"Vis ikke dette ikon"</item>
   </string-array>
-    <string name="tuner_low_priority" msgid="1325884786608312358">"Vis ikoner for underretninger med lav prioritet"</string>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Vis ikoner for notifikationer med lav prioritet"</string>
     <string name="other" msgid="4060683095962566764">"Andet"</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>
@@ -779,7 +779,7 @@
     <string name="accessibility_qs_edit_tile_add" msgid="3520406665865985109">"Føj <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <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="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</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>
@@ -828,7 +828,7 @@
     <string name="tuner_right" msgid="6222734772467850156">"Højre"</string>
     <string name="tuner_menu" msgid="191640047241552081">"Menu"</string>
     <string name="tuner_app" msgid="3507057938640108777">"Appen <xliff:g id="APP">%1$s</xliff:g>"</string>
-    <string name="notification_channel_alerts" msgid="4496839309318519037">"Underretninger"</string>
+    <string name="notification_channel_alerts" msgid="4496839309318519037">"Notifikationer"</string>
     <string name="notification_channel_battery" msgid="5786118169182888462">"Batteri"</string>
     <string name="notification_channel_screenshot" msgid="6314080179230000938">"Screenshots"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"Generelle meddelelser"</string>
@@ -852,7 +852,7 @@
     <string name="qs_dnd_keep" msgid="1825009164681928736">"Behold"</string>
     <string name="qs_dnd_replace" msgid="8019520786644276623">"Erstat"</string>
     <string name="running_foreground_services_title" msgid="381024150898615683">"Apps, der kører i baggrunden"</string>
-    <string name="running_foreground_services_msg" msgid="6326247670075574355">"Tryk for at se oplysninger om batteri- og dataforbrug"</string>
+    <string name="running_foreground_services_msg" msgid="6326247670075574355">"Tryk for at se info om batteri- og dataforbrug"</string>
     <string name="mobile_data_disable_title" msgid="1068272097382942231">"Vil du deaktivere mobildata?"</string>
     <string name="mobile_data_disable_message" msgid="4756541658791493506">"Du vil ikke have data- eller internetadgang via <xliff:g id="CARRIER">%s</xliff:g>. Der vil kunne være adgang til internettet via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6078110473451946831">"dit mobilselskab"</string>
@@ -876,7 +876,7 @@
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Apps anvender enhedens <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
     <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
     <string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Luk"</string>
-    <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Se oplysninger"</string>
+    <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Se info"</string>
     <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App, der anvender din/dit <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
     <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps, der anvender din/dit <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Deaktiver sensorer"</string>
     <string name="device_services" msgid="1191212554435440592">"Enhedstjenester"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Ingen titel"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 447f8b9..0be730a 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -894,6 +894,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensoren aus"</string>
     <string name="device_services" msgid="1191212554435440592">"Gerätedienste"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Kein Titel"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 5e2fb03..703bc40 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
     <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3891569..31fd04a 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
     <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 5e2fb03..703bc40 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
     <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 5e2fb03..703bc40 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
     <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"No title"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 9ef6a83..761acf6 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> app más</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Se desactivaron los sensores"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"Servicios del dispositivo"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sin título"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index f4e107d..0b3ba5d 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desactivados"</string>
     <string name="device_services" msgid="1191212554435440592">"Servicios del dispositivo"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sin título"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index d8568df..e318291 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Andurid on välja lülitatud"</string>
     <string name="device_services" msgid="1191212554435440592">"Seadme teenused"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Pealkiri puudub"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index cfdf7a5..5b9f0cf 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sentsoreak desaktibatuta daude"</string>
     <string name="device_services" msgid="1191212554435440592">"Gailuetarako zerbitzuak"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Ez du izenik"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index cd92d45..931def7 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Anturit pois päältä"</string>
     <string name="device_services" msgid="1191212554435440592">"Laitepalvelut"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Ei nimeä"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 695a6a5..d1412aa 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Capteurs désactivés"</string>
     <string name="device_services" msgid="1191212554435440592">"Services de l\'appareil"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sans titre"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index a1a0352..8ba575b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Capteurs désactivés"</string>
     <string name="device_services" msgid="1191212554435440592">"Services pour l\'appareil"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sans titre"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 7f111e8..2784567 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Desactivar sensores"</string>
     <string name="device_services" msgid="1191212554435440592">"Servizos do dispositivo"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sen título"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 8525094..f17a7f6 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> અન્ય ઍપ</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"સેન્સર બંધ છે"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"ડિવાઇસ સેવાઓ"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"કોઈ શીર્ષક નથી"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index ae12412..49d923a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Érzékelők kikapcsolva"</string>
     <string name="device_services" msgid="1191212554435440592">"Eszközszolgáltatások"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Nincs cím"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index ffcc762..dc592c5 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Տվիչներն անջատած են"</string>
     <string name="device_services" msgid="1191212554435440592">"Սարքի ծառայություններ"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Անանուն"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index eb99a6c..1b43e65 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensor nonaktif"</string>
     <string name="device_services" msgid="1191212554435440592">"Layanan Perangkat"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Tanpa judul"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 0040c3c..36b557d 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Slökkt á skynjurum"</string>
     <string name="device_services" msgid="1191212554435440592">"Tækjaþjónusta"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Enginn titill"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 83956e8..21c259a 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensori disattivati"</string>
     <string name="device_services" msgid="1191212554435440592">"Servizi del dispositivo"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Senza titolo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index f7d52ed..ff88a0d 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"センサー OFF"</string>
     <string name="device_services" msgid="1191212554435440592">"デバイス サービス"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"タイトルなし"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index a1788fc..41ef3e2 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчиктер өшірулі"</string>
     <string name="device_services" msgid="1191212554435440592">"Құрылғы қызметтері"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Атауы жоқ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 0cbd06c..5520cb0 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"បិទឧបករណ៍​ចាប់សញ្ញា"</string>
     <string name="device_services" msgid="1191212554435440592">"សេវាកម្មឧបករណ៍"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"គ្មាន​ចំណងជើង"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 9f46742..f829c12 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ಇತರ ಆ್ಯಪ್‌ಗಳು</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"ಸೆನ್ಸರ್‌ಗಳು ಆಫ್"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"ಸಾಧನ ಸೇವೆಗಳು"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"ಯಾವುದೇ ಶೀರ್ಷಿಕೆಯಿಲ್ಲ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 581ffa9..f5168fb 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"센서 사용 안함"</string>
     <string name="device_services" msgid="1191212554435440592">"기기 서비스"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"제목 없음"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ba5a0ac..9558c3a 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Сенсорлорду өчүрүү"</string>
     <string name="device_services" msgid="1191212554435440592">"Түзмөк кызматтары"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Аталышы жок"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 46cd3f0..5786fb0 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -901,8 +901,6 @@
       <item quantity="other">Dar <xliff:g id="NUM_APPS_1">%d</xliff:g> programų</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Jutikliai išjungti"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"Įrenginio paslaugos"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Nėra pavadinimo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a1203d5..cebd91c 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -896,6 +896,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensori izslēgti"</string>
     <string name="device_services" msgid="1191212554435440592">"Ierīces pakalpojumi"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Nav nosaukuma"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a3fd2d5..df1bdbb 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Сензорите се исклучени"</string>
     <string name="device_services" msgid="1191212554435440592">"Услуги за уредот"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Без наслов"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index f3075b8..5800e0e 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"സെൻസറുകൾ ഓഫാണ്"</string>
     <string name="device_services" msgid="1191212554435440592">"ഉപകരണ സേവനങ്ങള്‍"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"പേരില്ല"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 7236893..e96865e 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Мэдрэгчийг унтраах"</string>
     <string name="device_services" msgid="1191212554435440592">"Төхөөрөмжийн үйлчилгээ"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Гарчиггүй"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 59e481c..6022a9c 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="other">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अ‍ॅप्स</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"सेन्सर बंद आहेत"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"डिव्हाइस सेवा"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"शीर्षक नाही"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index e77771d..1a9eb1c 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Penderia dimatikan"</string>
     <string name="device_services" msgid="1191212554435440592">"Perkhidmatan Peranti"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Tiada tajuk"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index e5561e8..dbfa01e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"အာရုံခံကိရိယာများ ပိတ်ထားသည်"</string>
     <string name="device_services" msgid="1191212554435440592">"စက်ပစ္စည်းဝန်ဆောင်မှုများ"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"ခေါင်းစဉ် မရှိပါ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index a1a12bd..e790684 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorer er av"</string>
     <string name="device_services" msgid="1191212554435440592">"Enhetstjenester"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Ingen tittel"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 096db56..9b4441f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> अन्य अनुप्रयोग</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"सेन्सरहरू निष्क्रिय छन्"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"यन्त्रका सेवाहरू"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"शीर्षक छैन"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 3607ff8..e570789 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensoren uit"</string>
     <string name="device_services" msgid="1191212554435440592">"Apparaatservices"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Geen titel"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 5cb98ee..dd260b1 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g>ଟି ଅନ୍ୟ ଆପ୍‍</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"ସେନ୍ସର୍‍ଗୁଡ଼ିକ ବନ୍ଦ ଅଛି"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"ଡିଭାଇସ୍‍ ସେବାଗୁଡିକ"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"କୌଣସି ଶୀର୍ଷକ ନାହିଁ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3702e2b..e36fea2 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ਹੋਰ ਐਪਾਂ</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"ਸੈਂਸਰ ਬੰਦ ਕਰੋ"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"ਕੋਈ ਸਿਰਲੇਖ ਨਹੀਂ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 4ece82c..ed2902f 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -901,8 +901,6 @@
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> inna aplikacja</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Wyłącz czujniki"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"Usługi urządzenia"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Bez tytułu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 375f5e8..c1858fc 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> outra aplicação</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desativados"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"Serviços do dispositivo"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sem título"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 24254f6..c393bde 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -896,6 +896,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori dezactivați"</string>
     <string name="device_services" msgid="1191212554435440592">"Servicii pentru dispozitiv"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Fără titlu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a5d4059..e643c8f 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -902,6 +902,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчики отключены"</string>
     <string name="device_services" msgid="1191212554435440592">"Сервисы устройства"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Без названия"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index d9ba8de..bcc444f 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -901,8 +901,6 @@
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ďalšia aplikácia</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzory sú vypnuté"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"Služby zariadenia"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Bez názvu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 5048227..706e700 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -902,6 +902,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Izklop za tipala"</string>
     <string name="device_services" msgid="1191212554435440592">"Storitve naprave"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Brez naslova"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 22ffa81..3c8c3ab 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -800,7 +800,7 @@
     <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimera"</string>
     <string name="pip_phone_close" msgid="8416647892889710330">"Stäng"</string>
     <string name="pip_phone_settings" msgid="8080777499521528521">"Inställningar"</string>
-    <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Tryck och dra nedåt för att avvisa"</string>
+    <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Tryck och dra nedåt för att ta bort"</string>
     <string name="pip_menu_title" msgid="4707292089961887657">"Meny"</string>
     <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> visas i bild-i-bild"</string>
     <string name="pip_notification_message" msgid="5619512781514343311">"Om du inte vill att den här funktionen används i <xliff:g id="NAME">%s</xliff:g> öppnar du inställningarna genom att trycka. Sedan inaktiverar du funktionen."</string>
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorer har inaktiverats"</string>
     <string name="device_services" msgid="1191212554435440592">"Enhetstjänster"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Ingen titel"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index a35c78c..f75cc4d 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="one">வேறு <xliff:g id="NUM_APPS_0">%d</xliff:g> ஆப்ஸ்</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"சென்சார்களை ஆஃப் செய்தல்"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"சாதன சேவைகள்"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"தலைப்பு இல்லை"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index e7eafbe..894263e 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -889,8 +889,6 @@
       <item quantity="one">మరో <xliff:g id="NUM_APPS_0">%d</xliff:g> యాప్</item>
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"సెన్సార్‌లు ఆఫ్"</string>
-    <!-- no translation found for device_services (1191212554435440592) -->
-    <skip />
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="device_services" msgid="1191212554435440592">"పరికర సేవలు"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"శీర్షిక లేదు"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 05df7bd..ae581ac 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"เซ็นเซอร์ปิดอยู่"</string>
     <string name="device_services" msgid="1191212554435440592">"บริการของอุปกรณ์"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"ไม่มีชื่อ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 3bd6ea6..ab20ff0 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Naka-off ang mga sensor"</string>
     <string name="device_services" msgid="1191212554435440592">"Mga Serbisyo ng Device"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Walang pamagat"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 4962b16..32201fb 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensörler kapalı"</string>
     <string name="device_services" msgid="1191212554435440592">"Cihaz Hizmetleri"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Başlıksız"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 3a59029..ee9da5e 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"سینسرز آف ہیں"</string>
     <string name="device_services" msgid="1191212554435440592">"آلہ کی سروس"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"کوئی عنوان نہیں ہے"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3388bcb..488bfae 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorlar nofaol"</string>
     <string name="device_services" msgid="1191212554435440592">"Qurilma xizmatlari"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Nomsiz"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 3ebb8dd..125eb72 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"Tắt cảm biến"</string>
     <string name="device_services" msgid="1191212554435440592">"Dịch vụ cho thiết bị"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Không có tiêu đề"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d480a8a..d6fd3d89 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"已关闭传感器"</string>
     <string name="device_services" msgid="1191212554435440592">"设备服务"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"无标题"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 7ff70d9..dbe24bb 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -890,6 +890,5 @@
     </plurals>
     <string name="sensor_privacy_mode" msgid="8982771253020769598">"已關閉感應器"</string>
     <string name="device_services" msgid="1191212554435440592">"裝置服務"</string>
-    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
-    <skip />
+    <string name="music_controls_no_title" msgid="5236895307087002011">"無標題"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 889db66..98f0cbe 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -106,7 +106,7 @@
 
     <!-- The default tiles to display in QuickSettings -->
     <string name="quick_settings_tiles_default" translatable="false">
-        wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast
+        wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,sensorprivacy
     </string>
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
@@ -449,6 +449,15 @@
          better (narrower) line-break for a double-line smart reply button. -->
     <integer name="config_smart_replies_in_notifications_max_squeeze_remeasure_attempts">3</integer>
 
+    <!-- Smart replies in notifications: Whether by default tapping on a choice should let the user
+         edit the input before it is sent to the app. Developers can override this via
+         RemoteInput.Builder.setEditChoicesBeforeSending. -->
+    <bool name="config_smart_replies_in_notifications_edit_choices_before_sending">false</bool>
+
+    <!-- Smart replies in notifications: Whether smart suggestions in notifications are enabled in
+         heads-up notifications.  -->
+    <bool name="config_smart_replies_in_notifications_show_in_heads_up">true</bool>
+
     <!-- Screenshot editing default activity.  Must handle ACTION_EDIT image/png intents.
          Blank sends the user to the Chooser first.
          This name is in the ComponentName flattened format (package/class)  -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 06df0e7..ab0bbe1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -66,9 +66,7 @@
     <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
 
     <dimen name="group_overflow_number_size">@*android:dimen/notification_text_size</dimen>
-    <dimen name="group_overflow_number_size_dark">16sp</dimen>
     <dimen name="group_overflow_number_padding">@*android:dimen/notification_content_margin_end</dimen>
-    <dimen name="group_overflow_number_extra_padding_dark">@*android:dimen/notification_extra_margin_ambient</dimen>
 
     <!-- max height of a notification such that the content can still fade out when closing -->
     <dimen name="max_notification_fadeout_height">100dp</dimen>
@@ -94,9 +92,6 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">294dp</dimen>
 
-    <!-- Height of an ambient notification on ambient display -->
-    <dimen name="notification_ambient_height">400dp</dimen>
-
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
 
@@ -952,6 +947,8 @@
     <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen>
     <!-- Height and width of Application icons in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
+    <!-- Height and width of Plus sign in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_app_plus_size">24dp</dimen>
     <!-- Height of line in Ongoing App Ops dialog-->
     <dimen name="ongoing_appops_dialog_line_height">48dp</dimen>
     <!-- Side margin of title in Ongoing App Ops dialog -->
@@ -981,14 +978,16 @@
 
     <!-- How much a bubble is elevated -->
     <dimen name="bubble_elevation">8dp</dimen>
+    <!-- Padding around a collapsed bubble -->
+    <dimen name="bubble_view_padding">0dp</dimen>
     <!-- Padding between bubbles when displayed in expanded state -->
     <dimen name="bubble_padding">8dp</dimen>
-    <!-- Padding around the view displayed when the bubble is expanded -->
-    <dimen name="bubble_expanded_view_padding">8dp</dimen>
     <!-- Size of the collapsed bubble -->
     <dimen name="bubble_size">56dp</dimen>
-    <!-- Size of an icon displayed within the bubble -->
-    <dimen name="bubble_icon_size">24dp</dimen>
+    <!-- How much to inset the icon in the circle -->
+    <dimen name="bubble_icon_inset">16dp</dimen>
+    <!-- Padding around the view displayed when the bubble is expanded -->
+    <dimen name="bubble_expanded_view_padding">8dp</dimen>
     <!-- Default height of the expanded view shown when the bubble is expanded -->
     <dimen name="bubble_expanded_default_height">400dp</dimen>
     <!-- Height of the triangle that points to the expanded bubble -->
@@ -997,4 +996,14 @@
     <dimen name="bubble_pointer_width">6dp</dimen>
     <!-- Extra padding around the dismiss target for bubbles -->
     <dimen name="bubble_dismiss_slop">16dp</dimen>
+    <!-- Height of the header within the expanded view. -->
+    <dimen name="bubble_expanded_header_height">48dp</dimen>
+    <!-- Left and right padding applied to the header. -->
+    <dimen name="bubble_expanded_header_horizontal_padding">24dp</dimen>
+    <!-- Max width of the message bubble-->
+    <dimen name="bubble_message_max_width">144dp</dimen>
+    <!-- Min width of the message bubble -->
+    <dimen name="bubble_message_min_width">32dp</dimen>
+    <!-- Interior padding of the message bubble -->
+    <dimen name="bubble_message_padding">4dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index dac20b5..bd34bea 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -102,12 +102,20 @@
     <item type="id" name="action_snooze_assistant_suggestion_1"/>
     <item type="id" name="action_snooze"/>
 
-    <!-- For StatusBarIconContainer to tag its icon views -->
+    <!-- For StatusIconContainer to tag its icon views -->
     <item type="id" name="status_bar_view_state_tag" />
 
     <item type="id" name="display_cutout" />
 
     <!-- Optional cancel button on Keyguard -->
     <item type="id" name="cancel_button"/>
+
+    <!-- AodMaskView transition tag -->
+    <item type="id" name="aod_mask_transition_progress_tag" />
+    <item type="id" name="aod_mask_transition_progress_end_tag" />
+    <item type="id" name="aod_mask_transition_progress_start_tag" />
+
+    <!-- Global Actions Menu -->
+    <item type="id" name="global_actions_view" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8a5a69b..fa675b7 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -64,48 +64,16 @@
         <item name="hybridNotificationTextStyle">@style/hybrid_notification_text</item>
     </style>
 
-    <style name="HybridNotification.Ambient">
-        <item name="hybridNotificationStyle">@style/hybrid_notification_ambient</item>
-        <item name="hybridNotificationTitleStyle">@style/hybrid_notification_title_ambient</item>
-        <item name="hybridNotificationTextStyle">@style/hybrid_notification_text_ambient</item>
-    </style>
-
-    <style name="hybrid_notification_ambient">
-        <item name="android:paddingStart">@*android:dimen/notification_extra_margin_ambient</item>
-        <item name="android:paddingEnd">@*android:dimen/notification_extra_margin_ambient</item>
-        <item name="android:orientation">vertical</item>
-    </style>
-
     <style name="hybrid_notification">
         <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item>
         <item name="android:paddingEnd">12dp</item>
     </style>
 
-    <style name="hybrid_notification_title_ambient">
-        <item name="android:layout_marginTop">@*android:dimen/notification_header_margin_top_ambient</item>
-        <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item>
-        <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item>
-        <item name="android:textAppearance">@*android:style/Notification.Header.Ambient</item>
-        <item name="android:layout_gravity">top|center_horizontal</item>
-        <item name="android:textSize">@*android:dimen/notification_ambient_title_text_size</item>
-        <item name="android:textColor">#ffffffff</item>
-    </style>
-
     <style name="hybrid_notification_title">
         <item name="android:paddingEnd">4dp</item>
         <item name="android:textAppearance">@*android:style/TextAppearance.Material.Notification.Title</item>
     </style>
 
-    <style name="hybrid_notification_text_ambient">
-        <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item>
-        <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item>
-        <item name="android:textSize">@*android:dimen/notification_ambient_text_size</item>
-        <item name="android:textColor">#eeffffff</item>
-        <item name="android:gravity">top|center_horizontal</item>
-        <item name="android:ellipsize">end</item>
-        <item name="android:maxLines">3</item>
-    </style>
-
     <style name="hybrid_notification_text"
            parent="@*android:style/Widget.Material.Notification.Text">
         <item name="android:paddingEnd">4dp</item>
@@ -125,7 +93,7 @@
 
     <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
         <item name="android:textSize">@dimen/status_bar_clock_size</item>
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:textColor">@color/status_bar_clock_color</item>
     </style>
 
@@ -265,6 +233,10 @@
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
+    <style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+        <item name="android:gravity">center</item>
+    </style>
+
     <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 523720d5..1a684a0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -323,7 +323,7 @@
                     return null;
                 }
                 // Create our own ClassLoader so we can use our own code as the parent.
-                ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
+                ClassLoader classLoader = mManager.getClassLoader(info);
                 Context pluginContext = new PluginContextWrapper(
                         mContext.createApplicationContext(info, 0), classLoader);
                 Class<?> pluginClass = Class.forName(cls, true, classLoader);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index da143f9..7139708 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.shared.plugins;
 
+import android.app.LoadedApk;
 import android.app.Notification;
 import android.app.Notification.Action;
 import android.app.NotificationManager;
@@ -44,15 +45,17 @@
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
 import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
 
 import dalvik.system.PathClassLoader;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 /**
  * @see Plugin
@@ -117,6 +120,7 @@
         return mPluginEnabler;
     }
 
+    // TODO(mankoff): This appears to be only called from tests. Remove?
     public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
         ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
         if (info == null) {
@@ -282,17 +286,25 @@
         }
     }
 
-    public ClassLoader getClassLoader(String sourceDir, String pkg) {
-        if (!isDebuggable && !mWhitelistedPlugins.contains(pkg)) {
-            Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + sourceDir +
-                    ", pkg: " + pkg);
+    /** Returns class loader specific for the given plugin. */
+    public ClassLoader getClassLoader(ApplicationInfo appInfo) {
+        if (!isDebuggable && !mWhitelistedPlugins.contains(appInfo.packageName)) {
+            Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:"
+                    + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
             return null;
         }
-        if (mClassLoaders.containsKey(pkg)) {
-            return mClassLoaders.get(pkg);
+        if (mClassLoaders.containsKey(appInfo.packageName)) {
+            return mClassLoaders.get(appInfo.packageName);
         }
-        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
-        mClassLoaders.put(pkg, classLoader);
+
+        List<String> zipPaths = new ArrayList<>();
+        List<String> libPaths = new ArrayList<>();
+        LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths);
+        ClassLoader classLoader = new PathClassLoader(
+                TextUtils.join(File.pathSeparator, zipPaths),
+                TextUtils.join(File.pathSeparator, libPaths),
+                getParentClassLoader());
+        mClassLoaders.put(appInfo.packageName, classLoader);
         return classLoader;
     }
 
@@ -309,11 +321,6 @@
         return mParentClassLoader;
     }
 
-    public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
-        ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
-        return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
-    }
-
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
         for (int i = 0; i < mPluginMap.size(); i++) {
             if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index f3bdbae..078947c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -76,4 +76,10 @@
      */
     float getWindowCornerRadius() = 10;
 
+    /**
+     * If device supports live rounded corners on windows.
+     * This might be turned off for performance reasons
+     */
+    boolean supportsRoundedCornersOnWindows() = 11;
+
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index c0a1d89..e6acfbe 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -16,14 +16,10 @@
 
 package com.android.systemui.shared.system;
 
+import android.graphics.HardwareRenderer;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.view.Surface;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.SyncRtSurfaceTransactionApplier;
-import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewRootImpl;
 
@@ -31,20 +27,21 @@
 
 /**
  * Helper class to apply surface transactions in sync with RenderThread.
+ *
+ * NOTE: This is a modification of {@link android.view.SyncRtSurfaceTransactionApplier}, we can't 
+ *       currently reference that class from the shared lib as it is hidden.
  */
 public class SyncRtSurfaceTransactionApplierCompat {
 
-    private final SyncRtSurfaceTransactionApplier mApplier;
+    private final Surface mTargetSurface;
+    private final ViewRootImpl mTargetViewRootImpl;
 
     /**
      * @param targetView The view in the surface that acts as synchronization anchor.
      */
     public SyncRtSurfaceTransactionApplierCompat(View targetView) {
-        mApplier = new SyncRtSurfaceTransactionApplier(targetView);
-    }
-
-    private SyncRtSurfaceTransactionApplierCompat(SyncRtSurfaceTransactionApplier applier) {
-        mApplier = applier;
+        mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
+        mTargetSurface = mTargetViewRootImpl != null ? mTargetViewRootImpl.mSurface : null;
     }
 
     /**
@@ -53,38 +50,74 @@
      * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
      *               this method to avoid synchronization issues.
      */
-    public void scheduleApply(final SurfaceParams... params) {
-        mApplier.scheduleApply(convert(params));
-    }
-
-    private SyncRtSurfaceTransactionApplier.SurfaceParams[] convert(SurfaceParams[] params) {
-        SyncRtSurfaceTransactionApplier.SurfaceParams[] result =
-                new SyncRtSurfaceTransactionApplier.SurfaceParams[params.length];
-        for (int i = 0; i < params.length; i++) {
-            result[i] = params[i].mParams;
+    public void scheduleApply(final SyncRtSurfaceTransactionApplierCompat.SurfaceParams... params) {
+        if (mTargetViewRootImpl == null) {
+            return;
         }
-        return result;
+        mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
+            @Override
+            public void onFrameDraw(long frame) {
+                if (mTargetSurface == null || !mTargetSurface.isValid()) {
+                    return;
+                }
+                TransactionCompat t = new TransactionCompat();
+                for (int i = params.length - 1; i >= 0; i--) {
+                    SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
+                            params[i];
+                    SurfaceControlCompat surface = surfaceParams.surface;
+                    t.deferTransactionUntil(surface, mTargetSurface, frame);
+                    applyParams(t, surfaceParams);
+                }
+                t.setEarlyWakeup();
+                t.apply();
+            }
+        });
+
+        // Make sure a frame gets scheduled.
+        mTargetViewRootImpl.getView().invalidate();
     }
 
-    public static void applyParams(TransactionCompat t, SurfaceParams params) {
-        SyncRtSurfaceTransactionApplier.applyParams(t.mTransaction, params.mParams, t.mTmpValues);
+    public static void applyParams(TransactionCompat t,
+            SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) {
+        t.setMatrix(params.surface, params.matrix);
+        t.setWindowCrop(params.surface, params.windowCrop);
+        t.setAlpha(params.surface, params.alpha);
+        t.setLayer(params.surface, params.layer);
+        t.setCornerRadius(params.surface, params.cornerRadius);
+        t.show(params.surface);
     }
 
+    /**
+     * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+     * attached if necessary.
+     */
     public static void create(final View targetView,
             final Consumer<SyncRtSurfaceTransactionApplierCompat> callback) {
-        SyncRtSurfaceTransactionApplier.create(targetView,
-                new Consumer<SyncRtSurfaceTransactionApplier>() {
-                    @Override
-                    public void accept(SyncRtSurfaceTransactionApplier applier) {
-                        callback.accept(new SyncRtSurfaceTransactionApplierCompat(applier));
-                    }
-                });
+        if (targetView == null) {
+            // No target view, no applier
+            callback.accept(null);
+        } else if (targetView.getViewRootImpl() != null) {
+            // Already attached, we're good to go
+            callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
+        } else {
+            // Haven't been attached before we can get the view root
+            targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    targetView.removeOnAttachStateChangeListener(this);
+                    callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    // Do nothing
+                }
+            });
+        }
     }
 
     public static class SurfaceParams {
 
-        private final SyncRtSurfaceTransactionApplier.SurfaceParams mParams;
-
         /**
          * Constructs surface parameters to be applied when the current view state gets pushed to
          * RenderThread.
@@ -96,8 +129,19 @@
          */
         public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
                 Rect windowCrop, int layer, float cornerRadius) {
-            mParams = new SyncRtSurfaceTransactionApplier.SurfaceParams(surface.mSurfaceControl,
-                    alpha, matrix, windowCrop, layer, cornerRadius);
+            this.surface = surface;
+            this.alpha = alpha;
+            this.matrix = new Matrix(matrix);
+            this.windowCrop = new Rect(windowCrop);
+            this.layer = layer;
+            this.cornerRadius = cornerRadius;
         }
+
+        public final SurfaceControlCompat surface;
+        public final float alpha;
+        final float cornerRadius;
+        public final Matrix matrix;
+        public final Rect windowCrop;
+        public final int layer;
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index 2aba3fa..af32f48 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -82,6 +82,11 @@
         return this;
     }
 
+    public TransactionCompat setCornerRadius(SurfaceControlCompat surfaceControl, float radius) {
+        mTransaction.setCornerRadius(surfaceControl.mSurfaceControl, radius);
+        return this;
+    }
+
     public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl,
             IBinder handle, long frameNumber) {
         mTransaction.deferTransactionUntil(surfaceControl.mSurfaceControl, handle, frameNumber);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 41e9eba..a055950 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -46,6 +46,7 @@
     protected View mEcaView;
     protected boolean mEnableHaptics;
     private boolean mDismissing;
+    protected boolean mResumed;
     private CountDownTimer mCountdownTimer = null;
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
@@ -263,6 +264,8 @@
 
     @Override
     public void onPause() {
+        mResumed = false;
+
         if (mCountdownTimer != null) {
             mCountdownTimer.cancel();
             mCountdownTimer = null;
@@ -276,6 +279,7 @@
 
     @Override
     public void onResume(int reason) {
+        mResumed = true;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 570d351..1aff394 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,9 +1,15 @@
 package com.android.keyguard;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -12,18 +18,29 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.keyguard.clock.BubbleClockController;
+import com.android.keyguard.clock.StretchAnalogClockController;
+import com.android.keyguard.clock.TypeClockController;
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionController.Extension;
 
 import java.util.Objects;
 import java.util.TimeZone;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
 public class KeyguardClockSwitch extends RelativeLayout {
+
+    private LayoutInflater mLayoutInflater;
+
+    private final ContentResolver mContentResolver;
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -45,43 +62,48 @@
      * or not to show it below the alternate clock.
      */
     private View mKeyguardStatusArea;
+    /**
+     * Used to select between plugin or default implementations of ClockPlugin interface.
+     */
+    private Extension<ClockPlugin> mClockExtension;
+    /**
+     * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
+     */
+    private final Consumer<ClockPlugin> mClockPluginConsumer = plugin -> setClockPlugin(plugin);
+    /**
+     * Maintain state so that a newly connected plugin can be initialized.
+     */
+    private float mDarkAmount;
 
-    private final PluginListener<ClockPlugin> mClockPluginListener =
-            new PluginListener<ClockPlugin>() {
+    private final StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
                 @Override
-                public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
-                    disconnectPlugin();
-                    View smallClockView = plugin.getView();
-                    if (smallClockView != null) {
-                        // For now, assume that the most recently connected plugin is the
-                        // selected clock face. In the future, the user should be able to
-                        // pick a clock face from the available plugins.
-                        mSmallClockFrame.addView(smallClockView, -1,
-                                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                                        ViewGroup.LayoutParams.WRAP_CONTENT));
-                        initPluginParams();
-                        mClockView.setVisibility(View.GONE);
+                public void onStateChanged(int newState) {
+                    if (mBigClockContainer == null) {
+                        return;
                     }
-                    View bigClockView = plugin.getBigClockView();
-                    if (bigClockView != null && mBigClockContainer != null) {
-                        mBigClockContainer.addView(bigClockView);
-                        mBigClockContainer.setVisibility(View.VISIBLE);
-                    }
-                    if (!plugin.shouldShowStatusArea()) {
-                        mKeyguardStatusArea.setVisibility(View.GONE);
-                    }
-                    mClockPlugin = plugin;
-                }
-
-                @Override
-                public void onPluginDisconnected(ClockPlugin plugin) {
-                    if (Objects.equals(plugin, mClockPlugin)) {
-                        disconnectPlugin();
-                        mClockView.setVisibility(View.VISIBLE);
-                        mKeyguardStatusArea.setVisibility(View.VISIBLE);
+                    if (newState == StatusBarState.SHADE) {
+                        if (mBigClockContainer.getVisibility() == View.VISIBLE) {
+                            mBigClockContainer.setVisibility(View.INVISIBLE);
+                        }
+                    } else {
+                        if (mBigClockContainer.getVisibility() == View.INVISIBLE) {
+                            mBigClockContainer.setVisibility(View.VISIBLE);
+                        }
                     }
                 }
-            };
+    };
+
+    private final ContentObserver mContentObserver =
+            new ContentObserver(new Handler(Looper.getMainLooper())) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+                    if (mClockExtension != null) {
+                        mClockExtension.reload();
+                    }
+                }
+    };
 
     public KeyguardClockSwitch(Context context) {
         this(context, null);
@@ -89,6 +111,15 @@
 
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mLayoutInflater = LayoutInflater.from(context);
+        mContentResolver = context.getContentResolver();
+    }
+
+    /**
+     * Returns if this view is presenting a custom clock, or the default implementation.
+     */
+    public boolean hasCustomClock() {
+        return mClockPlugin != null;
     }
 
     @Override
@@ -102,14 +133,88 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(PluginManager.class).addPluginListener(mClockPluginListener,
-                ClockPlugin.class);
+        mClockExtension = Dependency.get(ExtensionController.class).newExtension(ClockPlugin.class)
+                .withPlugin(ClockPlugin.class)
+                .withCallback(mClockPluginConsumer)
+                // Using withDefault even though this isn't the default as a workaround.
+                // ExtensionBulider doesn't provide the ability to supply a ClockPlugin
+                // instance based off of the value of a setting. Since multiple "default"
+                // can be provided, using a supplier that changes the settings value.
+                // A null return will cause Extension#reload to look at the next "default"
+                // supplier.
+                .withDefault(
+                        new SettingsGattedSupplier(
+                                mContentResolver,
+                                Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                                BubbleClockController.class.getName(),
+                                () -> BubbleClockController.build(mLayoutInflater)))
+                .withDefault(
+                        new SettingsGattedSupplier(
+                                mContentResolver,
+                                Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                                StretchAnalogClockController.class.getName(),
+                                () -> StretchAnalogClockController.build(mLayoutInflater)))
+                .withDefault(
+                        new SettingsGattedSupplier(
+                                mContentResolver,
+                                Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                                TypeClockController.class.getName(),
+                                () -> TypeClockController.build(mLayoutInflater)))
+                .build();
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
+                false, mContentObserver);
+        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        Dependency.get(PluginManager.class).removePluginListener(mClockPluginListener);
+        mClockExtension.destroy();
+        mContentResolver.unregisterContentObserver(mContentObserver);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
+    }
+
+    private void setClockPlugin(ClockPlugin plugin) {
+        // Disconnect from existing plugin.
+        if (mClockPlugin != null) {
+            View smallClockView = mClockPlugin.getView();
+            if (smallClockView != null && smallClockView.getParent() == mSmallClockFrame) {
+                mSmallClockFrame.removeView(smallClockView);
+            }
+            if (mBigClockContainer != null) {
+                mBigClockContainer.removeAllViews();
+                mBigClockContainer.setVisibility(View.GONE);
+            }
+            mClockPlugin = null;
+        }
+        if (plugin == null) {
+            mClockView.setVisibility(View.VISIBLE);
+            mKeyguardStatusArea.setVisibility(View.VISIBLE);
+            return;
+        }
+        // Attach small and big clock views to hierarchy.
+        View smallClockView = plugin.getView();
+        if (smallClockView != null) {
+            mSmallClockFrame.addView(smallClockView, -1,
+                    new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.WRAP_CONTENT));
+            mClockView.setVisibility(View.GONE);
+        }
+        View bigClockView = plugin.getBigClockView();
+        if (bigClockView != null && mBigClockContainer != null) {
+            mBigClockContainer.addView(bigClockView);
+            mBigClockContainer.setVisibility(View.VISIBLE);
+        }
+        // Hide default clock.
+        if (!plugin.shouldShowStatusArea()) {
+            mKeyguardStatusArea.setVisibility(View.GONE);
+        }
+        // Initialize plugin parameters.
+        mClockPlugin = plugin;
+        mClockPlugin.setStyle(getPaint().getStyle());
+        mClockPlugin.setTextColor(getCurrentTextColor());
+        mClockPlugin.setDarkAmount(mDarkAmount);
     }
 
     /**
@@ -150,10 +255,6 @@
         mClockView.setShowCurrentUserTime(showCurrentUserTime);
     }
 
-    public void setElegantTextHeight(boolean elegant) {
-        mClockView.setElegantTextHeight(elegant);
-    }
-
     public void setTextSize(int unit, float size) {
         mClockView.setTextSize(unit, size);
     }
@@ -171,6 +272,7 @@
      * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
      */
     public void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
         if (mClockPlugin != null) {
             mClockPlugin.setDarkAmount(darkAmount);
         }
@@ -210,32 +312,53 @@
         }
     }
 
-    /**
-     * When plugin changes, set all kept parameters into newer plugin.
-     */
-    private void initPluginParams() {
-        if (mClockPlugin != null) {
-            mClockPlugin.setStyle(getPaint().getStyle());
-            mClockPlugin.setTextColor(getCurrentTextColor());
-        }
-    }
-
-    private void disconnectPlugin() {
-        if (mClockPlugin != null) {
-            View smallClockView = mClockPlugin.getView();
-            if (smallClockView != null) {
-                mSmallClockFrame.removeView(smallClockView);
-            }
-            if (mBigClockContainer != null) {
-                mBigClockContainer.removeAllViews();
-                mBigClockContainer.setVisibility(View.GONE);
-            }
-            mClockPlugin = null;
-        }
+    @VisibleForTesting (otherwise = VisibleForTesting.NONE)
+    Consumer<ClockPlugin> getClockPluginConsumer() {
+        return mClockPluginConsumer;
     }
 
     @VisibleForTesting (otherwise = VisibleForTesting.NONE)
-    PluginListener getClockPluginListener() {
-        return mClockPluginListener;
+    StatusBarStateController.StateListener getStateListener() {
+        return mStateListener;
+    }
+
+    /**
+     * Supplier that only gets an instance when a settings value matches expected value.
+     */
+    private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {
+
+        private final ContentResolver mContentResolver;
+        private final String mKey;
+        private final String mValue;
+        private final Supplier<ClockPlugin> mSupplier;
+
+        /**
+         * Constructs a supplier that changes secure setting key against value.
+         *
+         * @param contentResolver Used to look up settings value.
+         * @param key Settings key.
+         * @param value If the setting matches this values that get supplies a ClockPlugin
+         *        instance.
+         * @param supplier Supplier of ClockPlugin instance, only used if the setting
+         *        matches value.
+         */
+        SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
+                Supplier<ClockPlugin> supplier) {
+            mContentResolver = contentResolver;
+            mKey = key;
+            mValue = value;
+            mSupplier = supplier;
+        }
+
+        /**
+         * Returns null if the settings value doesn't match the expected value.
+         *
+         * A null return causes Extension#reload to skip this supplier and move to the next.
+         */
+        @Override
+        public ClockPlugin get() {
+            final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
+            return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 41afa9a..3296c10 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -81,6 +81,11 @@
     protected void resetState() {
         mSecurityMessageDisplay.setMessage("");
         final boolean wasDisabled = mPasswordEntry.isEnabled();
+        // Don't set enabled password entry & showSoftInput when PasswordEntry is invisible or in
+        // pausing stage.
+        if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
+            return;
+        }
         setPasswordEntryEnabled(true);
         setPasswordEntryInputEnabled(true);
         if (wasDisabled) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 669e6ff..bac7844 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -90,7 +90,7 @@
      */
     private Runnable mContentChangeListener;
     private Slice mSlice;
-    private boolean mPulsing;
+    private boolean mHasHeader;
 
     public KeyguardSliceView(Context context) {
         this(context, null, 0);
@@ -150,10 +150,18 @@
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
+    /**
+     * Returns whether the current visible slice has a title/header.
+     */
+    public boolean hasHeader() {
+        return mHasHeader;
+    }
+
     private void showSlice() {
         Trace.beginSection("KeyguardSliceView#showSlice");
-        if (mPulsing || mSlice == null) {
+        if (mSlice == null) {
             mRow.setVisibility(GONE);
+            mHasHeader = false;
             if (mContentChangeListener != null) {
                 mContentChangeListener.run();
             }
@@ -162,7 +170,7 @@
 
         ListContent lc = new ListContent(getContext(), mSlice);
         SliceContent headerContent = lc.getHeader();
-        boolean hasHeader = headerContent != null
+        mHasHeader = headerContent != null
                 && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
         List<SliceContent> subItems = new ArrayList<>();
         for (int i = 0; i < lc.getRowItems().size(); i++) {
@@ -177,7 +185,7 @@
         mClickActions.clear();
         final int subItemsCount = subItems.size();
         final int blendedColor = getTextColor();
-        final int startIndex = hasHeader ? 1 : 0; // First item is header; skip it
+        final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
         mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
         for (int i = startIndex; i < subItemsCount; i++) {
             RowContent rc = (RowContent) subItems.get(i);
@@ -189,7 +197,7 @@
                 button = new KeyguardSliceButton(mContext);
                 button.setTextColor(blendedColor);
                 button.setTag(itemTag);
-                final int viewIndex = i - (hasHeader ? 1 : 0);
+                final int viewIndex = i - (mHasHeader ? 1 : 0);
                 mRow.addView(button, viewIndex);
             }
 
@@ -234,18 +242,6 @@
         Trace.endSection();
     }
 
-    public void setPulsing(boolean pulsing, boolean animate) {
-        mPulsing = pulsing;
-        LayoutTransition transition = getLayoutTransition();
-        if (!animate) {
-            setLayoutTransition(null);
-        }
-        showSlice();
-        if (!animate) {
-            setLayoutTransition(transition);
-        }
-    }
-
     public void setDarkAmount(float darkAmount) {
         mDarkAmount = darkAmount;
         mRow.setDarkAmount(darkAmount);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index c6f1726..bb549ad 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -72,7 +72,6 @@
 
     private ArraySet<View> mVisibleInDoze;
     private boolean mPulsing;
-    private boolean mWasPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
     private int mLastLayoutHeight;
@@ -141,6 +140,13 @@
         onDensityOrFontScaleChanged();
     }
 
+    /**
+     * If we're presenting a custom clock of just the default one.
+     */
+    public boolean hasCustomClock() {
+        return mClockView.hasCustomClock();
+    }
+
     private void setEnableMarquee(boolean enabled) {
         if (DEBUG) Log.v(TAG, "Schedule setEnableMarquee: " + (enabled ? "Enable" : "Disable"));
         if (enabled) {
@@ -195,10 +201,6 @@
         updateOwnerInfo();
         updateLogoutView();
         updateDark();
-
-        // Disable elegant text height because our fancy colon makes the ymin value huge for no
-        // reason.
-        mClockView.setElegantTextHeight(false);
     }
 
     /**
@@ -207,7 +209,7 @@
     private void onSliceContentChanged() {
         LinearLayout.LayoutParams layoutParams =
                 (LinearLayout.LayoutParams) mClockView.getLayoutParams();
-        layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0;
+        layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0;
         mClockView.setLayoutParams(layoutParams);
     }
 
@@ -217,16 +219,16 @@
     @Override
     public void onLayoutChange(View view, int left, int top, int right, int bottom,
             int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        int heightOffset = mPulsing || mWasPulsing ? 0 : getHeight() - mLastLayoutHeight;
+        boolean smallClock = mKeyguardSlice.hasHeader();
+        int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight;
         long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
-        long delay = mPulsing || mWasPulsing ? 0 : duration / 4;
-        mWasPulsing = false;
+        long delay = smallClock ? 0 : duration / 4;
 
         boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
                 && mKeyguardSlice.getLayoutTransition().isRunning();
         if (view == mClockView) {
-            float clockScale = mPulsing ? mSmallClockScale : 1;
-            Paint.Style style = mPulsing ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
+            float clockScale = smallClock ? mSmallClockScale : 1;
+            Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
             mClockView.animate().cancel();
             if (shouldAnimate) {
                 mClockView.setY(oldTop + heightOffset);
@@ -431,15 +433,11 @@
         }
     }
 
-    public void setPulsing(boolean pulsing, boolean animate) {
+    public void setPulsing(boolean pulsing) {
         if (mPulsing == pulsing) {
             return;
         }
-        if (mPulsing) {
-            mWasPulsing = true;
-        }
         mPulsing = pulsing;
-        mKeyguardSlice.setPulsing(pulsing, animate);
         updateDozeVisibleViews();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
new file mode 100644
index 0000000..db6127f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.graphics.Paint.Style;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextClock;
+
+import com.android.keyguard.R;
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.TimeZone;
+
+/**
+ * Controller for Bubble clock that can appear on lock screen and AOD.
+ */
+public class BubbleClockController implements ClockPlugin {
+
+    /**
+     * Custom clock shown on AOD screen and behind stack scroller on lock.
+     */
+    private View mView;
+    private TextClock mDigitalClock;
+    private ImageClock mAnalogClock;
+
+    /**
+     * Small clock shown on lock screen above stack scroller.
+     */
+    private View mLockClockContainer;
+    private TextClock mLockClock;
+
+    /**
+     * Controller for transition to dark state.
+     */
+    private CrossFadeDarkController mDarkController;
+
+    private BubbleClockController() { }
+
+    /**
+     * Create a BubbleClockController instance.
+     *
+     * @param layoutInflater Inflater used to inflate custom clock views.
+     */
+    public static BubbleClockController build(LayoutInflater layoutInflater) {
+        BubbleClockController controller = new BubbleClockController();
+        controller.createViews(layoutInflater);
+        return controller;
+    }
+
+    private void createViews(LayoutInflater layoutInflater) {
+        mView = layoutInflater.inflate(R.layout.bubble_clock, null);
+        mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock);
+        mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock);
+
+        mLockClockContainer = layoutInflater.inflate(R.layout.digital_clock, null);
+        mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock);
+        mLockClock.setVisibility(View.GONE);
+
+        mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock);
+    }
+
+    @Override
+    public View getView() {
+        return mLockClockContainer;
+    }
+
+    @Override
+    public View getBigClockView() {
+        return mView;
+    }
+
+    @Override
+    public void setStyle(Style style) {}
+
+    @Override
+    public void setTextColor(int color) {
+        mLockClock.setTextColor(color);
+        mDigitalClock.setTextColor(color);
+    }
+
+    @Override
+    public void dozeTimeTick() {
+        mAnalogClock.onTimeChanged();
+    }
+
+    @Override
+    public void setDarkAmount(float darkAmount) {
+        mDarkController.setDarkAmount(darkAmount);
+    }
+
+    @Override
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mAnalogClock.onTimeZoneChanged(timeZone);
+    }
+
+    @Override
+    public boolean shouldShowStatusArea() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
new file mode 100644
index 0000000..3591dc8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.keyguard.R;
+
+/**
+ * Positions clock faces (analog, digital, typographic) and handles pixel shifting
+ * to prevent screen burn-in.
+ */
+public class ClockLayout extends FrameLayout {
+
+    /**
+     * Clock face views.
+     */
+    private View mDigitalClock;
+    private View mAnalogClock;
+    private View mTypeClock;
+
+    /**
+     * Pixel shifting amplitidues used to prevent screen burn-in.
+     */
+    private int mBurnInPreventionOffsetX;
+    private int mBurnInPreventionOffsetY;
+
+    public ClockLayout(Context context) {
+        this(context, null);
+    }
+
+    public ClockLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDigitalClock = findViewById(R.id.digital_clock);
+        mAnalogClock = findViewById(R.id.analog_clock);
+        mTypeClock = findViewById(R.id.type_clock);
+
+        // Get pixel shifting X, Y amplitudes from resources.
+        Resources resources = getResources();
+        mBurnInPreventionOffsetX = resources.getDimensionPixelSize(
+            R.dimen.burn_in_prevention_offset_x);
+        mBurnInPreventionOffsetY = resources.getDimensionPixelSize(
+            R.dimen.burn_in_prevention_offset_y);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX, true);
+        final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY, false);
+
+        // Put digital clock in two left corner of the screen.
+        if (mDigitalClock != null) {
+            mDigitalClock.setX(0.1f * getWidth() + offsetX);
+            mDigitalClock.setY(0.1f * getHeight() + offsetY);
+        }
+
+        // Put the analog clock in the middle of the screen.
+        if (mAnalogClock != null) {
+            mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth()))
+                    + offsetX);
+            mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight()))
+                    + offsetY);
+        }
+
+        // Put the typographic clock part way down the screen.
+        if (mTypeClock != null) {
+            mTypeClock.setX(offsetX);
+            mTypeClock.setY(0.2f * getHeight() + offsetY);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java
new file mode 100644
index 0000000..3c3f475
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.view.View;
+
+/**
+ * Controls transition to dark state by cross fading between views.
+ */
+final class CrossFadeDarkController {
+
+    private final View mFadeInView;
+    private final View mFadeOutView;
+
+    /**
+     * Creates a new controller that fades between views.
+     *
+     * @param fadeInView View to fade in when transitioning to AOD.
+     * @param fadeOutView View to fade out when transitioning to AOD.
+     */
+    CrossFadeDarkController(View fadeInView, View fadeOutView) {
+        mFadeInView = fadeInView;
+        mFadeOutView = fadeOutView;
+    }
+
+    /**
+     * Sets the amount the system has transitioned to the dark state.
+     *
+     * @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen.
+     */
+    void setDarkAmount(float darkAmount) {
+        mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f));
+        if (darkAmount == 0f) {
+            mFadeInView.setVisibility(View.GONE);
+        } else {
+            if (mFadeInView.getVisibility() == View.GONE) {
+                mFadeInView.setVisibility(View.VISIBLE);
+            }
+        }
+        mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount));
+        if (darkAmount == 1f) {
+            mFadeOutView.setVisibility(View.GONE);
+        } else {
+            if (mFadeOutView.getVisibility() == View.GONE) {
+                mFadeOutView.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
new file mode 100644
index 0000000..2c709e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.keyguard.R;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Clock composed of two images that rotate with the time.
+ *
+ * The images are the clock hands. ImageClock expects two child ImageViews
+ * with ids hour_hand and minute_hand.
+ */
+public class ImageClock extends FrameLayout {
+
+    private ImageView mHourHand;
+    private ImageView mMinuteHand;
+    private Calendar mTime;
+    private String mDescFormat;
+    private TimeZone mTimeZone;
+
+    public ImageClock(Context context) {
+        this(context, null);
+    }
+
+    public ImageClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mTime = Calendar.getInstance();
+        mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
+    }
+
+    /**
+     * Call when the time changes to update the rotation of the clock hands.
+     */
+    public void onTimeChanged() {
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        final float hourAngle = mTime.get(Calendar.HOUR) * 30f;
+        mHourHand.setRotation(hourAngle);
+        final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f;
+        mMinuteHand.setRotation(minuteAngle);
+        setContentDescription(DateFormat.format(mDescFormat, mTime));
+        invalidate();
+    }
+
+    /**
+     * Call when the time zone has changed to update clock hands.
+     *
+     * @param timeZone The updated time zone that will be used.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mTimeZone = timeZone;
+        mTime.setTimeZone(timeZone);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mHourHand = findViewById(R.id.hour_hand);
+        mMinuteHand = findViewById(R.id.minute_hand);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        onTimeChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
new file mode 100644
index 0000000..87347545
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Analog clock where the minute hand extends off of the screen.
+ */
+public class StretchAnalogClock extends View {
+
+    private static final int DEFAULT_COLOR = Color.parseColor("#F5C983");
+    private static final float HOUR_STROKE_WIDTH = 60f;
+    private static final float MINUTE_STROKE_WIDTH = 20f;
+    private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 80f;
+
+    private final Paint mHourPaint = new Paint();
+    private final Paint mMinutePaint = new Paint();
+    private Calendar mTime;
+    private TimeZone mTimeZone;
+
+    public StretchAnalogClock(Context context) {
+        this(context, null);
+    }
+
+    public StretchAnalogClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    /**
+     * Call when the time changes to update the clock hands.
+     */
+    public void onTimeChanged() {
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        invalidate();
+    }
+
+    /**
+     * Call when the time zone has changed to update clock hands.
+     *
+     * @param timeZone The updated time zone that will be used.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mTime.setTimeZone(timeZone);
+    }
+
+    /**
+     * Set the color of the minute hand.
+     */
+    public void setMinuteHandColor(int color) {
+        mMinutePaint.setColor(color);
+        invalidate();
+    }
+
+    private void init() {
+        mHourPaint.setColor(DEFAULT_COLOR);
+        mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
+        mHourPaint.setAntiAlias(true);
+        mHourPaint.setStrokeCap(Paint.Cap.ROUND);
+
+        mMinutePaint.setColor(Color.WHITE);
+        mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
+        mMinutePaint.setAntiAlias(true);
+        mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final float centerX = getWidth() / 2f;
+        final float centerY = getHeight() / 2f;
+
+        final float minutesRotation = mTime.get(Calendar.MINUTE) * 6f;
+        final float hoursRotation = (mTime.get(Calendar.HOUR) * 30);
+
+        // Compute length of clock hands. Hour hand is 60% the length from center to edge
+        // and minute hand is twice the length to make sure it extends past screen edge.
+        double sMinuteHandLengthFactor = Math.sin(2d * Math.PI * minutesRotation / 360d);
+        float sMinuteHandLength = (float) (2d * (centerY + (centerX - centerY)
+                * sMinuteHandLengthFactor * sMinuteHandLengthFactor));
+        double sHourHandLengthFactor = Math.sin(2d * Math.PI * hoursRotation / 360d);
+        float sHourHandLength = (float) (0.6d * (centerY + (centerX - centerY)
+                * sHourHandLengthFactor * sHourHandLengthFactor));
+
+        canvas.save();
+
+        canvas.rotate(minutesRotation, centerX, centerY);
+        canvas.drawLine(
+                centerX,
+                centerY + CENTER_GAP_AND_CIRCLE_RADIUS,
+                centerX,
+                centerY - sMinuteHandLength,
+                mMinutePaint);
+
+        canvas.rotate(hoursRotation - minutesRotation, centerX, centerY);
+        canvas.drawLine(
+                centerX,
+                centerY + CENTER_GAP_AND_CIRCLE_RADIUS,
+                centerX,
+                centerY - sHourHandLength,
+                mHourPaint);
+
+        canvas.restore();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        onTimeChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
new file mode 100644
index 0000000..0a39158
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.graphics.Paint.Style;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextClock;
+
+import com.android.keyguard.R;
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.TimeZone;
+
+/**
+ * Controller for Stretch clock that can appear on lock screen and AOD.
+ */
+public class StretchAnalogClockController implements ClockPlugin {
+
+    /**
+     * Custom clock shown on AOD screen and behind stack scroller on lock.
+     */
+    private View mBigClockView;
+    private TextClock mDigitalClock;
+    private StretchAnalogClock mAnalogClock;
+
+    /**
+     * Small clock shown on lock screen above stack scroller.
+     */
+    private View mView;
+    private TextClock mLockClock;
+
+    /**
+     * Controller for transition to dark state.
+     */
+    private CrossFadeDarkController mDarkController;
+
+    private StretchAnalogClockController() { }
+
+    /**
+     * Create a BubbleClockController instance.
+     *
+     * @param layoutInflater Inflater used to inflate custom clock views.
+     */
+    public static StretchAnalogClockController build(LayoutInflater layoutInflater) {
+        StretchAnalogClockController controller = new StretchAnalogClockController();
+        controller.createViews(layoutInflater);
+        return controller;
+    }
+
+    private void createViews(LayoutInflater layoutInflater) {
+        mBigClockView = layoutInflater.inflate(R.layout.stretchanalog_clock, null);
+        mAnalogClock = mBigClockView.findViewById(R.id.analog_clock);
+        mDigitalClock = mBigClockView.findViewById(R.id.digital_clock);
+
+        mView = layoutInflater.inflate(R.layout.digital_clock, null);
+        mLockClock = mView.findViewById(R.id.lock_screen_clock);
+        mLockClock.setVisibility(View.GONE);
+
+        mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock);
+    }
+
+    @Override
+    public View getView() {
+        return mView;
+    }
+
+    @Override
+    public View getBigClockView() {
+        return mBigClockView;
+    }
+
+    @Override
+    public void setStyle(Style style) {}
+
+    @Override
+    public void setTextColor(int color) {
+        mLockClock.setTextColor(color);
+        mDigitalClock.setTextColor(color);
+        mAnalogClock.setMinuteHandColor(color);
+    }
+
+    @Override
+    public void dozeTimeTick() {
+        mAnalogClock.onTimeChanged();
+    }
+
+    @Override
+    public void setDarkAmount(float darkAmount) {
+        mDarkController.setDarkAmount(darkAmount);
+    }
+
+    @Override
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mAnalogClock.onTimeZoneChanged(timeZone);
+    }
+
+    @Override
+    public boolean shouldShowStatusArea() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
new file mode 100644
index 0000000..17d929d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.graphics.Paint.Style;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.keyguard.R;
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.TimeZone;
+
+/**
+ * Plugin for a custom Typographic clock face that displays the time in words.
+ */
+public class TypeClockController implements ClockPlugin {
+
+    /**
+     * Custom clock shown on AOD screen and behind stack scroller on lock.
+     */
+    private View mView;
+    private TypographicClock mTypeClock;
+
+    /**
+     * Small clock shown on lock screen above stack scroller.
+     */
+    private View mLockClockContainer;
+
+    /**
+     * Controller for transition into dark state.
+     */
+    private CrossFadeDarkController mDarkController;
+
+    private TypeClockController() {}
+
+    /**
+     * Create a TypeClockController instance.
+     *
+     * @param inflater Inflater used to inflate custom clock views.
+     */
+    public static TypeClockController build(LayoutInflater inflater) {
+        TypeClockController controller = new TypeClockController();
+        controller.createViews(inflater);
+        return controller;
+    }
+
+    private void createViews(LayoutInflater inflater) {
+        mView = inflater.inflate(R.layout.type_clock, null);
+        mTypeClock = mView.findViewById(R.id.type_clock);
+
+        // For now, this view is used to hide the default digital clock.
+        // Need better transition to lock screen.
+        mLockClockContainer = inflater.inflate(R.layout.digital_clock, null);
+        mLockClockContainer.setVisibility(View.GONE);
+    }
+
+    @Override
+    public View getView() {
+        return mLockClockContainer;
+    }
+
+    @Override
+    public View getBigClockView() {
+        return mView;
+    }
+
+    @Override
+    public void setStyle(Style style) {}
+
+    @Override
+    public void setTextColor(int color) {
+        mTypeClock.setTextColor(color);
+    }
+
+    @Override
+    public void dozeTimeTick() {
+        mTypeClock.onTimeChanged();
+    }
+
+    @Override
+    public void setDarkAmount(float darkAmount) {}
+
+    @Override
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mTypeClock.onTimeZoneChanged(timeZone);
+    }
+
+    @Override
+    public boolean shouldShowStatusArea() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
new file mode 100644
index 0000000..5f9da3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.keyguard.R;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Clock that presents the time in words.
+ */
+public class TypographicClock extends LinearLayout {
+
+    private final String[] mHours;
+    private final String[] mMinutes;
+    private TextView mHeaderText;
+    private TextView mHourText;
+    private TextView mMinuteText;
+    private Calendar mTime;
+    private String mDescFormat;
+    private TimeZone mTimeZone;
+
+    public TypographicClock(Context context) {
+        this(context, null);
+    }
+
+    public TypographicClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TypographicClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mTime = Calendar.getInstance();
+        mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
+        Resources res = context.getResources();
+        mHours = res.getStringArray(R.array.type_clock_hours);
+        mMinutes = res.getStringArray(R.array.type_clock_minutes);
+    }
+
+    /**
+     * Call when the time changes to update the text of the time.
+     */
+    public void onTimeChanged() {
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        setContentDescription(DateFormat.format(mDescFormat, mTime));
+        final int hour = mTime.get(Calendar.HOUR);
+        mHourText.setText(mHours[hour % 12]);
+        final int minute = mTime.get(Calendar.MINUTE);
+        mMinuteText.setText(mMinutes[minute % 60]);
+        invalidate();
+    }
+
+    /**
+     * Call when the time zone has changed to update clock time.
+     *
+     * @param timeZone The updated time zone that will be used.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mTimeZone = timeZone;
+        mTime.setTimeZone(timeZone);
+    }
+
+    /**
+     * Set the color of the text used to display the time.
+     *
+     * This is necessary when the wallpaper shown behind the clock on the
+     * lock screen changes.
+     */
+    public void setTextColor(int color) {
+        mHourText.setTextColor(color);
+        mMinuteText.setTextColor(color);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mHeaderText = findViewById(R.id.header);
+        mHourText = findViewById(R.id.hour);
+        mMinuteText = findViewById(R.id.minute);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        onTimeChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 6864ea1..2006794 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -40,7 +40,6 @@
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -56,7 +55,6 @@
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.IconLogger;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.Utils.DisableStateTracker;
@@ -71,11 +69,12 @@
 
 
     @Retention(SOURCE)
-    @IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF})
+    @IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF, MODE_ESTIMATE})
     public @interface BatteryPercentMode {}
     public static final int MODE_DEFAULT = 0;
     public static final int MODE_ON = 1;
     public static final int MODE_OFF = 2;
+    public static final int MODE_ESTIMATE = 3;
 
     private final BatteryMeterDrawableBase mDrawable;
     private final String mSlotBattery;
@@ -93,6 +92,7 @@
     // Some places may need to show the battery conditionally, and not obey the tuner
     private boolean mIgnoreTunerUpdates;
     private boolean mIsSubscribedForTunerUpdates;
+    private boolean mCharging;
 
     private int mDarkModeBackgroundColor;
     private int mDarkModeFillColor;
@@ -276,9 +276,6 @@
     public void onTuningChanged(String key, String newValue) {
         if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
             ArraySet<String> icons = StatusBarIconController.getIconBlacklist(newValue);
-            boolean hidden = icons.contains(mSlotBattery);
-            Dependency.get(IconLogger.class).onIconVisibility(mSlotBattery, !hidden);
-            setVisibility(hidden ? View.GONE : View.VISIBLE);
         }
     }
 
@@ -308,6 +305,7 @@
     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
         mDrawable.setBatteryLevel(level);
         mDrawable.setCharging(pluggedIn);
+        mCharging = pluggedIn;
         mLevel = level;
         updatePercentText();
         setContentDescription(
@@ -337,9 +335,19 @@
     }
 
     private void updatePercentText() {
+        if (mBatteryController == null) {
+            return;
+        }
+
         if (mBatteryPercentView != null) {
-            mBatteryPercentView.setText(
-                    NumberFormat.getPercentInstance().format(mLevel / 100f));
+            if (mShowPercentMode == MODE_ESTIMATE && !mCharging) {
+                mBatteryController.getEstimatedTimeRemainingString((String estimate) -> {
+                    mBatteryPercentView.setText(estimate);
+                });
+            } else {
+                mBatteryPercentView.setText(
+                        NumberFormat.getPercentInstance().format(mLevel / 100f));
+            }
         }
     }
 
@@ -350,7 +358,7 @@
                 SHOW_BATTERY_PERCENT, 0, mUser);
 
         if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
-                || mShowPercentMode == MODE_ON) {
+                || mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE) {
             if (!showing) {
                 mBatteryPercentView = loadPercentView();
                 if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 3cc9bb6..ec6ecc6 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -56,12 +56,12 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -86,7 +86,6 @@
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.IconLogger;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -217,7 +216,6 @@
     @Inject Lazy<LeakDetector> mLeakDetector;
     @Inject Lazy<LeakReporter> mLeakReporter;
     @Inject Lazy<GarbageMonitor> mGarbageMonitor;
-    @Inject Lazy<IconLogger> mIconLogger;
     @Inject Lazy<TunerService> mTunerService;
     @Inject Lazy<StatusBarWindowController> mStatusBarWindowController;
     @Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
@@ -392,8 +390,6 @@
 
         mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
 
-        mProviders.put(IconLogger.class, mIconLogger::get);
-
         mProviders.put(LightBarController.class, mLightBarController::get);
 
         mProviders.put(IWindowManager.class, mIWindowManager::get);
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
index f324a05b..ce9c637 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -46,8 +46,6 @@
 import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.IconLogger;
-import com.android.systemui.statusbar.policy.IconLoggerImpl;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.LocationController;
@@ -133,11 +131,6 @@
     /**
      */
     @Binds
-    public abstract IconLogger provideIconLogger(IconLoggerImpl loggerImpl);
-
-    /**
-     */
-    @Binds
     public abstract CastController provideCastController(CastControllerImpl controllerImpl);
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index dac977a..88e32cb 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -44,8 +44,6 @@
 import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-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.DataSaverController;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -141,12 +139,6 @@
 
     @Singleton
     @Provides
-    public ShadeController provideShadeController(Context context) {
-        return SysUiServiceProvider.getComponent(context, StatusBar.class);
-    }
-
-    @Singleton
-    @Provides
     public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
         return context.getSystemService(SensorPrivacyManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 151a6b0..96b62ac 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -24,9 +24,9 @@
 import android.util.Log;
 
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -49,26 +49,21 @@
         mForegroundServiceController = foregroundServiceController;
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onPendingEntryAdded(NotificationData.Entry entry) {
+            public void onPendingEntryAdded(NotificationEntry entry) {
                 addNotification(entry.notification, entry.importance);
             }
 
             @Override
-            public void onEntryUpdated(NotificationData.Entry entry) {
+            public void onPostEntryUpdated(NotificationEntry entry) {
                 updateNotification(entry.notification, entry.importance);
             }
 
             @Override
             public void onEntryRemoved(
-                    NotificationData.Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
-                if (entry != null && !lifetimeExtended) {
-                    removeNotification(entry.notification);
-                }
+                removeNotification(entry.notification);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 16e869e..e28aa9d 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -37,23 +37,25 @@
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.leak.RotationUtils;
 
-public class HardwareUiLayout extends LinearLayout implements Tunable {
+/**
+ * Layout for placing two containers at a specific physical position on the device, relative to the
+ * device's hardware, regardless of screen rotation.
+ */
+public class HardwareUiLayout extends MultiListLayout implements Tunable {
 
     private static final String EDGE_BLEED = "sysui_hwui_edge_bleed";
     private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider";
     private final int[] mTmp2 = new int[2];
-    private View mList;
-    private View mSeparatedView;
+    private ViewGroup mList;
+    private ViewGroup mSeparatedView;
     private int mOldHeight;
     private boolean mAnimating;
     private AnimatorSet mAnimation;
     private View mDivision;
-    private boolean mHasOutsideTouch;
     private HardwareBgDrawable mListBackground;
     private HardwareBgDrawable mSeparatedViewBackground;
     private Animator mAnimator;
     private boolean mCollapse;
-    private boolean mHasSeparatedButton;
     private int mEndPoint;
     private boolean mEdgeBleed;
     private boolean mRoundedDivider;
@@ -67,6 +69,35 @@
     }
 
     @Override
+    protected ViewGroup getSeparatedView() {
+        return findViewById(com.android.systemui.R.id.separated_button);
+    }
+
+    @Override
+    protected ViewGroup getListView() {
+        return findViewById(android.R.id.list);
+    }
+
+    @Override
+    public void removeAllItems() {
+        if (mList != null) {
+            mList.removeAllViews();
+        }
+        if (mSeparatedView != null) {
+            mSeparatedView.removeAllViews();
+        }
+    }
+
+    @Override
+    public ViewGroup getParentView(boolean separated, int index) {
+        if (separated) {
+            return getSeparatedView();
+        } else {
+            return getListView();
+        }
+    }
+
+    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         updateSettings();
@@ -137,9 +168,9 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (mList == null) {
             if (getChildCount() != 0) {
-                mList = getChildAt(0);
+                mList = getListView();
                 mList.setBackground(mListBackground);
-                mSeparatedView = getChildAt(1);
+                mSeparatedView = getSeparatedView();
                 mSeparatedView.setBackground(mSeparatedViewBackground);
                 updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
                 mOldHeight = mList.getMeasuredHeight();
@@ -187,7 +218,7 @@
         } else {
             rotateLeft();
         }
-        if (mHasSeparatedButton) {
+        if (mHasSeparatedView) {
             if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) {
                 // Separated view has top margin, so seascape separated view need special rotation,
                 // not a full left or right rotation.
@@ -408,8 +439,8 @@
         if (mList == null) return;
         // If got separated button, setRotatedBackground to false,
         // all items won't get white background.
-        mListBackground.setRotatedBackground(mHasSeparatedButton);
-        mSeparatedViewBackground.setRotatedBackground(mHasSeparatedButton);
+        mListBackground.setRotatedBackground(mHasSeparatedView);
+        mSeparatedViewBackground.setRotatedBackground(mHasSeparatedView);
         if (mDivision != null && mDivision.getVisibility() == VISIBLE) {
             int index = mRotatedBackground ? 0 : 1;
             mDivision.getLocationOnScreen(mTmp2);
@@ -460,21 +491,21 @@
             case RotationUtils.ROTATION_LANDSCAPE:
                 defaultTopPadding = getPaddingLeft();
                 viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
-                separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0;
+                separatedViewTopMargin = mHasSeparatedView ? 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;
+                separatedViewTopMargin = mHasSeparatedView ? 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;
+                separatedViewTopMargin = mHasSeparatedView ? params.topMargin : 0;
                 screenHeight = getMeasuredHeight();
                 targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
                 break;
@@ -491,30 +522,10 @@
         return super.getOutlineProvider();
     }
 
-    public void setOutsideTouchListener(OnClickListener onClickListener) {
-        mHasOutsideTouch = true;
-        requestLayout();
-        setOnClickListener(onClickListener);
-        setClickable(true);
-        setFocusable(true);
-    }
-
     public void setCollapse() {
         mCollapse = true;
     }
 
-    public void setHasSeparatedButton(boolean hasSeparatedButton) {
-        mHasSeparatedButton = hasSeparatedButton;
-    }
-
-    public static HardwareUiLayout get(View v) {
-        if (v instanceof HardwareUiLayout) return (HardwareUiLayout) v;
-        if (v.getParent() instanceof View) {
-            return get((View) v.getParent());
-        }
-        return null;
-    }
-
     private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
         if (mHasOutsideTouch || (mList == null)) {
             inoutInfo.setTouchableInsets(
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
new file mode 100644
index 0000000..0c7a9a9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 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 android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * Layout class representing the Global Actions menu which appears when the power button is held.
+ */
+public abstract class MultiListLayout extends LinearLayout {
+    boolean mHasOutsideTouch;
+    boolean mHasSeparatedView;
+
+    int mExpectedSeparatedItemCount;
+    int mExpectedListItemCount;
+
+    public MultiListLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    protected abstract ViewGroup getSeparatedView();
+
+    protected abstract ViewGroup getListView();
+
+    /**
+     * Removes all child items from the separated and list views, if they exist.
+     */
+    public abstract void removeAllItems();
+
+    /**
+     * Get the parent view which will be used to contain the item at the specified index.
+     * @param separated Whether or not this index refers to a position in the separated or list
+     *                  container.
+     * @param index The index of the item within the container.
+     * @return The parent ViewGroup which will be used to contain the specified item
+     * after it has been added to the layout.
+     */
+    public abstract ViewGroup getParentView(boolean separated, int index);
+
+    /**
+     * Sets the divided view, which may have a differently-colored background.
+     */
+    public abstract void setDivisionView(View v);
+
+    /**
+     * Set the view accessibility delegate for the list view container.
+     */
+    public void setListViewAccessibilityDelegate(View.AccessibilityDelegate delegate) {
+        getListView().setAccessibilityDelegate(delegate);
+    }
+
+    protected void setSeparatedViewVisibility(boolean visible) {
+        getSeparatedView().setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * Sets the number of items expected to be rendered in the separated container. This allows the
+     * layout to correctly determine which parent containers will be used for items before they have
+     * beenadded to the layout.
+     * @param count The number of items expected.
+     */
+    public void setExpectedSeparatedItemCount(int count) {
+        mExpectedSeparatedItemCount = count;
+    }
+
+    /**
+     * Sets the number of items expected to be rendered in the list container. This allows the
+     * layout to correctly determine which parent containers will be used for items before they have
+     * beenadded to the layout.
+     * @param count The number of items expected.
+     */
+    public void setExpectedListItemCount(int count) {
+        mExpectedListItemCount = count;
+    }
+
+    /**
+     * Sets whether the separated view should be shown, and handles updating visibility on
+     * that view.
+     */
+    public void setHasSeparatedView(boolean hasSeparatedView) {
+        mHasSeparatedView = hasSeparatedView;
+        setSeparatedViewVisibility(hasSeparatedView);
+    }
+
+    /**
+     * Sets this layout to respond to an outside touch listener.
+     */
+    public void setOutsideTouchListener(OnClickListener onClickListener) {
+        mHasOutsideTouch = true;
+        requestLayout();
+        setOnClickListener(onClickListener);
+        setClickable(true);
+        setFocusable(true);
+    }
+
+    /**
+     * Retrieve the MultiListLayout associated with the given view.
+     */
+    public static MultiListLayout get(View v) {
+        if (v instanceof MultiListLayout) return (MultiListLayout) v;
+        if (v.getParent() instanceof View) {
+            return get((View) v.getParent());
+        }
+        return null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 073516e..f66a57b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -40,9 +40,10 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -51,6 +52,7 @@
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 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.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -135,8 +137,8 @@
     }
 
     public NotificationIconAreaController createNotificationIconAreaController(Context context,
-            StatusBar statusBar) {
-        return new NotificationIconAreaController(context, statusBar);
+            StatusBar statusBar, StatusBarStateController statusBarStateController) {
+        return new NotificationIconAreaController(context, statusBar, statusBarStateController);
     }
 
     public KeyguardIndicationController createKeyguardIndicationController(Context context,
@@ -208,6 +210,12 @@
         return true;
     }
 
+    @Singleton
+    @Provides
+    public ShadeController provideShadeController(Context context) {
+        return SysUiServiceProvider.getComponent(context, StatusBar.class);
+    }
+
     @Module
     protected static class ContextHolder {
         private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
index 9f363f6..7e5b426 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
@@ -25,12 +25,20 @@
     private int mUid;
     private String mPackageName;
     private long mTimeStarted;
+    private String mState;
 
     public AppOpItem(int code, int uid, String packageName, long timeStarted) {
         this.mCode = code;
         this.mUid = uid;
         this.mPackageName = packageName;
         this.mTimeStarted = timeStarted;
+        mState = new StringBuilder()
+                .append("AppOpItem(")
+                .append("Op code=").append(code).append(", ")
+                .append("UID=").append(uid).append(", ")
+                .append("Package name=").append(packageName)
+                .append(")")
+                .toString();
     }
 
     public int getCode() {
@@ -48,4 +56,9 @@
     public long getTimeStarted() {
         return mTimeStarted;
     }
+
+    @Override
+    public String toString() {
+        return mState;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index e3bcb37..c013df3 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -29,7 +29,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -47,7 +50,7 @@
 @Singleton
 public class AppOpsControllerImpl implements AppOpsController,
         AppOpsManager.OnOpActiveChangedListener,
-        AppOpsManager.OnOpNotedListener {
+        AppOpsManager.OnOpNotedListener, Dumpable {
 
     private static final long NOTED_OP_TIME_DELAY_MS = 5000;
     private static final String TAG = "AppOpsControllerImpl";
@@ -271,6 +274,22 @@
         }
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("AppOpsController state:");
+        pw.println("  Active Items:");
+        for (int i = 0; i < mActiveItems.size(); i++) {
+            final AppOpItem item = mActiveItems.get(i);
+            pw.print("    "); pw.println(item.toString());
+        }
+        pw.println("  Noted Items:");
+        for (int i = 0; i < mNotedItems.size(); i++) {
+            final AppOpItem item = mNotedItems.get(i);
+            pw.print("    "); pw.println(item.toString());
+        }
+
+    }
+
     protected final class H extends Handler {
         H(Looper looper) {
             super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 3167b9e..016b8fe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -33,9 +33,6 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.CommandQueue;
 
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
  * BiometricDialogView).
@@ -52,10 +49,8 @@
     private static final int MSG_BUTTON_NEGATIVE = 6;
     private static final int MSG_USER_CANCELED = 7;
     private static final int MSG_BUTTON_POSITIVE = 8;
-    private static final int MSG_BIOMETRIC_SHOW_TRY_AGAIN = 9;
-    private static final int MSG_TRY_AGAIN_PRESSED = 10;
+    private static final int MSG_TRY_AGAIN_PRESSED = 9;
 
-    private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
     private SomeArgs mCurrentDialogArgs;
     private BiometricDialogView mCurrentDialog;
     private WindowManager mWindowManager;
@@ -63,21 +58,22 @@
     private boolean mDialogShowing;
     private Callback mCallback = new Callback();
 
-    private boolean mTryAgainShowing; // No good place to save state before config change :/
-    private boolean mConfirmShowing; // No good place to save state before config change :/
-
     private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch(msg.what) {
                 case MSG_SHOW_DIALOG:
-                    handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */);
+                    handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */,
+                            null /* savedState */);
                     break;
                 case MSG_BIOMETRIC_AUTHENTICATED:
-                    handleBiometricAuthenticated();
+                    handleBiometricAuthenticated((boolean) msg.obj);
                     break;
                 case MSG_BIOMETRIC_HELP:
-                    handleBiometricHelp((String) msg.obj);
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    handleBiometricHelp((String) args.arg1 /* message */,
+                            (boolean) args.arg2 /* requireTryAgain */);
+                    args.recycle();
                     break;
                 case MSG_BIOMETRIC_ERROR:
                     handleBiometricError((String) msg.obj);
@@ -94,9 +90,6 @@
                 case MSG_BUTTON_POSITIVE:
                     handleButtonPositive();
                     break;
-                case MSG_BIOMETRIC_SHOW_TRY_AGAIN:
-                    handleShowTryAgain();
-                    break;
                 case MSG_TRY_AGAIN_PRESSED:
                     handleTryAgainPressed();
                     break;
@@ -137,30 +130,22 @@
 
     @Override
     public void start() {
-        createDialogs();
-
-        if (!mDialogs.isEmpty()) {
+        final PackageManager pm = mContext.getPackageManager();
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
+                || pm.hasSystemFeature(PackageManager.FEATURE_FACE)
+                || pm.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
             getComponent(CommandQueue.class).addCallback(this);
             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         }
     }
 
-    private void createDialogs() {
-        final PackageManager pm = mContext.getPackageManager();
-        mDialogs = new HashMap<>();
-        if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
-            mDialogs.put(BiometricAuthenticator.TYPE_FACE, new FaceDialogView(mContext, mCallback));
-        }
-        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
-            mDialogs.put(BiometricAuthenticator.TYPE_FINGERPRINT,
-                    new FingerprintDialogView(mContext, mCallback));
-        }
-    }
-
     @Override
     public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
             int type, boolean requireConfirmation, int userId) {
-        if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
+        if (DEBUG) {
+            Log.d(TAG, "showBiometricDialog, type: " + type
+                    + ", requireConfirmation: " + requireConfirmation);
+        }
         // Remove these messages as they are part of the previous client
         mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
         mHandler.removeMessages(MSG_BIOMETRIC_HELP);
@@ -176,15 +161,18 @@
     }
 
     @Override
-    public void onBiometricAuthenticated() {
-        if (DEBUG) Log.d(TAG, "onBiometricAuthenticated");
-        mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
+    public void onBiometricAuthenticated(boolean authenticated) {
+        if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated);
+        mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, authenticated).sendToTarget();
     }
 
     @Override
     public void onBiometricHelp(String message) {
         if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
-        mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = message;
+        args.arg2 = false; // requireTryAgain
+        mHandler.obtainMessage(MSG_BIOMETRIC_HELP, args).sendToTarget();
     }
 
     @Override
@@ -199,16 +187,21 @@
         mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
     }
 
-    @Override
-    public void showBiometricTryAgain() {
-        if (DEBUG) Log.d(TAG, "showBiometricTryAgain");
-        mHandler.obtainMessage(MSG_BIOMETRIC_SHOW_TRY_AGAIN).sendToTarget();
-    }
-
-    private void handleShowDialog(SomeArgs args, boolean skipAnimation) {
+    private void handleShowDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
         mCurrentDialogArgs = args;
         final int type = args.argi1;
-        mCurrentDialog = mDialogs.get(type);
+
+        if (type == BiometricAuthenticator.TYPE_FINGERPRINT) {
+            mCurrentDialog = new FingerprintDialogView(mContext, mCallback);
+        } else if (type == BiometricAuthenticator.TYPE_FACE) {
+            mCurrentDialog = new FaceDialogView(mContext, mCallback);
+        } else {
+            Log.e(TAG, "Unsupported type: " + type);
+        }
+
+        if (savedState != null) {
+            mCurrentDialog.restoreState(savedState);
+        }
 
         if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
                 + mCurrentDialog.isAnimatingAway() + " type: " + type);
@@ -224,32 +217,36 @@
         mCurrentDialog.setRequireConfirmation((boolean) args.arg3);
         mCurrentDialog.setUserId(args.argi2);
         mCurrentDialog.setSkipIntro(skipAnimation);
-        mCurrentDialog.setPendingTryAgain(mTryAgainShowing);
-        mCurrentDialog.setPendingConfirm(mConfirmShowing);
         mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
         mDialogShowing = true;
     }
 
-    private void handleBiometricAuthenticated() {
-        if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
+    private void handleBiometricAuthenticated(boolean authenticated) {
+        if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated);
 
-        mCurrentDialog.announceForAccessibility(
-                mContext.getResources()
-                        .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
-        if (mCurrentDialog.requiresConfirmation()) {
-            mConfirmShowing = true;
-            mCurrentDialog.showConfirmationButton(true /* show */);
+        if (authenticated) {
+            mCurrentDialog.announceForAccessibility(
+                    mContext.getResources()
+                            .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
+            if (mCurrentDialog.requiresConfirmation()) {
+                mCurrentDialog.showConfirmationButton(true /* show */);
+            } else {
+                mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
+                mHandler.postDelayed(() -> {
+                    handleHideDialog(false /* userCanceled */);
+                }, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
+            }
         } else {
-            mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
-            mHandler.postDelayed(() -> {
-                handleHideDialog(false /* userCanceled */);
-            }, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
+            handleBiometricHelp(mContext.getResources()
+                    .getString(com.android.internal.R.string.biometric_not_recognized),
+                    true /* requireTryAgain */);
+            mCurrentDialog.showTryAgainButton(true /* show */);
         }
     }
 
-    private void handleBiometricHelp(String message) {
+    private void handleBiometricHelp(String message, boolean requireTryAgain) {
         if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
-        mCurrentDialog.showHelpMessage(message);
+        mCurrentDialog.showHelpMessage(message, requireTryAgain);
     }
 
     private void handleBiometricError(String error) {
@@ -258,7 +255,6 @@
             if (DEBUG) Log.d(TAG, "Dialog already dismissed");
             return;
         }
-        mTryAgainShowing = false;
         mCurrentDialog.showErrorMessage(error);
     }
 
@@ -279,8 +275,6 @@
         }
         mReceiver = null;
         mDialogShowing = false;
-        mConfirmShowing = false;
-        mTryAgainShowing = false;
         mCurrentDialog.startDismiss();
     }
 
@@ -294,7 +288,6 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling negative button", e);
         }
-        mTryAgainShowing = false;
         handleHideDialog(false /* userCanceled */);
     }
 
@@ -308,25 +301,16 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling positive button", e);
         }
-        mConfirmShowing = false;
         handleHideDialog(false /* userCanceled */);
     }
 
     private void handleUserCanceled() {
-        mTryAgainShowing = false;
-        mConfirmShowing = false;
         handleHideDialog(true /* userCanceled */);
     }
 
-    private void handleShowTryAgain() {
-        mCurrentDialog.showTryAgainButton(true /* show */);
-        mTryAgainShowing = true;
-    }
-
     private void handleTryAgainPressed() {
         try {
             mCurrentDialog.clearTemporaryMessage();
-            mTryAgainShowing = false;
             mReceiver.onTryAgainPressed();
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException when handling try again", e);
@@ -337,13 +321,20 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         final boolean wasShowing = mDialogShowing;
+
+        // Save the state of the current dialog (buttons showing, etc)
+        final Bundle savedState = new Bundle();
+        if (mCurrentDialog != null) {
+            mCurrentDialog.onSaveState(savedState);
+        }
+
         if (mDialogShowing) {
             mCurrentDialog.forceRemove();
             mDialogShowing = false;
         }
-        createDialogs();
+
         if (wasShowing) {
-            handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */);
+            handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 9934bfd..c927677 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -56,12 +56,15 @@
 
     private static final String TAG = "BiometricDialogView";
 
+    private static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
+    private static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
+
     private static final int ANIMATION_DURATION_SHOW = 250; // ms
     private static final int ANIMATION_DURATION_AWAY = 350; // ms
 
     private static final int MSG_CLEAR_MESSAGE = 1;
 
-    protected static final int STATE_NONE = 0;
+    protected static final int STATE_IDLE = 0;
     protected static final int STATE_AUTHENTICATING = 1;
     protected static final int STATE_ERROR = 2;
     protected static final int STATE_PENDING_CONFIRMATION = 3;
@@ -74,16 +77,24 @@
     private final DevicePolicyManager mDevicePolicyManager;
     private final float mAnimationTranslationOffset;
     private final int mErrorColor;
-    private final int mTextColor;
     private final float mDialogWidth;
     private final DialogViewCallback mCallback;
 
-    private ViewGroup mLayout;
-    private final Button mPositiveButton;
-    private final Button mNegativeButton;
-    private final TextView mErrorText;
+    protected final ViewGroup mLayout;
+    protected final LinearLayout mDialog;
+    protected final TextView mTitleText;
+    protected final TextView mSubtitleText;
+    protected final TextView mDescriptionText;
+    protected final ImageView mBiometricIcon;
+    protected final TextView mErrorText;
+    protected final Button mPositiveButton;
+    protected final Button mNegativeButton;
+    protected final Button mTryAgainButton;
+
+    protected final int mTextColor;
+
     private Bundle mBundle;
-    private final LinearLayout mDialog;
+
     private int mLastState;
     private boolean mAnimatingAway;
     private boolean mWasForceRemoved;
@@ -91,15 +102,14 @@
     protected boolean mRequireConfirmation;
     private int mUserId; // used to determine if we should show work background
 
-    private boolean mPendingShowTryAgain;
-    private boolean mPendingShowConfirm;
-
     protected abstract int getHintStringResourceId();
     protected abstract int getAuthenticatedAccessibilityResourceId();
     protected abstract int getIconDescriptionResourceId();
     protected abstract Drawable getAnimationForTransition(int oldState, int newState);
     protected abstract boolean shouldAnimateForTransition(int oldState, int newState);
     protected abstract int getDelayAfterAuthenticatedDurationMs();
+    protected abstract boolean shouldGrayAreaDismissDialog();
+    protected abstract void handleClearMessage(boolean requireTryAgain);
 
     private final Runnable mShowAnimationRunnable = new Runnable() {
         @Override
@@ -124,7 +134,7 @@
         public void handleMessage(Message msg) {
             switch(msg.what) {
                 case MSG_CLEAR_MESSAGE:
-                    handleClearMessage();
+                    handleClearMessage((boolean) msg.obj /* requireTryAgain */);
                     break;
                 default:
                     Log.e(TAG, "Unhandled message: " + msg.what);
@@ -158,10 +168,6 @@
         mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false);
         addView(mLayout);
 
-        mDialog = mLayout.findViewById(R.id.dialog);
-
-        mErrorText = mLayout.findViewById(R.id.error);
-
         mLayout.setOnKeyListener(new View.OnKeyListener() {
             boolean downPressed = false;
             @Override
@@ -184,12 +190,19 @@
         final View space = mLayout.findViewById(R.id.space);
         final View leftSpace = mLayout.findViewById(R.id.left_space);
         final View rightSpace = mLayout.findViewById(R.id.right_space);
-        final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
-        final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
+
+        mDialog = mLayout.findViewById(R.id.dialog);
+        mTitleText = mLayout.findViewById(R.id.title);
+        mSubtitleText = mLayout.findViewById(R.id.subtitle);
+        mDescriptionText = mLayout.findViewById(R.id.description);
+        mBiometricIcon = mLayout.findViewById(R.id.biometric_icon);
+        mErrorText = mLayout.findViewById(R.id.error);
         mNegativeButton = mLayout.findViewById(R.id.button2);
         mPositiveButton = mLayout.findViewById(R.id.button1);
+        mTryAgainButton = mLayout.findViewById(R.id.button_try_again);
 
-        icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
+        mBiometricIcon.setContentDescription(
+                getResources().getString(getIconDescriptionResourceId()));
 
         setDismissesDialog(space);
         setDismissesDialog(leftSpace);
@@ -206,8 +219,9 @@
             }, getDelayAfterAuthenticatedDurationMs());
         });
 
-        tryAgain.setOnClickListener((View v) -> {
+        mTryAgainButton.setOnClickListener((View v) -> {
             showTryAgainButton(false /* show */);
+            handleClearMessage(false /* requireTryAgain */);
             mCallback.onTryAgainPressed();
         });
 
@@ -215,15 +229,17 @@
         mLayout.requestFocus();
     }
 
+    public void onSaveState(Bundle bundle) {
+        bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
+        bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
+    }
+
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
         mErrorText.setText(getHintStringResourceId());
 
-        final TextView title = mLayout.findViewById(R.id.title);
-        final TextView subtitle = mLayout.findViewById(R.id.subtitle);
-        final TextView description = mLayout.findViewById(R.id.description);
         final ImageView backgroundView = mLayout.findViewById(R.id.background);
 
         if (mUserManager.isManagedProfile(mUserId)) {
@@ -244,36 +260,34 @@
             mDialog.getLayoutParams().width = (int) mDialogWidth;
         }
 
-        mLastState = STATE_NONE;
+        mLastState = STATE_IDLE;
         updateState(STATE_AUTHENTICATING);
 
         CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE);
 
-        title.setText(titleText);
-        title.setSelected(true);
+        mTitleText.setVisibility(View.VISIBLE);
+        mTitleText.setText(titleText);
+        mTitleText.setSelected(true);
 
         final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
         if (TextUtils.isEmpty(subtitleText)) {
-            subtitle.setVisibility(View.GONE);
+            mSubtitleText.setVisibility(View.GONE);
         } else {
-            subtitle.setVisibility(View.VISIBLE);
-            subtitle.setText(subtitleText);
+            mSubtitleText.setVisibility(View.VISIBLE);
+            mSubtitleText.setText(subtitleText);
         }
 
         final CharSequence descriptionText =
                 mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
         if (TextUtils.isEmpty(descriptionText)) {
-            description.setVisibility(View.GONE);
+            mDescriptionText.setVisibility(View.GONE);
         } else {
-            description.setVisibility(View.VISIBLE);
-            description.setText(descriptionText);
+            mDescriptionText.setVisibility(View.VISIBLE);
+            mDescriptionText.setText(descriptionText);
         }
 
         mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
-        showTryAgainButton(mPendingShowTryAgain);
-        showConfirmationButton(mPendingShowConfirm);
-
         if (mWasForceRemoved || mSkipIntro) {
             // Show the dialog immediately
             mLayout.animate().cancel();
@@ -302,8 +316,7 @@
                 ? (AnimatedVectorDrawable) icon
                 : null;
 
-        final ImageView imageView = getLayout().findViewById(R.id.biometric_icon);
-        imageView.setImageDrawable(icon);
+        mBiometricIcon.setImageDrawable(icon);
 
         if (animation != null && shouldAnimateForTransition(lastState, newState)) {
             animation.forceAnimationOnUI();
@@ -314,7 +327,7 @@
     private void setDismissesDialog(View v) {
         v.setClickable(true);
         v.setOnTouchListener((View view, MotionEvent event) -> {
-            if (mLastState != STATE_AUTHENTICATED) {
+            if (mLastState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
                 mCallback.onUserCanceled();
             }
             return true;
@@ -331,11 +344,9 @@
                 mWindowManager.removeView(BiometricDialogView.this);
                 mAnimatingAway = false;
                 // Set the icons / text back to normal state
-                handleClearMessage();
+                handleClearMessage(false /* requireTryAgain */);
                 showTryAgainButton(false /* show */);
-                mPendingShowTryAgain = false;
-                mPendingShowConfirm = false;
-                updateState(STATE_NONE);
+                updateState(STATE_IDLE);
             }
         };
 
@@ -412,35 +423,28 @@
         return mLayout;
     }
 
-    // Clears the temporary message and shows the help message.
-    private void handleClearMessage() {
-        updateState(STATE_AUTHENTICATING);
-        mErrorText.setText(getHintStringResourceId());
-        mErrorText.setTextColor(mTextColor);
-    }
-
     // Shows an error/help message
-    private void showTemporaryMessage(String message) {
+    private void showTemporaryMessage(String message, boolean requireTryAgain) {
         mHandler.removeMessages(MSG_CLEAR_MESSAGE);
         updateState(STATE_ERROR);
         mErrorText.setText(message);
         mErrorText.setTextColor(mErrorColor);
         mErrorText.setContentDescription(message);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE, requireTryAgain),
                 BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
     public void clearTemporaryMessage() {
         mHandler.removeMessages(MSG_CLEAR_MESSAGE);
-        mHandler.obtainMessage(MSG_CLEAR_MESSAGE).sendToTarget();
+        mHandler.obtainMessage(MSG_CLEAR_MESSAGE, false /* requireTryAgain */).sendToTarget();
     }
 
-    public void showHelpMessage(String message) {
-        showTemporaryMessage(message);
+    public void showHelpMessage(String message, boolean requireTryAgain) {
+        showTemporaryMessage(message, requireTryAgain);
     }
 
     public void showErrorMessage(String error) {
-        showTemporaryMessage(error);
+        showTemporaryMessage(error, false /* requireTryAgain */);
         showTryAgainButton(false /* show */);
         mCallback.onErrorShown();
     }
@@ -459,22 +463,11 @@
     }
 
     public void showTryAgainButton(boolean show) {
-        final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
-        if (show) {
-            tryAgain.setVisibility(View.VISIBLE);
-        } else {
-            tryAgain.setVisibility(View.GONE);
-        }
     }
 
-    // Set the state before the window is attached, so we know if the dialog should be started
-    // with or without the button. This is because there's no good onPause signal
-    public void setPendingTryAgain(boolean show) {
-        mPendingShowTryAgain = show;
-    }
-
-    public void setPendingConfirm(boolean show) {
-        mPendingShowConfirm = show;
+    public void restoreState(Bundle bundle) {
+        mTryAgainButton.setVisibility(bundle.getInt(KEY_TRY_AGAIN_VISIBILITY));
+        mPositiveButton.setVisibility(bundle.getInt(KEY_CONFIRM_VISIBILITY));
     }
 
     public WindowManager.LayoutParams getLayoutParams() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index de3f947..9fba44b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -16,8 +16,18 @@
 
 package com.android.systemui.biometrics;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Outline;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewOutlineProvider;
 
 import com.android.systemui.R;
 
@@ -28,13 +38,263 @@
  */
 public class FaceDialogView extends BiometricDialogView {
 
+    private static final String TAG = "FaceDialogView";
+    private static final String KEY_DIALOG_SIZE = "key_dialog_size";
+
     private static final int HIDE_DIALOG_DELAY = 500; // ms
+    private static final int IMPLICIT_Y_PADDING = 16; // dp
+    private static final int GROW_DURATION = 150; // ms
+    private static final int TEXT_ANIMATE_DISTANCE = 32; // dp
+
+    private static final int SIZE_UNKNOWN = 0;
+    private static final int SIZE_SMALL = 1;
+    private static final int SIZE_GROWING = 2;
+    private static final int SIZE_BIG = 3;
+
+    private int mSize;
+    private float mIconOriginalY;
+    private DialogOutlineProvider mOutlineProvider = new DialogOutlineProvider();
+
+    private final class DialogOutlineProvider extends ViewOutlineProvider {
+
+        float mY;
+
+        @Override
+        public void getOutline(View view, Outline outline) {
+            outline.setRoundRect(
+                    0 /* left */,
+                    (int) mY, /* top */
+                    mDialog.getWidth() /* right */,
+                    mDialog.getBottom(), /* bottom */
+                    getResources().getDimension(R.dimen.biometric_dialog_corner_size));
+        }
+
+        int calculateSmall() {
+            final float padding = dpToPixels(IMPLICIT_Y_PADDING);
+            return mDialog.getHeight() - mBiometricIcon.getHeight() - 2 * (int) padding;
+        }
+
+        void setOutlineY(float y) {
+            mY = y;
+        }
+    }
 
     public FaceDialogView(Context context,
             DialogViewCallback callback) {
         super(context, callback);
     }
 
+    private void updateSize(int newSize) {
+        final float padding = dpToPixels(IMPLICIT_Y_PADDING);
+        final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding;
+
+        if (newSize == SIZE_SMALL) {
+            // These fields are required and/or always hold a spot on the UI, so should be set to
+            // INVISIBLE so they keep their position
+            mTitleText.setVisibility(View.INVISIBLE);
+            mErrorText.setVisibility(View.INVISIBLE);
+            mNegativeButton.setVisibility(View.INVISIBLE);
+
+            // These fields are optional, so set them to gone or invisible depending on their
+            // usage. If they're empty, they're already set to GONE in BiometricDialogView.
+            if (!TextUtils.isEmpty(mSubtitleText.getText())) {
+                mSubtitleText.setVisibility(View.INVISIBLE);
+            }
+            if (!TextUtils.isEmpty(mDescriptionText.getText())) {
+                mDescriptionText.setVisibility(View.INVISIBLE);
+            }
+
+            // Move the biometric icon to the small spot
+            mBiometricIcon.setY(iconSmallPositionY);
+
+            // Clip the dialog to the small size
+            mDialog.setOutlineProvider(mOutlineProvider);
+            mOutlineProvider.setOutlineY(mOutlineProvider.calculateSmall());
+
+            mDialog.setClipToOutline(true);
+            mDialog.invalidateOutline();
+
+            mSize = newSize;
+        } else if (mSize == SIZE_SMALL && newSize == SIZE_BIG) {
+            mSize = SIZE_GROWING;
+
+            // Animate the outline
+            final ValueAnimator outlineAnimator =
+                    ValueAnimator.ofFloat(mOutlineProvider.calculateSmall(), 0);
+            outlineAnimator.addUpdateListener((animation) -> {
+                final float y = (float) animation.getAnimatedValue();
+                mOutlineProvider.setOutlineY(y);
+                mDialog.invalidateOutline();
+            });
+
+            // Animate the icon back to original big position
+            final ValueAnimator iconAnimator =
+                    ValueAnimator.ofFloat(iconSmallPositionY, mIconOriginalY);
+            iconAnimator.addUpdateListener((animation) -> {
+                final float y = (float) animation.getAnimatedValue();
+                mBiometricIcon.setY(y);
+            });
+
+            // Animate the error text so it slides up with the icon
+            final ValueAnimator textSlideAnimator =
+                    ValueAnimator.ofFloat(dpToPixels(TEXT_ANIMATE_DISTANCE), 0);
+            textSlideAnimator.addUpdateListener((animation) -> {
+                final float y = (float) animation.getAnimatedValue();
+                mErrorText.setTranslationY(y);
+            });
+
+            // Opacity animator for things that should fade in (title, subtitle, details, negative
+            // button)
+            final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1);
+            opacityAnimator.addUpdateListener((animation) -> {
+                final float opacity = (float) animation.getAnimatedValue();
+
+                // These fields are required and/or always hold a spot on the UI
+                mTitleText.setAlpha(opacity);
+                mErrorText.setAlpha(opacity);
+                mNegativeButton.setAlpha(opacity);
+                mTryAgainButton.setAlpha(opacity);
+
+                // These fields are optional, so only animate them if they're supposed to be showing
+                if (!TextUtils.isEmpty(mSubtitleText.getText())) {
+                    mSubtitleText.setAlpha(opacity);
+                }
+                if (!TextUtils.isEmpty(mDescriptionText.getText())) {
+                    mDescriptionText.setAlpha(opacity);
+                }
+            });
+
+            // Choreograph together
+            final AnimatorSet as = new AnimatorSet();
+            as.setDuration(GROW_DURATION);
+            as.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    super.onAnimationStart(animation);
+                    // Set the visibility of opacity-animating views back to VISIBLE
+                    mTitleText.setVisibility(View.VISIBLE);
+                    mErrorText.setVisibility(View.VISIBLE);
+                    mNegativeButton.setVisibility(View.VISIBLE);
+                    mTryAgainButton.setVisibility(View.VISIBLE);
+
+                    if (!TextUtils.isEmpty(mSubtitleText.getText())) {
+                        mSubtitleText.setVisibility(View.VISIBLE);
+                    }
+                    if (!TextUtils.isEmpty(mDescriptionText.getText())) {
+                        mDescriptionText.setVisibility(View.VISIBLE);
+                    }
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    mSize = SIZE_BIG;
+                }
+            });
+            as.play(outlineAnimator).with(iconAnimator).with(opacityAnimator)
+                    .with(textSlideAnimator);
+            as.start();
+        } else if (mSize == SIZE_BIG) {
+            mDialog.setClipToOutline(false);
+            mDialog.invalidateOutline();
+
+            mBiometricIcon.setY(mIconOriginalY);
+
+            mSize = newSize;
+        }
+    }
+
+    @Override
+    public void onSaveState(Bundle bundle) {
+        super.onSaveState(bundle);
+        bundle.putInt(KEY_DIALOG_SIZE, mSize);
+    }
+
+
+    @Override
+    protected void handleClearMessage(boolean requireTryAgain) {
+        // Clears the temporary message and shows the help message. If requireTryAgain is true,
+        // we will start the authenticating state again.
+        if (!requireTryAgain) {
+            updateState(STATE_AUTHENTICATING);
+            mErrorText.setText(getHintStringResourceId());
+            mErrorText.setTextColor(mTextColor);
+            mErrorText.setVisibility(View.VISIBLE);
+        } else {
+            updateState(STATE_IDLE);
+            mErrorText.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    @Override
+    public void restoreState(Bundle bundle) {
+        super.restoreState(bundle);
+        // Keep in mind that this happens before onAttachedToWindow()
+        mSize = bundle.getInt(KEY_DIALOG_SIZE);
+    }
+
+    /**
+     * Do small/big layout here instead of onAttachedToWindow, since:
+     * 1) We need the big layout to be measured, etc for small -> big animation
+     * 2) We need the dialog measurements to know where to move the biometric icon to
+     *
+     * BiometricDialogView already sets the views to their default big state, so here we only
+     * need to hide the ones that are unnecessary.
+     */
+    @Override
+    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        if (mIconOriginalY == 0) {
+            mIconOriginalY = mBiometricIcon.getY();
+        }
+
+        // UNKNOWN means size hasn't been set yet. First time we create the dialog.
+        // onLayout can happen when visibility of views change (during animation, etc).
+        if (mSize != SIZE_UNKNOWN) {
+            // Probably not the cleanest way to do this, but since dialog is big by default,
+            // and small dialogs can persist across orientation changes, we need to set it to
+            // small size here again.
+            if (mSize == SIZE_SMALL) {
+                updateSize(SIZE_SMALL);
+            }
+            return;
+        }
+
+        // If we don't require confirmation, show the small dialog first (until errors occur).
+        if (!requiresConfirmation()) {
+            updateSize(SIZE_SMALL);
+        } else {
+            updateSize(SIZE_BIG);
+        }
+    }
+
+    @Override
+    public void showErrorMessage(String error) {
+        super.showErrorMessage(error);
+
+        // All error messages will cause the dialog to go from small -> big. Error messages
+        // are messages such as lockout, auth failed, etc.
+        if (mSize == SIZE_SMALL) {
+            updateSize(SIZE_BIG);
+        }
+    }
+
+    @Override
+    public void showTryAgainButton(boolean show) {
+        if (show && mSize == SIZE_SMALL) {
+            // Do not call super, we will nicely animate the alpha together with the rest
+            // of the elements in here.
+            updateSize(SIZE_BIG);
+        } else {
+            if (show) {
+                mTryAgainButton.setVisibility(View.VISIBLE);
+            } else {
+                mTryAgainButton.setVisibility(View.GONE);
+            }
+        }
+    }
+
     @Override
     protected int getHintStringResourceId() {
         return R.string.face_dialog_looking_for_face;
@@ -56,7 +316,9 @@
 
     @Override
     protected boolean shouldAnimateForTransition(int oldState, int newState) {
-        if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+        if (oldState == STATE_ERROR && newState == STATE_IDLE) {
+            return true;
+        } else if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
             return false;
         } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
             return true;
@@ -78,9 +340,19 @@
     }
 
     @Override
+    protected boolean shouldGrayAreaDismissDialog() {
+        if (mSize == SIZE_SMALL) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
     protected Drawable getAnimationForTransition(int oldState, int newState) {
         int iconRes;
-        if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+        if (oldState == STATE_ERROR && newState == STATE_IDLE) {
+            iconRes = R.drawable.face_dialog_error_to_face;
+        } else if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
             iconRes = R.drawable.face_dialog_face_to_error;
         } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
             iconRes = R.drawable.face_dialog_face_to_error;
@@ -97,4 +369,14 @@
         }
         return mContext.getDrawable(iconRes);
     }
+
+    private float dpToPixels(float dp) {
+        return dp * ((float) mContext.getResources().getDisplayMetrics().densityDpi
+                / DisplayMetrics.DENSITY_DEFAULT);
+    }
+
+    private float pixelsToDp(float pixels) {
+        return pixels / ((float) mContext.getResources().getDisplayMetrics().densityDpi
+                / DisplayMetrics.DENSITY_DEFAULT);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 1a6cee2..c9b30ba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -32,6 +32,14 @@
             DialogViewCallback callback) {
         super(context, callback);
     }
+
+    @Override
+    protected void handleClearMessage(boolean requireTryAgain) {
+        updateState(STATE_AUTHENTICATING);
+        mErrorText.setText(getHintStringResourceId());
+        mErrorText.setTextColor(mTextColor);
+    }
+
     @Override
     protected int getHintStringResourceId() {
         return R.string.fingerprint_dialog_touch_sensor;
@@ -49,7 +57,7 @@
 
     @Override
     protected boolean shouldAnimateForTransition(int oldState, int newState) {
-        if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+        if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
             return false;
         } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
             return true;
@@ -68,9 +76,15 @@
     }
 
     @Override
+    protected boolean shouldGrayAreaDismissDialog() {
+        // Fingerprint dialog always dismisses when region outside the dialog is tapped
+        return true;
+    }
+
+    @Override
     protected Drawable getAnimationForTransition(int oldState, int newState) {
         int iconRes;
-        if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+        if (oldState == STATE_IDLE && newState == STATE_AUTHENTICATING) {
             iconRes = R.drawable.fingerprint_dialog_fp_to_error;
         } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
             iconRes = R.drawable.fingerprint_dialog_fp_to_error;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
new file mode 100644
index 0000000..845b084
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
@@ -0,0 +1,80 @@
+/*
+ * 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.bubbles;
+
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.Log;
+
+// XXX: Mostly opied from launcher code / can we share?
+/**
+ * Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
+ */
+public class BadgeRenderer {
+
+    private static final String TAG = "BadgeRenderer";
+
+    // The badge sizes are defined as percentages of the app icon size.
+    private static final float SIZE_PERCENTAGE = 0.38f;
+
+    // Extra scale down of the dot
+    private static final float DOT_SCALE = 0.6f;
+
+    private final float mDotCenterOffset;
+    private final float mCircleRadius;
+    private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
+
+    public BadgeRenderer(int iconSizePx) {
+        mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
+        int size = (int) (DOT_SCALE * mDotCenterOffset);
+        mCircleRadius = size / 2f;
+    }
+
+    /**
+     * Draw a circle in the top right corner of the given bounds.
+     *
+     * @param color The color (based on the icon) to use for the badge.
+     * @param iconBounds The bounds of the icon being badged.
+     * @param badgeScale The progress of the animation, from 0 to 1.
+     * @param spaceForOffset How much space to offset the badge up and to the left or right.
+     * @param onLeft Whether the badge should be draw on left or right side.
+     */
+    public void draw(Canvas canvas, int color, Rect iconBounds, float badgeScale,
+            Point spaceForOffset, boolean onLeft) {
+        if (iconBounds == null) {
+            Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
+            return;
+        }
+        canvas.save();
+        // We draw the badge relative to its center.
+        int x = onLeft ? iconBounds.left : iconBounds.right;
+        float offset = onLeft ? (mDotCenterOffset / 2) : -(mDotCenterOffset / 2);
+        float badgeCenterX = x + offset;
+        float badgeCenterY = iconBounds.top + mDotCenterOffset / 2;
+
+        canvas.translate(badgeCenterX + spaceForOffset.x, badgeCenterY - spaceForOffset.y);
+
+        canvas.scale(badgeScale, badgeScale);
+        mCirclePaint.setColor(color);
+        canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
+        canvas.restore();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
new file mode 100644
index 0000000..92d3cc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -0,0 +1,129 @@
+/*
+ * 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.bubbles;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * View that circle crops its contents and supports displaying a coloured dot on a top corner.
+ */
+public class BadgedImageView extends ImageView {
+
+    private BadgeRenderer mDotRenderer;
+    private int mIconSize;
+    private Rect mTempBounds = new Rect();
+    private Point mTempPoint = new Point();
+    private Path mClipPath = new Path();
+
+    private float mDotScale = 0f;
+    private int mUpdateDotColor;
+    private boolean mShowUpdateDot;
+    private boolean mOnLeft;
+
+    public BadgedImageView(Context context) {
+        this(context, null);
+    }
+
+    public BadgedImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setScaleType(ScaleType.CENTER_CROP);
+        mIconSize = getResources().getDimensionPixelSize(R.dimen.bubble_size);
+        mDotRenderer = new BadgeRenderer(mIconSize);
+    }
+
+    // TODO: Clipping oval path isn't great: rerender image into a separate, rounded bitmap and
+    // then draw would be better
+    @Override
+    public void onDraw(Canvas canvas) {
+        canvas.save();
+        // Circle crop
+        mClipPath.addOval(getPaddingStart(), getPaddingTop(),
+                getWidth() - getPaddingEnd(), getHeight() - getPaddingBottom(), Path.Direction.CW);
+        canvas.clipPath(mClipPath);
+        super.onDraw(canvas);
+
+        // After we've circle cropped what we're showing, restore so we don't clip the badge
+        canvas.restore();
+
+        // Draw the badge
+        if (mShowUpdateDot) {
+            getDrawingRect(mTempBounds);
+            mTempPoint.set((getWidth() - mIconSize) / 2, getPaddingTop());
+            mDotRenderer.draw(canvas, mUpdateDotColor, mTempBounds, mDotScale, mTempPoint,
+                    mOnLeft);
+        }
+    }
+
+    /**
+     * Set whether the dot should appear on left or right side of the view.
+     */
+    public void setDotPosition(boolean onLeft) {
+        mOnLeft = onLeft;
+        invalidate();
+    }
+
+    /**
+     * Set whether the dot should show or not.
+     */
+    public void setShowDot(boolean showBadge) {
+        mShowUpdateDot = showBadge;
+        invalidate();
+    }
+
+    /**
+     * @return whether the dot is being displayed.
+     */
+    public boolean isShowingDot() {
+        return mShowUpdateDot;
+    }
+
+    /**
+     * The colour to use for the dot.
+     */
+    public void setDotColor(int color) {
+        mUpdateDotColor = color;
+        invalidate();
+    }
+
+    /**
+     * How big the dot should be, fraction from 0 to 1.
+     */
+    public void setDotScale(float fraction) {
+        mDotScale = fraction;
+        invalidate();
+    }
+
+    public float getDotScale() {
+        return mDotScale;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 881aa18..957d772 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -21,25 +21,42 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.notification.NotificationAlertingManager.alertAgain;
 
+import android.annotation.Nullable;
+import android.app.INotificationManager;
 import android.app.Notification;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -57,47 +74,38 @@
     private static final String TAG = "BubbleController";
 
     // Enables some subset of notifs to automatically become bubbles
-    public static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
-    // When a bubble is dismissed, recreate it as a notification
-    public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
+    private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
 
     // Secure settings
     private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
     private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
     private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
+    private static final String ENABLE_BUBBLE_ACTIVITY_VIEW = "experiment_bubble_activity_view";
+    private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent";
 
-    private Context mContext;
-    private BubbleDismissListener mDismissListener;
+    private final Context mContext;
+    private final NotificationEntryManager mNotificationEntryManager;
     private BubbleStateChangeListener mStateChangeListener;
     private BubbleExpandListener mExpandListener;
+    private LayoutInflater mInflater;
 
-    private Map<String, BubbleView> mBubbles = new HashMap<>();
+    private final Map<String, BubbleView> mBubbles = new HashMap<>();
     private BubbleStackView mStackView;
-    private Point mDisplaySize;
+    private final Point mDisplaySize;
 
     // Bubbles get added to the status bar view
-    @VisibleForTesting
-    protected StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowController mStatusBarWindowController;
+    private StatusBarStateListener mStatusBarStateListener;
+
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
+
+    private INotificationManager mNotificationManagerService;
 
     // Used for determining view rect for touch interaction
     private Rect mTempRect = new Rect();
 
     /**
-     * Listener to find out about bubble / bubble stack dismissal events.
-     */
-    public interface BubbleDismissListener {
-        /**
-         * Called when the entire stack of bubbles is dismissed by the user.
-         */
-        void onStackDismissed();
-
-        /**
-         * Called when a specific bubble is dismissed by the user.
-         */
-        void onBubbleDismissed(String key);
-    }
-
-    /**
      * Listener to be notified when some states of the bubbles change.
      */
     public interface BubbleStateChangeListener {
@@ -113,11 +121,30 @@
     public interface BubbleExpandListener {
         /**
          * Called when the expansion state of the bubble stack changes.
-         *
          * @param isExpanding whether it's expanding or collapsing
-         * @param amount fraction of how expanded or collapsed it is, 1 being fully, 0 at the start
+         * @param key the notification key associated with bubble being expanded
          */
-        void onBubbleExpandChanged(boolean isExpanding, float amount);
+        void onBubbleExpandChanged(boolean isExpanding, String key);
+    }
+
+    /**
+     * Listens for the current state of the status bar and updates the visibility state
+     * of bubbles as needed.
+     */
+    private class StatusBarStateListener implements StatusBarStateController.StateListener {
+        private int mState;
+        /**
+         * Returns the current status bar state.
+         */
+        public int getCurrentState() {
+            return mState;
+        }
+
+        @Override
+        public void onStateChanged(int newState) {
+            mState = newState;
+            updateVisibility();
+        }
     }
 
     @Inject
@@ -126,14 +153,21 @@
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mDisplaySize = new Point();
         wm.getDefaultDisplay().getSize(mDisplaySize);
-        mStatusBarWindowController = statusBarWindowController;
-    }
+        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
-    /**
-     * Set a listener to be notified of bubble dismissal events.
-     */
-    public void setDismissListener(BubbleDismissListener listener) {
-        mDismissListener = listener;
+        mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
+        mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
+
+        try {
+            mNotificationManagerService = INotificationManager.Stub.asInterface(
+                    ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
+        } catch (ServiceManager.ServiceNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        mStatusBarWindowController = statusBarWindowController;
+        mStatusBarStateListener = new StatusBarStateListener();
+        Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
     }
 
     /**
@@ -158,7 +192,12 @@
      * screen (e.g. if on AOD).
      */
     public boolean hasBubbles() {
-        return mBubbles.size() > 0;
+        for (BubbleView bv : mBubbles.values()) {
+            if (!bv.getEntry().isBubbleDismissed()) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -173,43 +212,43 @@
      */
     public void collapseStack() {
         if (mStackView != null) {
-            mStackView.animateExpansion(false);
+            mStackView.collapseStack();
         }
     }
 
     /**
      * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
      */
-    public void dismissStack() {
+    void dismissStack() {
         if (mStackView == null) {
             return;
         }
-        Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
+        Set<String> keys = mBubbles.keySet();
+        for (String key: keys) {
+            mBubbles.get(key).getEntry().setBubbleDismissed(true);
+        }
+        mStackView.stackDismissed();
+
         // Reset the position of the stack (TODO - or should we save / respect last user position?)
+        Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
         mStackView.setPosition(startPoint.x, startPoint.y);
-        for (String key: mBubbles.keySet()) {
-            removeBubble(key);
-        }
-        if (mDismissListener != null) {
-            mDismissListener.onStackDismissed();
-        }
-        updateBubblesShowing();
+
+        updateVisibility();
+        mNotificationEntryManager.updateNotifications();
     }
 
     /**
-     * Adds a bubble associated with the provided notification entry or updates it if it exists.
+     * Adds or updates a bubble associated with the provided notification entry.
+     *
+     * @param notif the notification associated with this bubble.
+     * @param updatePosition whether this update should promote the bubble to the top of the stack.
      */
-    public void addBubble(NotificationData.Entry notif) {
+    public void updateBubble(NotificationEntry notif, boolean updatePosition) {
         if (mBubbles.containsKey(notif.key)) {
             // It's an update
             BubbleView bubble = mBubbles.get(notif.key);
-            mStackView.updateBubble(bubble, notif);
+            mStackView.updateBubble(bubble, notif, updatePosition);
         } else {
-            // It's new
-            BubbleView bubble = new BubbleView(mContext);
-            bubble.setNotif(notif);
-            mBubbles.put(bubble.getKey(), bubble);
-
             boolean setPosition = mStackView != null && mStackView.getVisibility() != VISIBLE;
             if (mStackView == null) {
                 setPosition = true;
@@ -224,75 +263,134 @@
                     mStackView.setExpandListener(mExpandListener);
                 }
             }
+            // It's new
+            BubbleView bubble = (BubbleView) mInflater.inflate(
+                    R.layout.bubble_view, mStackView, false /* attachToRoot */);
+            bubble.setNotif(notif);
+            if (shouldUseActivityView(mContext)) {
+                bubble.setAppOverlayIntent(getAppOverlayIntent(notif));
+            }
+            mBubbles.put(bubble.getKey(), bubble);
             mStackView.addBubble(bubble);
             if (setPosition) {
                 // Need to add the bubble to the stack before we can know the width
                 Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
                 mStackView.setPosition(startPoint.x, startPoint.y);
-                mStackView.setVisibility(VISIBLE);
             }
-            updateBubblesShowing();
         }
+        updateVisibility();
+    }
+
+    @Nullable
+    private PendingIntent getAppOverlayIntent(NotificationEntry notif) {
+        Notification notification = notif.notification.getNotification();
+        if (canLaunchInActivityView(notification.getBubbleMetadata() != null
+                ? notification.getBubbleMetadata().getIntent() : null)) {
+            return notification.getBubbleMetadata().getIntent();
+        } else if (shouldUseContentIntent(mContext)
+                && canLaunchInActivityView(notification.contentIntent)) {
+            Log.d(TAG, "[addBubble " + notif.key
+                    + "]: No appOverlayIntent, using contentIntent.");
+            return notification.contentIntent;
+        }
+        Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
+        return null;
     }
 
     /**
      * Removes the bubble associated with the {@param uri}.
      */
-    public void removeBubble(String key) {
-        BubbleView bv = mBubbles.get(key);
+    void removeBubble(String key) {
+        BubbleView bv = mBubbles.remove(key);
         if (mStackView != null && bv != null) {
             mStackView.removeBubble(bv);
-            bv.getEntry().setBubbleDismissed(true);
+            bv.destroyActivityView(mStackView);
         }
-        if (mDismissListener != null) {
-            mDismissListener.onBubbleDismissed(key);
+
+        NotificationEntry entry = bv != null ? bv.getEntry() : null;
+        if (entry != null) {
+            entry.setBubbleDismissed(true);
+            mNotificationEntryManager.updateNotifications();
         }
-        updateBubblesShowing();
+        updateVisibility();
     }
 
-    private void updateBubblesShowing() {
-        boolean hasBubblesShowing = false;
-        for (BubbleView bv : mBubbles.values()) {
-            if (!bv.getEntry().isBubbleDismissed()) {
-                hasBubblesShowing = true;
-                break;
+    @SuppressWarnings("FieldCanBeLocal")
+    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+        @Override
+        public void onPendingEntryAdded(NotificationEntry entry) {
+            if (shouldAutoBubble(mContext, entry) || shouldBubble(entry)) {
+                // TODO: handle group summaries
+                // It's a new notif, it shows in the shade and as a bubble
+                entry.setIsBubble(true);
+                entry.setShowInShadeWhenBubble(true);
             }
         }
-        boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
-        mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
-        if (mStackView != null && !hasBubblesShowing) {
-            mStackView.setVisibility(INVISIBLE);
+
+        @Override
+        public void onEntryInflated(NotificationEntry entry,
+                @NotificationInflater.InflationFlag int inflatedFlags) {
+            if (entry.isBubble() && mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
+                updateBubble(entry, true /* updatePosition */);
+            }
         }
+
+        @Override
+        public void onPreEntryUpdated(NotificationEntry entry) {
+            if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+                    && alertAgain(entry, entry.notification.getNotification())) {
+                entry.setShowInShadeWhenBubble(true);
+                entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
+                if (mBubbles.containsKey(entry.key)) {
+                    mBubbles.get(entry.key).updateDotVisibility();
+                }
+                updateBubble(entry, true /* updatePosition */);
+            }
+        }
+
+        @Override
+        public void onEntryRemoved(NotificationEntry entry,
+                @Nullable NotificationVisibility visibility,
+                boolean removedByUser) {
+            entry.setShowInShadeWhenBubble(false);
+            if (mBubbles.containsKey(entry.key)) {
+                mBubbles.get(entry.key).updateDotVisibility();
+            }
+            if (!removedByUser) {
+                // This was a cancel so we should remove the bubble
+                removeBubble(entry.key);
+            }
+        }
+    };
+
+    /**
+     * Lets any listeners know if bubble state has changed.
+     */
+    private void updateBubblesShowing() {
+        if (mStackView == null) {
+            return;
+        }
+
+        boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
+        boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
+        mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
         if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
             mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
         }
     }
 
     /**
-     * Sets the visibility of the bubbles, doesn't un-bubble them, just changes visibility.
+     * Updates the visibility of the bubbles based on current state.
+     * Does not un-bubble, just hides or un-hides. Will notify any
+     * {@link BubbleStateChangeListener}s if visibility changes.
      */
-    public void updateVisibility(boolean visible) {
-        if (mStackView == null) {
-            return;
-        }
-        ArrayList<BubbleView> viewsToRemove = new ArrayList<>();
-        for (BubbleView bv : mBubbles.values()) {
-            NotificationData.Entry entry = bv.getEntry();
-            if (entry != null) {
-                if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
-                    viewsToRemove.add(bv);
-                }
-            }
-        }
-        for (BubbleView view : viewsToRemove) {
-            mBubbles.remove(view.getKey());
-            mStackView.removeBubble(view);
-        }
-        if (mStackView != null) {
-            mStackView.setVisibility(visible ? VISIBLE : INVISIBLE);
-            if (!visible) {
-                collapseStack();
-            }
+    public void updateVisibility() {
+        if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) {
+            // Bubbles only appear in unlocked shade
+            mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
+        } else if (mStackView != null) {
+            mStackView.setVisibility(INVISIBLE);
+            collapseStack();
         }
         updateBubblesShowing();
     }
@@ -308,8 +406,19 @@
         return mTempRect;
     }
 
+    private boolean canLaunchInActivityView(PendingIntent intent) {
+        if (intent == null) {
+            return false;
+        }
+        ActivityInfo info =
+                intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
+        return info != null
+                && ActivityInfo.isResizeableMode(info.resizeMode)
+                && (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+    }
+
     @VisibleForTesting
-    public BubbleStackView getStackView() {
+    BubbleStackView getStackView() {
         return mStackView;
     }
 
@@ -317,7 +426,7 @@
     /**
      * Gets an appropriate starting point to position the bubble stack.
      */
-    public static Point getStartPoint(int size, Point displaySize) {
+    private static Point getStartPoint(int size, Point displaySize) {
         final int x = displaySize.x - size + EDGE_OVERLAP;
         final int y = displaySize.y / 4;
         return new Point(x, y);
@@ -326,24 +435,48 @@
     /**
      * Gets an appropriate position for the bubble when the stack is expanded.
      */
-    public static Point getExpandPoint(BubbleStackView view, int size, Point displaySize) {
+    static Point getExpandPoint(BubbleStackView view, int size, Point displaySize) {
         // Same place for now..
         return new Point(EDGE_OVERLAP, size);
     }
 
     /**
-     * Whether the notification should bubble or not.
+     * Whether the notification has been developer configured to bubble and is allowed by the user.
      */
-    public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+    private boolean shouldBubble(NotificationEntry entry) {
+        StatusBarNotification n = entry.notification;
+        boolean canAppOverlay = false;
+        try {
+            canAppOverlay = mNotificationManagerService.areBubblesAllowedForPackage(
+                    n.getPackageName(), n.getUid());
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error calling NoMan to determine if app can overlay", e);
+        }
+
+        boolean canChannelOverlay = mNotificationEntryManager.getNotificationData().getChannel(
+                entry.key).canBubble();
+        boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null
+                && n.getNotification().getBubbleMetadata().getIntent() != null;
+        return hasOverlayIntent && canChannelOverlay && canAppOverlay;
+    }
+
+    /**
+     * Whether the notification should bubble or not. Gated by debug flag.
+     * <p>
+     * If a notification has been set to bubble via proper bubble APIs or if it is an important
+     * message-like notification.
+     * </p>
+     */
+    private boolean shouldAutoBubble(Context context, NotificationEntry entry) {
         if (entry.isBubbleDismissed()) {
             return false;
         }
+        StatusBarNotification n = entry.notification;
 
         boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
         boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
         boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;
 
-        StatusBarNotification n = entry.notification;
         boolean hasRemoteInput = false;
         if (n.getNotification().actions != null) {
             for (Notification.Action action : n.getNotification().actions) {
@@ -380,4 +513,14 @@
         return Settings.Secure.getInt(context.getContentResolver(),
                 ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
     }
+
+    private static boolean shouldUseActivityView(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_BUBBLE_ACTIVITY_VIEW, 0) != 0;
+    }
+
+    private static boolean shouldUseContentIntent(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
index e28d96b..71ae1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -21,9 +21,11 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.ShapeDrawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
@@ -35,6 +37,8 @@
 
     // The triangle pointing to the expanded view
     private View mPointerView;
+    // The view displayed between the pointer and the expanded view
+    private TextView mHeaderView;
     // The view that is being displayed for the expanded state
     private View mExpandedView;
 
@@ -68,6 +72,7 @@
                 TriangleShape.create(width, height, true /* pointUp */));
         triangleDrawable.setTint(Color.WHITE); // TODO: dark mode
         mPointerView.setBackground(triangleDrawable);
+        mHeaderView = findViewById(R.id.bubble_content_header);
     }
 
     /**
@@ -80,9 +85,20 @@
     }
 
     /**
+     * Set the text displayed within the header.
+     */
+    public void setHeaderText(CharSequence text) {
+        mHeaderView.setText(text);
+        mHeaderView.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
+    }
+
+    /**
      * Set the view to display for the expanded state. Passing null will clear the view.
      */
     public void setExpandedView(View view) {
+        if (mExpandedView == view) {
+            return;
+        }
         if (mExpandedView != null) {
             removeView(mExpandedView);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index dfd18b2..9a11b96 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -21,10 +21,13 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.app.ActivityView;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.RectF;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -40,7 +43,7 @@
 
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -50,6 +53,7 @@
  */
 public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView {
 
+    private static final String TAG = "BubbleStackView";
     private Point mDisplaySize;
 
     private FrameLayout mBubbleContainer;
@@ -59,9 +63,10 @@
     private int mBubblePadding;
 
     private boolean mIsExpanded;
+    private int mExpandedBubbleHeight;
+    private BubbleTouchHandler mTouchHandler;
     private BubbleView mExpandedBubble;
     private Point mCollapsedPosition;
-    private BubbleTouchHandler mTouchHandler;
     private BubbleController.BubbleExpandListener mExpandListener;
 
     private boolean mViewUpdatedRequested = false;
@@ -106,6 +111,7 @@
         mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
         mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
 
+        mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
         mDisplaySize = new Point();
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         wm.getDefaultDisplay().getSize(mDisplaySize);
@@ -205,13 +211,24 @@
      */
     public void setExpandedBubble(BubbleView bubbleToExpand) {
         mExpandedBubble = bubbleToExpand;
+        boolean prevExpanded = mIsExpanded;
         mIsExpanded = true;
-        updateExpandedBubble();
-        requestUpdate();
+        if (!prevExpanded) {
+            // If we weren't previously expanded we should animate open.
+            animateExpansion(true /* expand */);
+        } else {
+            // If we were expanded just update the views
+            updateExpandedBubble();
+            requestUpdate();
+        }
+        mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
+        notifyExpansionChanged(mExpandedBubble, true /* expanded */);
     }
 
     /**
-     * Adds a bubble to the stack.
+     * Adds a bubble to the top of the stack.
+     *
+     * @param bubbleView the view to add to the stack.
      */
     public void addBubble(BubbleView bubbleView) {
         mBubbleContainer.addView(bubbleView, 0,
@@ -228,17 +245,26 @@
         mBubbleContainer.removeView(bubbleView);
         boolean wasExpanded = mIsExpanded;
         int bubbleCount = mBubbleContainer.getChildCount();
-        if (bubbleView.equals(mExpandedBubble) && bubbleCount > 0) {
+        if (mIsExpanded && bubbleView.equals(mExpandedBubble) && bubbleCount > 0) {
             // If we have other bubbles and are expanded go to the next one or previous
             // if the bubble removed was last
             int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
-            mExpandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
+            BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
+            setExpandedBubble(expandedBubble);
         }
         mIsExpanded = wasExpanded && mBubbleContainer.getChildCount() > 0;
-        requestUpdate();
-        if (wasExpanded && !mIsExpanded && mExpandListener != null) {
-            mExpandListener.onBubbleExpandChanged(mIsExpanded, 1 /* amount */);
+        if (wasExpanded != mIsExpanded) {
+            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
         }
+        requestUpdate();
+    }
+
+    /**
+     * Dismiss the stack of bubbles.
+     */
+    public void stackDismissed() {
+        collapseStack();
+        mBubbleContainer.removeAllViews();
     }
 
     /**
@@ -246,11 +272,19 @@
      *
      * @param bubbleView the view to update in the stack.
      * @param entry the entry to update it with.
+     * @param updatePosition whether this bubble should be moved to top of the stack.
      */
-    public void updateBubble(BubbleView bubbleView, NotificationData.Entry entry) {
-        // TODO - move to top of bubble stack, make it show its update if it makes sense
+    public void updateBubble(BubbleView bubbleView, NotificationEntry entry,
+            boolean updatePosition) {
         bubbleView.update(entry);
-        if (bubbleView.equals(mExpandedBubble)) {
+        if (updatePosition && !mIsExpanded) {
+            // If alerting it gets promoted to top of the stack
+            mBubbleContainer.removeView(bubbleView);
+            mBubbleContainer.addView(bubbleView, 0);
+            requestUpdate();
+        }
+        if (mIsExpanded && bubbleView.equals(mExpandedBubble)) {
+            entry.setShowInShadeWhenBubble(false);
             requestUpdate();
         }
     }
@@ -281,17 +315,36 @@
     }
 
     /**
+     * Collapses the stack of bubbles.
+     */
+    public void collapseStack() {
+        if (mIsExpanded) {
+            // TODO: Save opened bubble & move it to top of stack
+            animateExpansion(false /* shouldExpand */);
+            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+        }
+    }
+
+    /**
+     * Expands the stack fo bubbles.
+     */
+    public void expandStack() {
+        if (!mIsExpanded) {
+            mExpandedBubble = getTopBubble();
+            mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
+            animateExpansion(true /* shouldExpand */);
+            notifyExpansionChanged(mExpandedBubble, true /* expanded */);
+        }
+    }
+
+    /**
      * Tell the stack to animate to collapsed or expanded state.
      */
-    public void animateExpansion(boolean shouldExpand) {
+    private void animateExpansion(boolean shouldExpand) {
         if (mIsExpanded != shouldExpand) {
             mIsExpanded = shouldExpand;
-            mExpandedBubble = shouldExpand ? getTopBubble() : null;
             updateExpandedBubble();
 
-            if (mExpandListener != null) {
-                mExpandListener.onBubbleExpandChanged(mIsExpanded, 1 /* amount */);
-            }
             if (shouldExpand) {
                 // Save current position so that we might return there
                 savePosition();
@@ -341,6 +394,13 @@
         mCollapsedPosition = getPosition();
     }
 
+    private void notifyExpansionChanged(BubbleView bubbleView, boolean expanded) {
+        if (mExpandListener != null) {
+            NotificationEntry entry = bubbleView != null ? bubbleView.getEntry() : null;
+            mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null);
+        }
+    }
+
     private BubbleView getTopBubble() {
         return getBubbleAt(0);
     }
@@ -389,32 +449,78 @@
     }
 
     private void updateExpandedBubble() {
-        if (mExpandedBubble != null) {
+        if (mExpandedBubble == null) {
+            return;
+        }
+
+        if (mExpandedBubble.hasAppOverlayIntent()) {
+            // Bubble with activity view expanded state
+            ActivityView expandedView = mExpandedBubble.getActivityView();
+            expandedView.setLayoutParams(new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
+
+            final PendingIntent intent = mExpandedBubble.getAppOverlayIntent();
+            mExpandedViewContainer.setHeaderText(intent.getIntent().getComponent().toShortString());
+            mExpandedViewContainer.setExpandedView(expandedView);
+            expandedView.setCallback(new ActivityView.StateCallback() {
+                @Override
+                public void onActivityViewReady(ActivityView view) {
+                    Log.d(TAG, "onActivityViewReady("
+                            + mExpandedBubble.getEntry().key + "): " + view);
+                    view.startActivity(intent);
+                }
+
+                @Override
+                public void onActivityViewDestroyed(ActivityView view) {
+                    NotificationEntry entry = mExpandedBubble != null
+                            ? mExpandedBubble.getEntry() : null;
+                    Log.d(TAG, "onActivityViewDestroyed(key="
+                            + ((entry != null) ? entry.key : "(none)") + "): " + view);
+                }
+            });
+        } else {
+            // Bubble with notification view expanded state
             ExpandableNotificationRow row = mExpandedBubble.getRowView();
-            if (!row.equals(mExpandedViewContainer.getChildAt(0))) {
-                // Different expanded view than what we have
+            if (row.getParent() != null) {
+                // Row might still be in the shade when we expand
+                ((ViewGroup) row.getParent()).removeView(row);
+            }
+            if (mIsExpanded) {
+                mExpandedViewContainer.setExpandedView(row);
+            } else {
                 mExpandedViewContainer.setExpandedView(null);
             }
-            int pointerPosition = mExpandedBubble.getPosition().x
-                    + (mExpandedBubble.getWidth() / 2);
-            mExpandedViewContainer.setPointerPosition(pointerPosition);
-            mExpandedViewContainer.setExpandedView(row);
+            // Bubble with notification as expanded state doesn't need a header / title
+            mExpandedViewContainer.setHeaderText(null);
+
         }
+        int pointerPosition = mExpandedBubble.getPosition().x
+                + (mExpandedBubble.getWidth() / 2);
+        mExpandedViewContainer.setPointerPosition(pointerPosition);
     }
 
     private void applyCurrentState() {
+        Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
+
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         if (!mIsExpanded) {
             mExpandedViewContainer.setExpandedView(null);
         } else {
             mExpandedViewContainer.setTranslationY(mBubbleContainer.getHeight());
-            ExpandableNotificationRow row = mExpandedBubble.getRowView();
-            applyRowState(row);
+            View expandedView = mExpandedViewContainer.getExpandedView();
+            if (expandedView instanceof ActivityView) {
+                if (expandedView.isAttachedToWindow()) {
+                    ((ActivityView) expandedView).onLocationChanged();
+                }
+            } else {
+                applyRowState(mExpandedBubble.getRowView());
+            }
         }
         int bubbsCount = mBubbleContainer.getChildCount();
         for (int i = 0; i < bubbsCount; i++) {
             BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
-            bv.setZ(bubbsCount - 1);
+            bv.updateDotVisibility();
+            bv.setZ(bubbsCount - i);
 
             int transX = mIsExpanded ? (bv.getWidth() + mBubblePadding) * i : mBubblePadding * i;
             ViewState viewState = new ViewState();
@@ -468,6 +574,7 @@
     private void applyRowState(ExpandableNotificationRow view) {
         view.reset();
         view.setHeadsUp(false);
+        view.resetTranslation();
         view.setOnKeyguard(false);
         view.setOnAmbient(false);
         view.setClipBottomAmount(0);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 88030ee..97784b0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -33,7 +33,7 @@
  * Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing,
  * dismissing, and flings.
  */
-public class BubbleTouchHandler implements View.OnTouchListener {
+class BubbleTouchHandler implements View.OnTouchListener {
 
     private BubbleController mController = Dependency.get(BubbleController.class);
     private PipDismissViewController mDismissViewController;
@@ -110,7 +110,7 @@
                 : stack.getTargetView(event);
         boolean isFloating = targetView instanceof FloatingView;
         if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) {
-            stack.animateExpansion(false /* shouldExpand */);
+            stack.collapseStack();
             cleanUpDismissTarget();
             resetTouches();
             return false;
@@ -196,9 +196,13 @@
                         mMovementHelper.getTranslateAnim(floatingView, toGoTo, 100, 0).start();
                     }
                 } else if (floatingView.equals(stack.getExpandedBubble())) {
-                    stack.animateExpansion(false /* shouldExpand */);
+                    stack.collapseStack();
                 } else if (isBubbleStack) {
-                    stack.animateExpansion(!stack.isExpanded() /* shouldExpand */);
+                    if (stack.isExpanded()) {
+                        stack.collapseStack();
+                    } else {
+                        stack.expandStack();
+                    }
                 } else {
                     stack.setExpandedBubble((BubbleView) floatingView);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 6c47aac..91893ef 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -16,36 +16,49 @@
 
 package com.android.systemui.bubbles;
 
+import android.annotation.Nullable;
+import android.app.ActivityView;
 import android.app.Notification;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
 
-import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
- * A floating object on the screen that has a collapsed and expanded state.
+ * A floating object on the screen that can post message updates.
  */
-public class BubbleView extends LinearLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView {
     private static final String TAG = "BubbleView";
 
+    // Same value as Launcher3 badge code
+    private static final float WHITE_SCRIM_ALPHA = 0.54f;
     private Context mContext;
-    private View mIconView;
 
-    private NotificationData.Entry mEntry;
-    private int mBubbleSize;
-    private int mIconSize;
+    private BadgedImageView mBadgedImageView;
+    private TextView mMessageView;
+    private int mPadding;
+    private int mIconInset;
+
+    private NotificationEntry mEntry;
+    private PendingIntent mAppOverlayIntent;
+    private ActivityView mActivityView;
 
     public BubbleView(Context context) {
         this(context, null);
@@ -61,72 +74,201 @@
 
     public BubbleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        setOrientation(LinearLayout.VERTICAL);
         mContext = context;
-        mBubbleSize = getResources().getDimensionPixelSize(R.dimen.bubble_size);
-        mIconSize = getResources().getDimensionPixelSize(R.dimen.bubble_icon_size);
+        // XXX: can this padding just be on the view and we look it up?
+        mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding);
+        mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBadgedImageView = (BadgedImageView) findViewById(R.id.bubble_image);
+        mMessageView = (TextView) findViewById(R.id.message_view);
+        mMessageView.setVisibility(GONE);
+        mMessageView.setPivotX(0);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateViews();
+    }
+
+    @Override
+    protected void onMeasure(int widthSpec, int heightSpec) {
+        measureChild(mBadgedImageView, widthSpec, heightSpec);
+        measureChild(mMessageView, widthSpec, heightSpec);
+        boolean messageGone = mMessageView.getVisibility() == GONE;
+        int imageHeight = mBadgedImageView.getMeasuredHeight();
+        int imageWidth = mBadgedImageView.getMeasuredWidth();
+        int messageHeight = messageGone ? 0 : mMessageView.getMeasuredHeight();
+        int messageWidth = messageGone ? 0 : mMessageView.getMeasuredWidth();
+        setMeasuredDimension(
+                getPaddingStart() + imageWidth + mPadding + messageWidth + getPaddingEnd(),
+                getPaddingTop() + Math.max(imageHeight, messageHeight) + getPaddingBottom());
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        left = getPaddingStart();
+        top = getPaddingTop();
+        int imageWidth = mBadgedImageView.getMeasuredWidth();
+        int imageHeight = mBadgedImageView.getMeasuredHeight();
+        int messageWidth = mMessageView.getMeasuredWidth();
+        int messageHeight = mMessageView.getMeasuredHeight();
+        mBadgedImageView.layout(left, top, left + imageWidth, top + imageHeight);
+        mMessageView.layout(left + imageWidth + mPadding, top,
+                left + imageWidth + mPadding + messageWidth, top + messageHeight);
     }
 
     /**
      * Populates this view with a notification.
+     * <p>
+     * This should only be called when a new notification is being set on the view, updates to the
+     * current notification should use {@link #update(NotificationEntry)}.
      *
      * @param entry the notification to display as a bubble.
      */
-    public void setNotif(NotificationData.Entry entry) {
-        removeAllViews();
-        // TODO: migrate to inflater
-        mIconView = new ImageView(mContext);
-        addView(mIconView);
-
-        LinearLayout.LayoutParams iconLp = (LinearLayout.LayoutParams) mIconView.getLayoutParams();
-        iconLp.width = mBubbleSize;
-        iconLp.height = mBubbleSize;
-        mIconView.setLayoutParams(iconLp);
-
-        update(entry);
-    }
-
-    /**
-     * Updates the UI based on the entry.
-     */
-    public void update(NotificationData.Entry entry) {
+    public void setNotif(NotificationEntry entry) {
         mEntry = entry;
-        Notification n = entry.notification.getNotification();
-        Icon ic = n.getLargeIcon() != null ? n.getLargeIcon() : n.getSmallIcon();
-
-        if (n.getLargeIcon() == null) {
-            createCircledIcon(n.color, ic, ((ImageView) mIconView));
-        } else {
-            ((ImageView) mIconView).setImageIcon(ic);
-        }
+        updateViews();
     }
 
     /**
-     * @return the key identifying this bubble / notification entry associated with this
-     * bubble, if it exists.
+     * The {@link NotificationEntry} associated with this view, if one exists.
      */
-    public String getKey() {
-        return mEntry == null ? null : mEntry.key;
-    }
-
-    /**
-     * @return the notification entry associated with this bubble.
-     */
-    public NotificationData.Entry getEntry() {
+    @Nullable
+    public NotificationEntry getEntry() {
         return mEntry;
     }
 
     /**
-     * @return the view to display when the bubble is expanded.
+     * The key for the {@link NotificationEntry} associated with this view, if one exists.
      */
+    @Nullable
+    public String getKey() {
+        return (mEntry != null) ? mEntry.key : null;
+    }
+
+    /**
+     * Updates the UI based on the entry, updates badge and animates messages as needed.
+     */
+    public void update(NotificationEntry entry) {
+        mEntry = entry;
+        updateViews();
+    }
+
+
+    /**
+     * @return the {@link ExpandableNotificationRow} view to display notification content when the
+     * bubble is expanded.
+     */
+    @Nullable
     public ExpandableNotificationRow getRowView() {
-        return mEntry.getRow();
+        return (mEntry != null) ? mEntry.getRow() : null;
+    }
+
+    /**
+     * Marks this bubble as "read", i.e. no badge should show.
+     */
+    public void updateDotVisibility() {
+        boolean showDot = getEntry().showInShadeWhenBubble();
+        animateDot(showDot);
+    }
+
+    /**
+     * Animates the badge to show or hide.
+     */
+    private void animateDot(boolean showDot) {
+        if (mBadgedImageView.isShowingDot() != showDot) {
+            mBadgedImageView.setShowDot(showDot);
+            mBadgedImageView.clearAnimation();
+            mBadgedImageView.animate().setDuration(200)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                    .setUpdateListener((valueAnimator) -> {
+                        float fraction = valueAnimator.getAnimatedFraction();
+                        fraction = showDot ? fraction : 1 - fraction;
+                        mBadgedImageView.setDotScale(fraction);
+                    }).withEndAction(() -> {
+                        if (!showDot) {
+                            mBadgedImageView.setShowDot(false);
+                        }
+                    }).start();
+        }
+    }
+
+    private void updateViews() {
+        if (mEntry == null) {
+            return;
+        }
+        Notification n = mEntry.notification.getNotification();
+        boolean isLarge = n.getLargeIcon() != null;
+        Icon ic = isLarge ? n.getLargeIcon() : n.getSmallIcon();
+        Drawable iconDrawable = ic.loadDrawable(mContext);
+        if (!isLarge) {
+            // Center icon on coloured background
+            iconDrawable.setTint(Color.WHITE); // TODO: dark mode
+            Drawable bg = new ColorDrawable(n.color);
+            InsetDrawable d = new InsetDrawable(iconDrawable, mIconInset);
+            Drawable[] layers = {bg, d};
+            mBadgedImageView.setImageDrawable(new LayerDrawable(layers));
+        } else {
+            mBadgedImageView.setImageDrawable(iconDrawable);
+        }
+        int badgeColor = determineDominateColor(iconDrawable, n.color);
+        mBadgedImageView.setDotColor(badgeColor);
+        animateDot(mEntry.showInShadeWhenBubble() /* showDot */);
+    }
+
+    private int determineDominateColor(Drawable d, int defaultTint) {
+        // XXX: should we pull from the drawable, app icon, notif tint?
+        return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA);
+    }
+
+    /**
+     * @return a view used to display app overlay content when expanded.
+     */
+    public ActivityView getActivityView() {
+        if (mActivityView == null) {
+            mActivityView = new ActivityView(mContext);
+            Log.d(TAG, "[getActivityView] created: " + mActivityView);
+        }
+        return mActivityView;
+    }
+
+    /**
+     * Removes and releases an ActivityView if one was previously created for this bubble.
+     */
+    public void destroyActivityView(ViewGroup tmpParent) {
+        if (mActivityView == null) {
+            return;
+        }
+        // HACK: Only release if initialized. There's no way to know if the ActivityView has
+        // been initialized. Calling release() if it hasn't been initialized will crash.
+
+        if (!mActivityView.isAttachedToWindow()) {
+            // HACK: release() will crash if the view is not attached.
+
+            mActivityView.setVisibility(View.GONE);
+            tmpParent.addView(mActivityView, new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT));
+        }
+        try {
+            mActivityView.release();
+        } catch (IllegalStateException ex) {
+            Log.e(TAG, "ActivityView either already released, or not yet initialized.", ex);
+        }
+
+        ((ViewGroup) mActivityView.getParent()).removeView(mActivityView);
+        mActivityView = null;
     }
 
     @Override
     public void setPosition(int x, int y) {
-        setTranslationX(x);
-        setTranslationY(y);
+        setPositionX(x);
+        setPositionY(y);
     }
 
     @Override
@@ -144,22 +286,19 @@
         return new Point((int) getTranslationX(), (int) getTranslationY());
     }
 
-    // Seems sub optimal
-    private void createCircledIcon(int tint, Icon icon, ImageView v) {
-        // TODO: dark mode
-        icon.setTint(Color.WHITE);
-        icon.scaleDownIfNecessary(mIconSize, mIconSize);
-        v.setImageDrawable(icon.loadDrawable(mContext));
-        v.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) v.getLayoutParams();
-        int color = ContrastColorUtil.ensureContrast(tint, Color.WHITE,
-                false /* isBgDarker */, 3);
-        Drawable d = new ShapeDrawable(new OvalShape());
-        d.setTint(color);
-        v.setBackgroundDrawable(d);
+    /**
+     * @return whether an ActivityView should be used to display the content of this Bubble
+     */
+    public boolean hasAppOverlayIntent() {
+        return mAppOverlayIntent != null;
+    }
 
-        lp.width = mBubbleSize;
-        lp.height = mBubbleSize;
-        v.setLayoutParams(lp);
+    public PendingIntent getAppOverlayIntent() {
+        return mAppOverlayIntent;
+
+    }
+
+    public void setAppOverlayIntent(PendingIntent intent) {
+        mAppOverlayIntent = intent;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 1718cff..4ff27b1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -48,7 +48,7 @@
  *
  * It does not collect touch events when the bouncer shows up.
  */
-public class FalsingManager implements SensorEventListener {
+public class FalsingManager implements SensorEventListener, StateListener {
     private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
 
     private static final int[] CLASSIFIER_SENSORS = new int[] {
@@ -84,8 +84,6 @@
     private boolean mShowingAod;
     private Runnable mPendingWtf;
 
-    private final StateListener mStateListener = this::setStatusBarState;
-
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange) {
@@ -108,7 +106,7 @@
                 UserHandle.USER_ALL);
 
         updateConfiguration();
-        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     public static FalsingManager getInstance(Context context) {
@@ -282,14 +280,15 @@
         updateSessionActive();
     }
 
-    private void setStatusBarState(int state) {
+    @Override
+    public void onStateChanged(int newState) {
         if (FalsingLog.ENABLED) {
             FalsingLog.i("setStatusBarState", new StringBuilder()
                     .append("from=").append(StatusBarState.toShortString(mState))
-                    .append(" to=").append(StatusBarState.toShortString(state))
+                    .append(" to=").append(StatusBarState.toShortString(newState))
                     .toString());
         }
-        mState = state;
+        mState = newState;
         updateSessionActive();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 4cb1fee..bd7a421 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -38,7 +38,13 @@
     void setAnimateWakeup(boolean animateWakeup);
     void setAnimateScreenOff(boolean animateScreenOff);
 
-    void onDoubleTap(float x, float y);
+    /**
+     * Reports that a tap event happend on the Sensors Low Power Island.
+     *
+     * @param x Touch X, or -1 if sensor doesn't support touch location.
+     * @param y Touch Y, or -1 if sensor doesn't support touch location.
+     */
+    void onSlpiTap(float x, float y);
 
     default void setAodDimmingScrim(float scrimOpacity) {}
     void setDozeScreenBrightness(int value);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 50003e3..a784773 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    private static final int REASONS = 9;
+    private static final int REASONS = 10;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -47,6 +47,7 @@
     public static final int PULSE_REASON_DOCKING = 6;
     public static final int REASON_SENSOR_WAKE_UP = 7;
     public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
+    public static final int PULSE_REASON_SENSOR_TAP = 9;
 
     private static boolean sRegisterKeyguardCallback = true;
 
@@ -207,6 +208,7 @@
             case PULSE_REASON_DOCKING: return "docking";
             case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
             case REASON_SENSOR_WAKE_UP: return "wakeup";
+            case PULSE_REASON_SENSOR_TAP: return "tap";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index b6fc355..78374a0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -107,6 +107,13 @@
                         dozeParameters.doubleTapReportsTouchCoordinates(),
                         true /* touchscreen */),
                 new TriggerSensor(
+                        findSensorWithType(config.tapSensorType()),
+                        Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
+                        true /* configured */,
+                        DozeLog.PULSE_REASON_SENSOR_TAP,
+                        false /* reports touch coordinates */,
+                        true /* touchscreen */),
+                new TriggerSensor(
                         findSensorWithType(config.longPressSensorType()),
                         Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                         false /* settingDef */,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 6a9b689..e2e448b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -132,6 +132,7 @@
     private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
             float screenX, float screenY, float[] rawValues) {
         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
+        boolean isTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_TAP;
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
         boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
@@ -148,8 +149,10 @@
                     // In pocket, drop event.
                     return;
                 }
-                if (isDoubleTap) {
-                    mDozeHost.onDoubleTap(screenX, screenY);
+                if (isDoubleTap || isTap) {
+                    if (screenX != -1 && screenY != -1) {
+                        mDozeHost.onSlpiTap(screenX, screenY);
+                    }
                     mMachine.wakeUp();
                 } else if (isPickup) {
                     mMachine.wakeUp();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 268245b..7b18fad 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -67,10 +67,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.internal.R;
@@ -86,8 +84,8 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
-import com.android.systemui.HardwareUiLayout;
 import com.android.systemui.Interpolators;
+import com.android.systemui.MultiListLayout;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -490,6 +488,11 @@
         public boolean showBeforeProvisioning() {
             return true;
         }
+
+        @Override
+        public boolean shouldBeSeparated() {
+            return true;
+        }
     }
 
     private final class RestartAction extends SinglePressAction implements LongPressAction {
@@ -926,6 +929,34 @@
             return getItem(position).isEnabled();
         }
 
+        public ArrayList<Action> getSeparatedActions(boolean shouldUseSeparatedView) {
+            ArrayList<Action> separatedActions = new ArrayList<Action>();
+            if (!shouldUseSeparatedView) {
+                return separatedActions;
+            }
+            for (int i = 0; i < mItems.size(); i++) {
+                final Action action = mItems.get(i);
+                if (action.shouldBeSeparated()) {
+                    separatedActions.add(action);
+                }
+            }
+            return separatedActions;
+        }
+
+        public ArrayList<Action> getListActions(boolean shouldUseSeparatedView) {
+            if (!shouldUseSeparatedView) {
+                return new ArrayList<Action>(mItems);
+            }
+            ArrayList<Action> listActions = new ArrayList<Action>();
+            for (int i = 0; i < mItems.size(); i++) {
+                final Action action = mItems.get(i);
+                if (!action.shouldBeSeparated()) {
+                    listActions.add(action);
+                }
+            }
+            return listActions;
+        }
+
         @Override
         public boolean areAllItemsEnabled() {
             return false;
@@ -965,7 +996,7 @@
             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
             // Everything but screenshot, the last item, gets white background.
             if (position == getCount() - 1) {
-                HardwareUiLayout.get(parent).setDivisionView(view);
+                MultiListLayout.get(parent).setDivisionView(view);
             }
             return view;
         }
@@ -1004,6 +1035,10 @@
         boolean showBeforeProvisioning();
 
         boolean isEnabled();
+
+        default boolean shouldBeSeparated() {
+            return false;
+        }
     }
 
     /**
@@ -1423,9 +1458,7 @@
 
         private final Context mContext;
         private final MyAdapter mAdapter;
-        private final LinearLayout mListView;
-        private final FrameLayout mSeparatedView;
-        private final HardwareUiLayout mHardwareLayout;
+        private final MultiListLayout mGlobalActionsLayout;
         private final OnClickListener mClickListener;
         private final OnItemLongClickListener mLongClickListener;
         private final GradientDrawable mGradientDrawable;
@@ -1466,16 +1499,11 @@
             window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
 
             setContentView(com.android.systemui.R.layout.global_actions_wrapped);
-            mListView = findViewById(android.R.id.list);
-            mSeparatedView = findViewById(com.android.systemui.R.id.separated_button);
-            if (!mShouldDisplaySeparatedButton) {
-                mSeparatedView.setVisibility(View.GONE);
-            }
-            mHardwareLayout = HardwareUiLayout.get(mListView);
-            mHardwareLayout.setOutsideTouchListener(view -> dismiss());
-            mHardwareLayout.setHasSeparatedButton(mShouldDisplaySeparatedButton);
-            setTitle(R.string.global_actions);
-            mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            mGlobalActionsLayout = (MultiListLayout)
+                    findViewById(com.android.systemui.R.id.global_actions_view);
+            mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
+            mGlobalActionsLayout.setHasSeparatedView(mShouldDisplaySeparatedButton);
+            mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() {
                 @Override
                 public boolean dispatchPopulateAccessibilityEvent(
                         View host, AccessibilityEvent event) {
@@ -1484,20 +1512,33 @@
                     return true;
                 }
             });
+            setTitle(R.string.global_actions);
         }
 
         private void updateList() {
-            mListView.removeAllViews();
-            mSeparatedView.removeAllViews();
+            mGlobalActionsLayout.removeAllItems();
+            ArrayList<Action> separatedActions =
+                    mAdapter.getSeparatedActions(mShouldDisplaySeparatedButton);
+            ArrayList<Action> listActions = mAdapter.getListActions(mShouldDisplaySeparatedButton);
+            mGlobalActionsLayout.setExpectedListItemCount(listActions.size());
+            mGlobalActionsLayout.setExpectedSeparatedItemCount(separatedActions.size());
+
             for (int i = 0; i < mAdapter.getCount(); i++) {
-                ViewGroup parentView = mShouldDisplaySeparatedButton && i == mAdapter.getCount() - 1
-                        ? mSeparatedView : mListView;
-                View v = mAdapter.getView(i, null, parentView);
+                Action action = mAdapter.getItem(i);
+                int separatedIndex = separatedActions.indexOf(action);
+                ViewGroup parent;
+                if (separatedIndex != -1) {
+                    parent = mGlobalActionsLayout.getParentView(true, separatedIndex);
+                } else {
+                    int listIndex = listActions.indexOf(action);
+                    parent = mGlobalActionsLayout.getParentView(false, listIndex);
+                }
+                View v = mAdapter.getView(i, null, parent);
                 final int pos = i;
                 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
                 v.setOnLongClickListener(view ->
                         mLongClickListener.onItemLongClick(null, v, pos, 0));
-                parentView.addView(v);
+                parent.addView(v);
             }
         }
 
@@ -1543,9 +1584,9 @@
             super.show();
             mShowing = true;
             mGradientDrawable.setAlpha(0);
-            mHardwareLayout.setTranslationX(getAnimTranslation());
-            mHardwareLayout.setAlpha(0);
-            mHardwareLayout.animate()
+            mGlobalActionsLayout.setTranslationX(getAnimTranslation());
+            mGlobalActionsLayout.setAlpha(0);
+            mGlobalActionsLayout.animate()
                     .alpha(1)
                     .translationX(0)
                     .setDuration(300)
@@ -1564,9 +1605,9 @@
                 return;
             }
             mShowing = false;
-            mHardwareLayout.setTranslationX(0);
-            mHardwareLayout.setAlpha(1);
-            mHardwareLayout.animate()
+            mGlobalActionsLayout.setTranslationX(0);
+            mGlobalActionsLayout.setAlpha(1);
+            mGlobalActionsLayout.animate()
                     .alpha(0)
                     .translationX(getAnimTranslation())
                     .setDuration(300)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 6a0e8ad..c4c8bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -50,9 +50,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -68,7 +68,7 @@
  */
 public class KeyguardSliceProvider extends SliceProvider implements
         NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback,
-        NotificationMediaManager.MediaListener {
+        NotificationMediaManager.MediaListener, StatusBarStateController.StateListener {
 
     private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
     public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
@@ -109,7 +109,9 @@
     private AlarmManager.AlarmClockInfo mNextAlarmInfo;
     private PendingIntent mPendingIntent;
     protected NotificationMediaManager mMediaManager;
+    private StatusBarStateController mStatusBarStateController;
     protected MediaMetadata mMediaMetaData;
+    protected boolean mDozing;
 
     /**
      * Receiver responsible for time ticking and updating the date format.
@@ -167,9 +169,20 @@
         mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
     }
 
-    public void initDependencies() {
-        mMediaManager = Dependency.get(NotificationMediaManager.class);
+    /**
+     * Initialize dependencies that don't exist during {@link android.content.ContentProvider}
+     * instantiation.
+     *
+     * @param mediaManager {@link NotificationMediaManager} singleton.
+     * @param statusBarStateController {@link StatusBarStateController} singleton.
+     */
+    public void initDependencies(
+            NotificationMediaManager mediaManager,
+            StatusBarStateController statusBarStateController) {
+        mMediaManager = mediaManager;
         mMediaManager.addCallback(this);
+        mStatusBarStateController = statusBarStateController;
+        mStatusBarStateController.addCallback(this);
     }
 
     @AnyThread
@@ -179,7 +192,7 @@
         Slice slice;
         synchronized (this) {
             ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
-            if (mMediaMetaData != null) {
+            if (needsMediaLocked()) {
                 addMediaLocked(builder);
             } else {
                 builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
@@ -193,6 +206,10 @@
         return slice;
     }
 
+    protected boolean needsMediaLocked() {
+        return mMediaMetaData != null && mDozing;
+    }
+
     protected void addMediaLocked(ListBuilder listBuilder) {
         if (mMediaMetaData != null) {
             SpannableStringBuilder builder = new SpannableStringBuilder();
@@ -209,7 +226,7 @@
             }
 
             RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
-            Icon notificationIcon = mMediaManager.getMediaIcon();
+            Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
             if (notificationIcon != null) {
                 IconCompat icon = IconCompat.createFromIcon(notificationIcon);
                 mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
@@ -389,13 +406,35 @@
 
     @Override
     public void onMetadataChanged(MediaMetadata metadata) {
+        final boolean notify;
         synchronized (this) {
+            boolean neededMedia = needsMediaLocked();
             mMediaMetaData = metadata;
+            notify = neededMedia != needsMediaLocked();
         }
-        notifyChange();
+        if (notify) {
+            notifyChange();
+        }
     }
 
     protected void notifyChange() {
         mContentResolver.notifyChange(mSliceUri, null /* observer */);
     }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        final boolean notify;
+        synchronized (this) {
+            boolean neededMedia = needsMediaLocked();
+            mDozing = isDozing;
+            notify = neededMedia != needsMediaLocked();
+        }
+        if (notify) {
+            notifyChange();
+        }
+    }
+
+    @Override
+    public void onStateChanged(int newState) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 323cf1f..f14495b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1843,6 +1843,13 @@
      */
     private void handleHide() {
         Trace.beginSection("KeyguardViewMediator#handleHide");
+
+        // It's possible that the device was unlocked in a dream state. It's time to wake up.
+        if (mAodShowing) {
+            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:BOUNCER_DOZING");
+        }
+
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleHide");
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
index 1e0d4d0..b09d6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
@@ -36,8 +36,7 @@
     private Handler mHandler;
     private IActivityManager mActivityManager;
     private AppOpsManager mAppOpsManager;
-
-    private PipMotionHelper mMotionHelper;
+    private Callback mCallback;
 
     private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
         @Override
@@ -52,7 +51,7 @@
                     if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
                             mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
                                     packageName) != MODE_ALLOWED) {
-                        mHandler.post(() -> mMotionHelper.dismissPip());
+                        mHandler.post(() -> mCallback.dismissPip());
                     }
                 }
             } catch (NameNotFoundException e) {
@@ -63,12 +62,12 @@
     };
 
     public PipAppOpsListener(Context context, IActivityManager activityManager,
-            PipMotionHelper motionHelper) {
+            Callback callback) {
         mContext = context;
         mHandler = new Handler(mContext.getMainLooper());
         mActivityManager = activityManager;
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        mMotionHelper = motionHelper;
+        mCallback = callback;
     }
 
     public void onActivityPinned(String packageName) {
@@ -89,4 +88,10 @@
     private void unregisterAppOpsListener() {
         mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
     }
-}
\ No newline at end of file
+
+    /** Callback for PipAppOpsListener to request changes to the PIP window. */
+    public interface Callback {
+        /** Dismisses the PIP window. */
+        void dismissPip();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 3858356..82aa473 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -55,7 +55,7 @@
 /**
  * A helper to animate and manipulate the PiP.
  */
-public class PipMotionHelper implements Handler.Callback {
+public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback {
 
     private static final String TAG = "PipMotionHelper";
     private static final boolean DEBUG = false;
@@ -172,7 +172,8 @@
     /**
      * Dismisses the pinned stack.
      */
-    void dismissPip() {
+    @Override
+    public void dismissPip() {
         if (DEBUG) {
             Log.d(TAG, "dismissPip: callers=\n" + Debug.getCallers(5, "    "));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 01ee5ca..77e25e3 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -20,6 +20,7 @@
 import android.content.DialogInterface
 import android.content.Intent
 import android.content.res.ColorStateList
+import android.util.IconDrawableFactory
 import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
@@ -37,11 +38,22 @@
 
     private val iconSize = context.resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_dialog_icon_size)
+    private val plusSize = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_app_plus_size)
     private val iconColor = context.resources.getColor(
             com.android.internal.R.color.text_color_primary, context.theme)
+    private val plusColor: Int
     private val iconMargin = context.resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_dialog_icon_margin)
     private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
+    private val iconFactory = IconDrawableFactory.newInstance(context, true)
+
+    init {
+        val a = context.theme.obtainStyledAttributes(
+                intArrayOf(com.android.internal.R.attr.colorAccent))
+        plusColor = a.getColor(0, 0)
+        a.recycle()
+    }
 
     fun createDialog(): Dialog {
         val builder = AlertDialog.Builder(context).apply {
@@ -52,7 +64,8 @@
 
                         @Suppress("DEPRECATION")
                         override fun onClick(dialog: DialogInterface?, which: Int) {
-                            Dependency.get(ActivityStarter::class.java).startActivity(intent, false)
+                            Dependency.get(ActivityStarter::class.java)
+                                    .postStartActivityDismissingKeyguard(intent, 0)
                         }
                     })
         }
@@ -86,9 +99,15 @@
                     numItems - MAX_ITEMS
             )
             val overflowPlus = overflow.findViewById(R.id.app_icon) as ImageView
+            val lp = overflowPlus.layoutParams.apply {
+                height = plusSize
+                width = plusSize
+            }
+            overflowPlus.layoutParams = lp
             overflowPlus.apply {
-                imageTintList = ColorStateList.valueOf(iconColor)
-                setImageDrawable(context.getDrawable(R.drawable.plus))
+                val plus = context.getDrawable(R.drawable.plus)
+                imageTintList = ColorStateList.valueOf(plusColor)
+                setImageDrawable(plus)
             }
         }
 
@@ -113,17 +132,18 @@
         }
 
         app.icon.let {
-            appIcon.setImageDrawable(it)
+            appIcon.setImageDrawable(iconFactory.getShadowedIcon(it))
         }
 
         appName.text = app.applicationName
         if (showIcons) {
-            dialogBuilder.generateIconsForApp(types).forEach {
+            dialogBuilder.generateIconsForApp(types).forEachIndexed { index, it ->
                 it.setBounds(0, 0, iconSize, iconSize)
                 val image = ImageView(context).apply {
                     imageTintList = ColorStateList.valueOf(iconColor)
                     setImageDrawable(it)
                 }
+                image.contentDescription = types[index].getName(context)
                 icons.addView(image, lp)
             }
             icons.visibility = View.VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7224599..75ab5df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -141,14 +141,11 @@
     private View mStatusSeparator;
     private ImageView mRingerModeIcon;
     private TextView mRingerModeTextView;
-    private BatteryMeterView mBatteryMeterView;
     private Clock mClockView;
     private DateView mDateView;
     private OngoingPrivacyChip mPrivacyChip;
     private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
-    private TextView mBatteryRemainingText;
-    private boolean mShowBatteryPercentAndEstimate;
 
     private PrivacyItemController mPrivacyItemController;
     /** Counts how many times the long press tooltip has been shown to the user. */
@@ -229,13 +226,6 @@
         // Set the correct tint for the status icons so they contrast
         mIconManager.setTint(fillColor);
 
-        mShowBatteryPercentAndEstimate = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_battery_percentage_setting_available);
-
-        mBatteryMeterView = findViewById(R.id.battery);
-        mBatteryMeterView.setPercentShowMode(mShowBatteryPercentAndEstimate
-                ? BatteryMeterView.MODE_ON : BatteryMeterView.MODE_OFF);
-        mBatteryMeterView.setOnClickListener(this);
         mClockView = findViewById(R.id.clock);
         mClockView.setOnClickListener(this);
         mDateView = findViewById(R.id.date);
@@ -245,13 +235,8 @@
 
         // Tint for the battery icons are handled in setupHost()
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
-        mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_OFF);
         // Don't need to worry about tuner settings for this icon
         mBatteryRemainingIcon.setIgnoreTunerUpdates(true);
-
-        mBatteryRemainingText = findViewById(R.id.batteryRemainingText);
-        mBatteryRemainingText.setTextColor(fillColor);
-
         updateShowPercent();
     }
 
@@ -268,10 +253,8 @@
     }
 
     private void setChipVisibility(boolean chipVisible) {
-        mBatteryMeterView.setVisibility(View.VISIBLE);
         if (chipVisible) {
             mPrivacyChip.setVisibility(View.VISIBLE);
-            if (mHasTopCutout) mBatteryMeterView.setVisibility(View.GONE);
         } else {
             mPrivacyChip.setVisibility(View.GONE);
         }
@@ -339,7 +322,6 @@
         // Update color schemes in landscape to use wallpaperTextColor
         boolean shouldUseWallpaperTextColor =
                 newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
-        mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
         mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
     }
 
@@ -415,17 +397,11 @@
                 .build();
     }
 
-    private void updateBatteryRemainingText() {
-        if (!mShowBatteryPercentAndEstimate) {
-            return;
-        }
-        mBatteryRemainingText.setText(mBatteryController.getEstimatedTimeRemainingString());
-    }
-
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
         mHeaderQsPanel.setExpanded(expanded);
+        updateEverything();
     }
 
     /**
@@ -518,7 +494,6 @@
             }
         }
         mSpace.setLayoutParams(lp);
-        // Decide whether to show BatteryMeterView
         setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
         return super.onApplyWindowInsets(insets);
     }
@@ -545,7 +520,6 @@
             mAlarmController.addCallback(this);
             mContext.registerReceiver(mRingerReceiver,
                     new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
-            updateBatteryRemainingText();
         } else {
             mZenController.removeCallback(this);
             mAlarmController.removeCallback(this);
@@ -558,9 +532,6 @@
         if (v == mClockView) {
             mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     AlarmClock.ACTION_SHOW_ALARMS),0);
-        } else if (v == mBatteryMeterView) {
-            mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
-                    Intent.ACTION_POWER_USAGE_SUMMARY),0);
         } else if (v == mPrivacyChip) {
             Handler mUiHandler = new Handler(Looper.getMainLooper());
             mUiHandler.post(() -> {
@@ -697,6 +668,10 @@
                 .start();
     }
 
+    public void updateEverything() {
+        post(() -> setClickable(!mExpanded));
+    }
+
     public void setQSPanel(final QSPanel qsPanel) {
         mQsPanel = qsPanel;
         setupHost(qsPanel.getHost());
@@ -708,9 +683,6 @@
         mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
         mHeaderQsPanel.setHost(host, null /* No customization in header */);
 
-        // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
-        mBatteryMeterView.setColorsFromContext(mHost.getContext());
-        mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
 
         Rect tintArea = new Rect(0, 0, 0, 0);
         int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
@@ -758,22 +730,8 @@
                 .getIntForUser(getContext().getContentResolver(),
                         SHOW_BATTERY_PERCENT, 0, ActivityManager.getCurrentUser());
 
-        mShowBatteryPercentAndEstimate = systemSetting;
-
-        updateBatteryViews();
-    }
-
-    private void updateBatteryViews() {
-        if (mShowBatteryPercentAndEstimate) {
-            mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ON);
-            mBatteryRemainingIcon.setVisibility(View.VISIBLE);
-            mBatteryRemainingText.setVisibility(View.VISIBLE);
-            updateBatteryRemainingText();
-        } else {
-            mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_OFF);
-            mBatteryRemainingIcon.setVisibility(View.GONE);
-            mBatteryRemainingText.setVisibility(View.GONE);
-        }
+        mBatteryRemainingIcon.setPercentShowMode(systemSetting
+                ? BatteryMeterView.MODE_ESTIMATE : BatteryMeterView.MODE_ON);
     }
 
     private final class PercentSettingObserver extends ContentObserver {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java
index 5230cea..7ee37d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java
@@ -84,7 +84,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return null;
+        return new Intent();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 81757d0..c474faf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,6 +101,7 @@
     private float mBackButtonAlpha;
     private MotionEvent mStatusBarGestureDownEvent;
     private float mWindowCornerRadius;
+    private boolean mSupportsRoundedCornersOnWindows;
 
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
@@ -244,6 +245,18 @@
             }
         }
 
+        public boolean supportsRoundedCornersOnWindows() {
+            if (!verifyCaller("supportsRoundedCornersOnWindows")) {
+                return false;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                return mSupportsRoundedCornersOnWindows;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
@@ -353,6 +366,8 @@
         mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
                 getDefaultInteractionFlags());
         mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
+        mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils
+                .supportsRoundedCornersOnWindows(mContext.getResources());
 
         // Listen for the package update changes.
         if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index bc38169..a776d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.NotificationData.Entry;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
@@ -29,6 +27,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.util.stream.Stream;
@@ -48,7 +47,7 @@
      * NotificationManagerService side, but we keep it to prevent the UI from looking weird and
      * will remove when possible. See {@link NotificationLifetimeExtender}
      */
-    protected final ArraySet<Entry> mExtendedLifetimeAlertEntries = new ArraySet<>();
+    protected final ArraySet<NotificationEntry> mExtendedLifetimeAlertEntries = new ArraySet<>();
 
     protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
     protected int mMinimumDisplayTime;
@@ -61,7 +60,7 @@
      * Adds the notification to be managed.
      * @param entry entry to show
      */
-    public void showNotification(@NonNull Entry entry) {
+    public void showNotification(@NonNull NotificationEntry entry) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "showNotification");
         }
@@ -139,7 +138,7 @@
      * @return the entry
      */
     @Nullable
-    public Entry getEntry(@NonNull String key) {
+    public NotificationEntry getEntry(@NonNull String key) {
         AlertEntry entry = mAlertEntries.get(key);
         return entry != null ? entry.mEntry : null;
     }
@@ -149,7 +148,7 @@
      * @return all entries
      */
     @NonNull
-    public Stream<Entry> getAllEntries() {
+    public Stream<NotificationEntry> getAllEntries() {
         return mAlertEntries.values().stream().map(headsUpEntry -> headsUpEntry.mEntry);
     }
 
@@ -180,7 +179,7 @@
      * Add a new entry and begin managing it.
      * @param entry the entry to add
      */
-    protected final void addAlertEntry(@NonNull Entry entry) {
+    protected final void addAlertEntry(@NonNull NotificationEntry entry) {
         AlertEntry alertEntry = createAlertEntry();
         alertEntry.setEntry(entry);
         mAlertEntries.put(entry.key, alertEntry);
@@ -203,7 +202,7 @@
         if (alertEntry == null) {
             return;
         }
-        Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         mAlertEntries.remove(key);
         onAlertEntryRemoved(alertEntry);
         entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -250,12 +249,12 @@
     }
 
     @Override
-    public boolean shouldExtendLifetime(Entry entry) {
+    public boolean shouldExtendLifetime(NotificationEntry entry) {
         return !canRemoveImmediately(entry.key);
     }
 
     @Override
-    public void setShouldManageLifetime(Entry entry, boolean shouldExtend) {
+    public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
         if (shouldExtend) {
             mExtendedLifetimeAlertEntries.add(entry);
         } else {
@@ -265,17 +264,17 @@
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
     protected class AlertEntry implements Comparable<AlertEntry> {
-        @Nullable public Entry mEntry;
+        @Nullable public NotificationEntry mEntry;
         public long mPostTime;
         public long mEarliestRemovaltime;
 
         @Nullable protected Runnable mRemoveAlertRunnable;
 
-        public void setEntry(@NonNull final Entry entry) {
+        public void setEntry(@NonNull final NotificationEntry entry) {
             setEntry(entry, () -> removeAlertEntry(entry.key));
         }
 
-        public void setEntry(@NonNull final Entry entry,
+        public void setEntry(@NonNull final NotificationEntry entry,
                 @Nullable Runnable removeAlertRunnable) {
             mEntry = entry;
             mRemoveAlertRunnable = removeAlertRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 9bfd4ee..a3beb96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -25,7 +25,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import javax.inject.Inject;
@@ -83,7 +83,7 @@
 
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setAmbientPulsing(true);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, true);
@@ -92,7 +92,7 @@
 
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setAmbientPulsing(false);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
@@ -131,7 +131,7 @@
          * @param entry the entry that changed
          * @param isPulsing true if the entry is now pulsing, false otherwise
          */
-        void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+        void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing);
     }
 
     private final class AmbientEntry extends AlertEntry {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6a01563..904478e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -111,7 +111,6 @@
     private static final int MSG_SHOW_CHARGING_ANIMATION       = 44 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
-    private static final int MSG_BIOMETRIC_TRY_AGAIN           = 47 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -271,11 +270,10 @@
 
         default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
                 int type, boolean requireConfirmation, int userId) { }
-        default void onBiometricAuthenticated() { }
+        default void onBiometricAuthenticated(boolean authenticated) { }
         default void onBiometricHelp(String message) { }
         default void onBiometricError(String error) { }
         default void hideBiometricDialog() { }
-        default void showBiometricTryAgain() { }
     }
 
     @VisibleForTesting
@@ -736,9 +734,9 @@
     }
 
     @Override
-    public void onBiometricAuthenticated() {
+    public void onBiometricAuthenticated(boolean authenticated) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, authenticated).sendToTarget();
         }
     }
 
@@ -763,13 +761,6 @@
         }
     }
 
-    @Override
-    public void showBiometricTryAgain() {
-        synchronized (mLock) {
-            mHandler.obtainMessage(MSG_BIOMETRIC_TRY_AGAIN).sendToTarget();
-        }
-    }
-
     private final class H extends Handler {
         private H(Looper l) {
             super(l);
@@ -991,7 +982,7 @@
                     break;
                 case MSG_BIOMETRIC_AUTHENTICATED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onBiometricAuthenticated();
+                        mCallbacks.get(i).onBiometricAuthenticated((boolean) msg.obj);
                     }
                     break;
                 case MSG_BIOMETRIC_HELP:
@@ -1024,11 +1015,6 @@
                         mCallbacks.get(i).showPinningEscapeToast();
                     }
                     break;
-                case MSG_BIOMETRIC_TRY_AGAIN:
-                    for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).showBiometricTryAgain();
-                    }
-                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 758fb7a..22d1d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -18,12 +18,14 @@
 
 import android.animation.Animator;
 import android.content.Context;
+import android.util.Log;
 import android.view.ViewPropertyAnimator;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
  * Utility class to calculate general fling animation when the finger is released.
@@ -196,9 +198,16 @@
         if (startGradient != mCachedStartGradient
                 || velocityFactor != mCachedVelocityFactor) {
             float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
-            mInterpolator = new PathInterpolator(speedup,
-                    speedup * startGradient,
-                    mLinearOutSlowInX2, mY2);
+            float x1 = speedup;
+            float y1 = speedup * startGradient;
+            float x2 = mLinearOutSlowInX2;
+            float y2 = mY2;
+            try {
+                mInterpolator = new PathInterpolator(x1, y1, x2, y2);
+            } catch (IllegalArgumentException e) {
+                throw new IllegalArgumentException("Illegal path with "
+                        + "x1=" + x1 + " y1=" + y1 + " x2=" + x2 + " y2=" + y2, e);
+            }
             mCachedStartGradient = startGradient;
             mCachedVelocityFactor = velocityFactor;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index e217777..3f1ff33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -32,7 +32,7 @@
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.List;
 
@@ -50,7 +50,7 @@
     private int mEndMargin;
     private View mIconPlaceholder;
     private TextView mTextView;
-    private NotificationData.Entry mShowingEntry;
+    private NotificationEntry mShowingEntry;
     private Rect mLayoutedIconRect = new Rect();
     private int[] mTmpPosition = new int[2];
     private boolean mFirstLayout = true;
@@ -162,7 +162,7 @@
         mTextView = findViewById(R.id.text);
     }
 
-    public void setEntry(NotificationData.Entry entry) {
+    public void setEntry(NotificationEntry entry) {
         if (entry != null) {
             mShowingEntry = entry;
             CharSequence text = entry.headsUpStatusBarText;
@@ -261,7 +261,7 @@
         return super.fitSystemWindows(insets);
     }
 
-    public NotificationData.Entry getShowingEntry() {
+    public NotificationEntry getShowingEntry() {
         return mShowingEntry;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
index ecd9814..0f295ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -2,7 +2,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * Interface for anything that may need to keep notifications managed even after
@@ -24,7 +24,7 @@
      * @param entry the entry containing the notification to check
      * @return true if the notification lifetime should be extended
      */
-    boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry);
+    boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
 
     /**
      * Sets whether or not the lifetime should be managed by the extender.  In practice, if
@@ -37,7 +37,7 @@
      * @param entry the entry that needs an extended lifetime
      * @param shouldManage true if the extender should manage the entry now, false otherwise
      */
-    void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage);
+    void setShouldManageLifetime(@NonNull NotificationEntry entry, boolean shouldManage);
 
     /**
      * The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index bc662e3..f46ded4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -18,7 +18,7 @@
 import android.service.notification.StatusBarNotification;
 import android.util.SparseArray;
 
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 public interface NotificationLockscreenUserManager {
     String PERMISSION_SELF = "com.android.systemui.permission.SELF";
@@ -55,7 +55,7 @@
 
     void updatePublicMode();
 
-    boolean needsRedaction(Entry entry);
+    boolean needsRedaction(NotificationEntry entry);
 
     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
index bba4369..d5f4d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -46,9 +46,9 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.recents.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.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
@@ -407,7 +407,7 @@
     }
 
     /** @return true if the entry needs redaction when on the lockscreen. */
-    public boolean needsRedaction(NotificationData.Entry ent) {
+    public boolean needsRedaction(NotificationEntry ent) {
         int userId = ent.notification.getUserId();
 
         boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 1bf101c..7412702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -37,7 +37,6 @@
 import android.os.Handler;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -47,9 +46,9 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -157,15 +156,10 @@
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
             public void onEntryRemoved(
-                    @Nullable Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
-                if (!lifetimeExtended) {
-                    onNotificationRemoved(key);
-                }
+                onNotificationRemoved(entry.key);
             }
         });
     }
@@ -194,7 +188,7 @@
             return null;
         }
         synchronized (mEntryManager.getNotificationData()) {
-            Entry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
+            NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
             if (entry == null || entry.expandedIcon == null) {
                 return null;
             }
@@ -216,15 +210,15 @@
         boolean metaDataChanged = false;
 
         synchronized (mEntryManager.getNotificationData()) {
-            ArrayList<Entry> activeNotifications =
+            ArrayList<NotificationEntry> activeNotifications =
                     mEntryManager.getNotificationData().getActiveNotifications();
             final int N = activeNotifications.size();
 
             // Promote the media notification with a controller in 'playing' state, if any.
-            Entry mediaNotification = null;
+            NotificationEntry mediaNotification = null;
             MediaController controller = null;
             for (int i = 0; i < N; i++) {
-                final Entry entry = activeNotifications.get(i);
+                final NotificationEntry entry = activeNotifications.get(i);
 
                 if (entry.isMediaNotification()) {
                     final MediaSession.Token token =
@@ -264,7 +258,7 @@
                             final String pkg = aController.getPackageName();
 
                             for (int i = 0; i < N; i++) {
-                                final Entry entry = activeNotifications.get(i);
+                                final NotificationEntry entry = activeNotifications.get(i);
                                 if (entry.notification.getPackageName().equals(pkg)) {
                                     if (DEBUG_MEDIA) {
                                         Log.v(TAG, "DEBUG_MEDIA: found controller matching "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 886d99e..7d6231f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -51,9 +51,9 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -103,7 +103,7 @@
      * Notifications that are already removed but are kept around because the remote input is
      * actively being used (i.e. user is typing in it).  See {@link RemoteInputActiveExtender}.
      */
-    protected final ArraySet<NotificationData.Entry> mEntriesKeptForRemoteInputActive =
+    protected final ArraySet<NotificationEntry> mEntriesKeptForRemoteInputActive =
             new ArraySet<>();
 
     // Dependencies:
@@ -253,14 +253,11 @@
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
             public void onEntryRemoved(
-                    @Nullable NotificationData.Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    @Nullable NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
                 if (removedByUser && entry != null) {
-                    onPerformRemoveNotification(entry, key);
+                    onPerformRemoveNotification(entry, entry.key);
                 }
             }
         });
@@ -272,7 +269,7 @@
         mRemoteInputController = new RemoteInputController(delegate);
         mRemoteInputController.addCallback(new RemoteInputController.Callback() {
             @Override
-            public void onRemoteInputSent(NotificationData.Entry entry) {
+            public void onRemoteInputSent(NotificationEntry entry) {
                 if (FORCE_REMOTE_INPUT_HISTORY
                         && isNotificationKeptForRemoteInputHistory(entry.key)) {
                     mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -416,7 +413,7 @@
     }
 
     @VisibleForTesting
-    void onPerformRemoveNotification(NotificationData.Entry entry, final String key) {
+    void onPerformRemoveNotification(NotificationEntry entry, final String key) {
         if (mKeysKeptForRemoteInputHistory.contains(key)) {
             mKeysKeptForRemoteInputHistory.remove(key);
         }
@@ -427,7 +424,7 @@
 
     public void onPanelCollapsed() {
         for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) {
-            NotificationData.Entry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
+            NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
             mRemoteInputController.removeRemoteInput(entry, null);
             if (mNotificationLifetimeFinishedCallback != null) {
                 mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -440,7 +437,7 @@
         return mKeysKeptForRemoteInputHistory.contains(key);
     }
 
-    public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
+    public boolean shouldKeepForRemoteInputHistory(NotificationEntry entry) {
         if (entry.isDismissed()) {
             return false;
         }
@@ -450,7 +447,7 @@
         return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput());
     }
 
-    public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
+    public boolean shouldKeepForSmartReplyHistory(NotificationEntry entry) {
         if (entry.isDismissed()) {
             return false;
         }
@@ -470,13 +467,13 @@
 
     @VisibleForTesting
     StatusBarNotification rebuildNotificationForCanceledSmartReplies(
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
                 false /* showSpinner */);
     }
 
     @VisibleForTesting
-    StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
+    StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
             CharSequence remoteInputText, boolean showSpinner) {
         StatusBarNotification sbn = entry.notification;
 
@@ -533,7 +530,7 @@
     }
 
     @VisibleForTesting
-    public Set<NotificationData.Entry> getEntriesKeptForRemoteInputActive() {
+    public Set<NotificationEntry> getEntriesKeptForRemoteInputActive() {
         return mEntriesKeptForRemoteInputActive;
     }
 
@@ -556,12 +553,12 @@
      */
     protected class RemoteInputHistoryExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             return shouldKeepForRemoteInputHistory(entry);
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 CharSequence remoteInputText = entry.remoteInputText;
@@ -602,12 +599,12 @@
      */
     protected class SmartReplyHistoryExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             return shouldKeepForSmartReplyHistory(entry);
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
@@ -640,7 +637,7 @@
      */
     protected class RemoteInputActiveExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             if (entry.isDismissed()) {
                 return false;
             }
@@ -648,7 +645,7 @@
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 91b34fc..9ef9c94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -55,7 +55,7 @@
  * overflow icons that don't fit into the regular list anymore.
  */
 public class NotificationShelf extends ActivatableNotificationView implements
-        View.OnLayoutChangeListener {
+        View.OnLayoutChangeListener, StateListener {
 
     private static final boolean USE_ANIMATIONS_WHEN_OPENING =
             SystemProperties.getBoolean("debug.icon_opening_animations", true);
@@ -65,12 +65,12 @@
     private static final String TAG = "NotificationShelf";
     private static final long SHELF_IN_TRANSLATION_DURATION = 200;
 
-    private boolean mDark;
     private NotificationIconContainer mShelfIcons;
     private int[] mTmp = new int[2];
     private boolean mHideBackground;
     private int mIconAppearTopPadding;
     private int mShelfAppearTranslation;
+    private float mDarkShelfPadding;
     private int mStatusBarHeight;
     private int mStatusBarPaddingStart;
     private AmbientState mAmbientState;
@@ -95,8 +95,6 @@
     private int mCutoutHeight;
     private int mGapHeight;
 
-    private final StateListener mStateListener = this::setStatusBarState;
-
     public NotificationShelf(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -121,13 +119,13 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Dependency.get(StatusBarStateController.class)
-                .addCallback(mStateListener, StatusBarStateController.RANK_SHELF);
+                .addCallback(this, StatusBarStateController.RANK_SHELF);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(this);
     }
 
     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
@@ -142,6 +140,7 @@
         mStatusBarPaddingStart = res.getDimensionPixelOffset(R.dimen.status_bar_padding_start);
         mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
         mShelfAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation);
+        mDarkShelfPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
 
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
         layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -167,11 +166,29 @@
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
-        super.setDark(dark, fade, delay);
         if (mDark == dark) return;
-        mDark = dark;
+        super.setDark(dark, fade, delay);
         mShelfIcons.setDark(dark, fade, delay);
         updateInteractiveness();
+        updateOutline();
+    }
+
+    /**
+     * Alpha animation with translation played when this view is visible on AOD.
+     */
+    public void fadeInTranslating() {
+        mShelfIcons.setTranslationY(-mShelfAppearTranslation);
+        mShelfIcons.setAlpha(0);
+        mShelfIcons.animate()
+                .setInterpolator(Interpolators.DECELERATE_QUINT)
+                .translationY(0)
+                .setDuration(SHELF_IN_TRANSLATION_DURATION)
+                .start();
+        mShelfIcons.animate()
+                .alpha(1)
+                .setInterpolator(Interpolators.LINEAR)
+                .setDuration(SHELF_IN_TRANSLATION_DURATION)
+                .start();
     }
 
     @Override
@@ -202,10 +219,9 @@
 
             float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
                     getFullyClosedTranslation());
-            float darkTranslation = mAmbientState.getDarkTopPadding();
             float yRatio = mAmbientState.hasPulsingNotifications() ?
                     0 : mAmbientState.getDarkAmount();
-            viewState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
+            viewState.yTranslation = awakenTranslation + mDarkShelfPadding * yRatio;
             viewState.zTranslation = ambientState.getBaseZHeight();
             // For the small display size, it's not enough to make the icon not covered by
             // the top cutout so the denominator add the height of cutout.
@@ -225,7 +241,7 @@
             }
             viewState.hasItemsInStableShelf = lastViewState.inShelf;
             viewState.hidden = !mAmbientState.isShadeExpanded()
-                    || mAmbientState.isQsCustomizerShowing() || mAmbientState.isFullyDark();
+                    || mAmbientState.isQsCustomizerShowing();
             viewState.maxShelfEnd = maxShelfEnd;
         } else {
             viewState.hidden = true;
@@ -420,7 +436,7 @@
         float maxTop = row.getTranslationY();
         StatusBarIconView icon = row.getEntry().expandedIcon;
         float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
-        if (shelfIconPosition < maxTop) {
+        if (shelfIconPosition < maxTop && !mAmbientState.isDark()) {
             int top = (int) (maxTop - shelfIconPosition);
             Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight()));
             icon.setClipBounds(clipRect);
@@ -431,7 +447,7 @@
 
     private void updateContinuousClipping(final ExpandableNotificationRow row) {
         StatusBarIconView icon = row.getEntry().expandedIcon;
-        boolean needsContinuousClipping = ViewState.isAnimatingY(icon);
+        boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark();
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
             final ViewTreeObserver observer = icon.getViewTreeObserver();
@@ -622,7 +638,9 @@
             iconState.translateContent = false;
         }
         float transitionAmount;
-        if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
+        if (mAmbientState.isDarkAtAll() && !row.isInShelf()) {
+            transitionAmount = mAmbientState.isFullyDark() ? 1 : 0;
+        } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
                 || iconState.useLinearTransitionAmount) {
             transitionAmount = iconTransitionAmount;
         } else {
@@ -745,18 +763,14 @@
         }
     }
 
-    public boolean hidesBackground() {
-        return mHideBackground;
-    }
-
     @Override
     protected boolean needsOutline() {
-        return !mHideBackground && super.needsOutline();
+        return !mHideBackground && !mDark && super.needsOutline();
     }
 
     @Override
     protected boolean shouldHideBackground() {
-        return super.shouldHideBackground() || mHideBackground;
+        return super.shouldHideBackground() || mHideBackground || mDark;
     }
 
     @Override
@@ -860,8 +874,9 @@
         mCollapsedIcons.addOnLayoutChangeListener(this);
     }
 
-    private void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
+    @Override
+    public void onStateChanged(int newState) {
+        mStatusBarState = newState;
         updateInteractiveness();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f23ae3f..f0d804d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -24,7 +24,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -52,7 +52,7 @@
     }
 
     public static NotificationUiAdjustment extractFromNotificationEntry(
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         return new NotificationUiAdjustment(
                 entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 017a9c3..f2ff85b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Trace;
@@ -26,10 +24,9 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -66,7 +63,6 @@
     protected final VisualStabilityManager mVisualStabilityManager;
     private final StatusBarStateController mStatusBarStateController;
     private final NotificationEntryManager mEntryManager;
-    private final BubbleController mBubbleController;
 
     // Lazy
     private final Lazy<ShadeController> mShadeController;
@@ -80,41 +76,6 @@
 
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
-    private StatusBarStateListener mStatusBarStateListener;
-
-    /**
-     * Listens for the current state of the status bar and updates the visibility state
-     * of bubbles as needed.
-     */
-    public class StatusBarStateListener implements StatusBarStateController.StateListener {
-        private int mState;
-        private BubbleController mController;
-
-        public StatusBarStateListener(BubbleController controller) {
-            mController = controller;
-        }
-
-        /**
-         * Returns the current status bar state.
-         */
-        public int getCurrentState() {
-            return mState;
-        }
-
-        @Override
-        public void onStateChanged(int newState) {
-            mState = newState;
-            // Order here matters because we need to remove the expandable notification row
-            // from it's current parent (NSSL or bubble) before it can be added to the new parent
-            if (mState == SHADE) {
-                updateNotificationViews();
-                mController.updateVisibility(true);
-            } else {
-                mController.updateVisibility(false);
-                updateNotificationViews();
-            }
-        }
-    }
 
     @Inject
     public NotificationViewHierarchyManager(Context context,
@@ -123,20 +84,16 @@
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
             NotificationEntryManager notificationEntryManager,
-            BubbleController bubbleController,
             Lazy<ShadeController> shadeController) {
         mLockscreenUserManager = notificationLockscreenUserManager;
         mGroupManager = groupManager;
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarStateController = statusBarStateController;
         mEntryManager = notificationEntryManager;
-        mBubbleController = bubbleController;
         mShadeController = shadeController;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
-        mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
-        mStatusBarStateController.addCallback(mStatusBarStateListener);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -150,25 +107,17 @@
      */
     //TODO: Rewrite this to focus on Entries, or some other data object instead of views
     public void updateNotificationViews() {
-        ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
+        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
         ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
-        ArrayList<NotificationData.Entry> toBubble = new ArrayList<>();
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
-            NotificationData.Entry ent = activeNotifications.get(i);
+            NotificationEntry ent = activeNotifications.get(i);
             if (ent.isRowDismissed() || ent.isRowRemoved()) {
                 // we don't want to update removed notifications because they could
                 // temporarily become children if they were isolated before.
                 continue;
             }
-            ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
-            boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
-                    && mStatusBarStateListener.getCurrentState() == SHADE;
-            if (showAsBubble) {
-                toBubble.add(ent);
-                continue;
-            }
 
             int userId = ent.notification.getUserId();
 
@@ -187,7 +136,7 @@
             ent.getRow().setSensitive(sensitive, deviceSensitive);
             ent.getRow().setNeedsRedaction(needsRedaction);
             if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
-                NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
+                NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
                 List<ExpandableNotificationRow> orderedChildren =
                         mTmpChildOrderMap.get(summary.getRow());
                 if (orderedChildren == null) {
@@ -269,12 +218,6 @@
 
         }
 
-        for (int i = 0; i < toBubble.size(); i++) {
-            // TODO: might make sense to leave them in the shade and just reposition them
-            NotificationData.Entry ent = toBubble.get(i);
-            mBubbleController.addBubble(ent);
-        }
-
         mVisualStabilityManager.onReorderingFinished();
         // clear the map again for the next usage
         mTmpChildOrderMap.clear();
@@ -385,7 +328,7 @@
         }
         while(!stack.isEmpty()) {
             ExpandableNotificationRow row = stack.pop();
-            NotificationData.Entry entry = row.getEntry();
+            NotificationEntry entry = row.getEntry();
             boolean isChildNotification =
                     mGroupManager.isChildInGroupWithSummary(entry.notification);
 
@@ -408,7 +351,7 @@
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
                 if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
-                    NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
+                    NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
                             entry.notification);
                     if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
                             summary.notification))         {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index e8abcc2..998cf52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -24,7 +24,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 
 import java.lang.ref.WeakReference;
@@ -38,7 +38,7 @@
     private static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
 
-    private final ArrayList<Pair<WeakReference<NotificationData.Entry>, Object>> mOpen
+    private final ArrayList<Pair<WeakReference<NotificationEntry>, Object>> mOpen
             = new ArrayList<>();
     private final ArrayMap<String, Object> mSpinning = new ArrayMap<>();
     private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
@@ -101,7 +101,7 @@
      * @param entry the entry for which a remote input is now active.
      * @param token a token identifying the view that is managing the remote input
      */
-    public void addRemoteInput(NotificationData.Entry entry, Object token) {
+    public void addRemoteInput(NotificationEntry entry, Object token) {
         Preconditions.checkNotNull(entry);
         Preconditions.checkNotNull(token);
 
@@ -122,7 +122,7 @@
      *              the entry is only removed if the token matches the last added token for this
      *              entry. If null, the entry is removed regardless.
      */
-    public void removeRemoteInput(NotificationData.Entry entry, Object token) {
+    public void removeRemoteInput(NotificationEntry entry, Object token) {
         Preconditions.checkNotNull(entry);
 
         pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
@@ -173,7 +173,7 @@
         return mSpinning.get(key) == token;
     }
 
-    private void apply(NotificationData.Entry entry) {
+    private void apply(NotificationEntry entry) {
         mDelegate.setRemoteInputActive(entry, isRemoteInputActive(entry));
         boolean remoteInputActive = isRemoteInputActive();
         int N = mCallbacks.size();
@@ -185,7 +185,7 @@
     /**
      * @return true if {@param entry} has an active RemoteInput
      */
-    public boolean isRemoteInputActive(NotificationData.Entry entry) {
+    public boolean isRemoteInputActive(NotificationEntry entry) {
         return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */,
                 null /* removeToken */);
     }
@@ -208,10 +208,10 @@
      * @return true if {@param contains} is in the set of active remote inputs
      */
     private boolean pruneWeakThenRemoveAndContains(
-            NotificationData.Entry contains, NotificationData.Entry remove, Object removeToken) {
+            NotificationEntry contains, NotificationEntry remove, Object removeToken) {
         boolean found = false;
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = mOpen.get(i).first.get();
+            NotificationEntry item = mOpen.get(i).first.get();
             Object itemToken = mOpen.get(i).second;
             boolean removeTokenMatches = (removeToken == null || itemToken == removeToken);
 
@@ -235,7 +235,7 @@
         mCallbacks.add(callback);
     }
 
-    public void remoteInputSent(NotificationData.Entry entry) {
+    public void remoteInputSent(NotificationEntry entry) {
         int N = mCallbacks.size();
         for (int i = 0; i < N; i++) {
             mCallbacks.get(i).onRemoteInputSent(entry);
@@ -248,16 +248,16 @@
         }
 
         // Make a copy because closing the remote inputs will modify mOpen.
-        ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
+        ArrayList<NotificationEntry> list = new ArrayList<>(mOpen.size());
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry entry = mOpen.get(i).first.get();
+            NotificationEntry entry = mOpen.get(i).first.get();
             if (entry != null && entry.rowExists()) {
                 list.add(entry);
             }
         }
 
         for (int i = list.size() - 1; i >= 0; i--) {
-            NotificationData.Entry entry = list.get(i);
+            NotificationEntry entry = list.get(i);
             if (entry.rowExists()) {
                 entry.closeRemoteInput();
             }
@@ -268,31 +268,31 @@
         mDelegate.requestDisallowLongPressAndDismiss();
     }
 
-    public void lockScrollTo(NotificationData.Entry entry) {
+    public void lockScrollTo(NotificationEntry entry) {
         mDelegate.lockScrollTo(entry);
     }
 
     public interface Callback {
         default void onRemoteInputActive(boolean active) {}
 
-        default void onRemoteInputSent(NotificationData.Entry entry) {}
+        default void onRemoteInputSent(NotificationEntry entry) {}
     }
 
     public interface Delegate {
         /**
          * Activate remote input if necessary.
          */
-        void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive);
+        void setRemoteInputActive(NotificationEntry entry, boolean remoteInputActive);
 
-       /**
-        * Request that the view does not dismiss nor perform long press for the current touch.
-        */
-       void requestDisallowLongPressAndDismiss();
+        /**
+         * Request that the view does not dismiss nor perform long press for the current touch.
+         */
+        void requestDisallowLongPressAndDismiss();
 
-      /**
-       * Request that the view is made visible by scrolling to it, and keep the scroll locked until
-       * the user scrolls, or {@param v} loses focus or is detached.
-       */
-       void lockScrollTo(NotificationData.Entry entry);
+        /**
+         * Request that the view is made visible by scrolling to it, and keep the scroll locked until
+         * the user scrolls, or {@param entry} loses focus or is detached.
+         */
+        void lockScrollTo(NotificationEntry entry);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
deleted file mode 100644
index 6d2c001..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
- * Copyright (C) 2011 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.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_NONE;
-
-import android.annotation.DrawableRes;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.telephony.SubscriptionInfo;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.settingslib.graph.SignalDrawable;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.policy.IconLogger;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.Utils.DisableStateTracker;
-
-import java.util.ArrayList;
-import java.util.List;
-
-// Intimately tied to the design of res/layout/signal_cluster_view.xml
-public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
-        SecurityController.SecurityControllerCallback, Tunable, DarkReceiver {
-
-    static final String TAG = "SignalClusterView";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private static final String SLOT_AIRPLANE = "airplane";
-    private static final String SLOT_MOBILE = "mobile";
-    private static final String SLOT_WIFI = "wifi";
-    private static final String SLOT_ETHERNET = "ethernet";
-    private static final String SLOT_VPN = "vpn";
-
-    private final NetworkController mNetworkController;
-    private final SecurityController mSecurityController;
-
-    private boolean mVpnVisible = false;
-    private int mVpnIconId = 0;
-    private int mLastVpnIconId = -1;
-    private boolean mEthernetVisible = false;
-    private int mEthernetIconId = 0;
-    private int mLastEthernetIconId = -1;
-    private boolean mWifiVisible = false;
-    private int mWifiStrengthId = 0;
-    private int mLastWifiStrengthId = -1;
-    private boolean mWifiIn;
-    private boolean mWifiOut;
-    private boolean mIsAirplaneMode = false;
-    private int mAirplaneIconId = 0;
-    private int mLastAirplaneIconId = -1;
-    private String mAirplaneContentDescription;
-    private String mWifiDescription;
-    private String mEthernetDescription;
-    private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
-    private int mIconTint = Color.WHITE;
-    private float mDarkIntensity;
-    private final Rect mTintArea = new Rect();
-
-    ViewGroup mEthernetGroup, mWifiGroup;
-    ImageView mVpn, mEthernet, mWifi, mAirplane, mEthernetDark, mWifiDark;
-    ImageView mWifiActivityIn;
-    ImageView mWifiActivityOut;
-    View mWifiAirplaneSpacer;
-    View mWifiSignalSpacer;
-    LinearLayout mMobileSignalGroup;
-
-    private final int mMobileSignalGroupEndPadding;
-    private final int mMobileDataIconStartPadding;
-    private final int mSecondaryTelephonyPadding;
-    private final int mEndPadding;
-    private final int mEndPaddingNothingVisible;
-    private final float mIconScaleFactor;
-
-    private boolean mBlockAirplane;
-    private boolean mBlockMobile;
-    private boolean mBlockWifi;
-    private boolean mBlockEthernet;
-    private boolean mActivityEnabled;
-    private boolean mForceBlockWifi;
-
-    private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
-
-    public SignalClusterView(Context context) {
-        this(context, null);
-    }
-
-    public SignalClusterView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SignalClusterView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        Resources res = getResources();
-        mMobileSignalGroupEndPadding =
-                res.getDimensionPixelSize(R.dimen.mobile_signal_group_end_padding);
-        mMobileDataIconStartPadding =
-                res.getDimensionPixelSize(R.dimen.mobile_data_icon_start_padding);
-        mSecondaryTelephonyPadding = res.getDimensionPixelSize(R.dimen.secondary_telephony_padding);
-        mEndPadding = res.getDimensionPixelSize(R.dimen.signal_cluster_battery_padding);
-        mEndPaddingNothingVisible = res.getDimensionPixelSize(
-                R.dimen.no_signal_cluster_battery_padding);
-
-        TypedValue typedValue = new TypedValue();
-        res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
-        mIconScaleFactor = typedValue.getFloat();
-        mNetworkController = Dependency.get(NetworkController.class);
-        mSecurityController = Dependency.get(SecurityController.class);
-        addOnAttachStateChangeListener(
-                new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
-        updateActivityEnabled();
-    }
-
-    public void setForceBlockWifi() {
-        mForceBlockWifi = true;
-        mBlockWifi = true;
-        if (isAttachedToWindow()) {
-            // Re-register to get new callbacks.
-            mNetworkController.removeCallback(this);
-            mNetworkController.addCallback(this);
-        }
-    }
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
-            return;
-        }
-        ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
-        boolean blockAirplane = blockList.contains(SLOT_AIRPLANE);
-        boolean blockMobile = blockList.contains(SLOT_MOBILE);
-        boolean blockWifi = blockList.contains(SLOT_WIFI);
-        boolean blockEthernet = blockList.contains(SLOT_ETHERNET);
-
-        if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
-                || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
-            mBlockAirplane = blockAirplane;
-            mBlockMobile = blockMobile;
-            mBlockEthernet = blockEthernet;
-            mBlockWifi = blockWifi || mForceBlockWifi;
-            // Re-register to get new callbacks.
-            mNetworkController.removeCallback(this);
-            mNetworkController.addCallback(this);
-        }
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mVpn            = findViewById(R.id.vpn);
-        mEthernetGroup  = findViewById(R.id.ethernet_combo);
-        mEthernet       = findViewById(R.id.ethernet);
-        mEthernetDark   = findViewById(R.id.ethernet_dark);
-        mWifiGroup      = findViewById(R.id.wifi_combo);
-        mWifi           = findViewById(R.id.wifi_signal);
-        mWifiDark       = findViewById(R.id.wifi_signal_dark);
-        mWifiActivityIn = findViewById(R.id.wifi_in);
-        mWifiActivityOut= findViewById(R.id.wifi_out);
-        mAirplane       = findViewById(R.id.airplane);
-        mWifiAirplaneSpacer =         findViewById(R.id.wifi_airplane_spacer);
-        mWifiSignalSpacer =           findViewById(R.id.wifi_signal_spacer);
-        mMobileSignalGroup =          findViewById(R.id.mobile_signal_group);
-
-        maybeScaleVpnAndNoSimsIcons();
-    }
-
-    /**
-     * Extracts the icon off of the VPN and no sims views and maybe scale them by
-     * {@link #mIconScaleFactor}. Note that the other icons are not scaled here because they are
-     * dynamic. As such, they need to be scaled each time the icon changes in {@link #apply()}.
-     */
-    private void maybeScaleVpnAndNoSimsIcons() {
-        if (mIconScaleFactor == 1.f) {
-            return;
-        }
-
-        mVpn.setImageDrawable(new ScalingDrawableWrapper(mVpn.getDrawable(), mIconScaleFactor));
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mVpnVisible = mSecurityController.isVpnEnabled();
-        mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
-
-        for (PhoneState state : mPhoneStates) {
-            if (state.mMobileGroup.getParent() == null) {
-                mMobileSignalGroup.addView(state.mMobileGroup);
-            }
-        }
-
-        int endPadding = mMobileSignalGroup.getChildCount() > 0 ? mMobileSignalGroupEndPadding : 0;
-        mMobileSignalGroup.setPaddingRelative(0, 0, endPadding, 0);
-
-        Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
-
-        apply();
-        applyIconTint();
-        mNetworkController.addCallback(this);
-        mSecurityController.addCallback(this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mMobileSignalGroup.removeAllViews();
-        Dependency.get(TunerService.class).removeTunable(this);
-        mSecurityController.removeCallback(this);
-        mNetworkController.removeCallback(this);
-
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        // Re-run all checks against the tint area for all icons
-        applyIconTint();
-    }
-
-    // From SecurityController.
-    @Override
-    public void onStateChanged() {
-        post(new Runnable() {
-            @Override
-            public void run() {
-                mVpnVisible = mSecurityController.isVpnEnabled();
-                mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
-                apply();
-            }
-        });
-    }
-
-    private void updateActivityEnabled() {
-        mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
-    }
-
-    @Override
-    public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-            boolean activityIn, boolean activityOut, String description, boolean isTransient,
-            String secondaryLabel) {
-        mWifiVisible = statusIcon.visible && !mBlockWifi;
-        mWifiStrengthId = statusIcon.icon;
-        mWifiDescription = statusIcon.contentDescription;
-        mWifiIn = activityIn && mActivityEnabled && mWifiVisible;
-        mWifiOut = activityOut && mActivityEnabled && mWifiVisible;
-
-        apply();
-    }
-
-    @Override
-    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-            int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-            String description, boolean isWide, int subId, boolean roaming) {
-        PhoneState state = getState(subId);
-        if (state == null) {
-            return;
-        }
-        state.mMobileVisible = statusIcon.visible && !mBlockMobile;
-        state.mMobileStrengthId = statusIcon.icon;
-        state.mMobileTypeId = statusType;
-        state.mMobileDescription = statusIcon.contentDescription;
-        state.mMobileTypeDescription = typeContentDescription;
-        state.mRoaming = roaming;
-        state.mActivityIn = activityIn && mActivityEnabled;
-        state.mActivityOut = activityOut && mActivityEnabled;
-
-        apply();
-    }
-
-    @Override
-    public void setEthernetIndicators(IconState state) {
-        mEthernetVisible = state.visible && !mBlockEthernet;
-        mEthernetIconId = state.icon;
-        mEthernetDescription = state.contentDescription;
-
-        apply();
-    }
-
-    @Override
-    public void setNoSims(boolean show, boolean simDetected) {
-        // Noop. Status bar no longer shows no sim icon.
-    }
-
-    @Override
-    public void setSubs(List<SubscriptionInfo> subs) {
-        if (hasCorrectSubs(subs)) {
-            return;
-        }
-        mPhoneStates.clear();
-        if (mMobileSignalGroup != null) {
-            mMobileSignalGroup.removeAllViews();
-        }
-        final int n = subs.size();
-        for (int i = 0; i < n; i++) {
-            inflatePhoneState(subs.get(i).getSubscriptionId());
-        }
-        if (isAttachedToWindow()) {
-            applyIconTint();
-        }
-    }
-
-    private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
-        final int N = subs.size();
-        if (N != mPhoneStates.size()) {
-            return false;
-        }
-        for (int i = 0; i < N; i++) {
-            if (mPhoneStates.get(i).mSubId != subs.get(i).getSubscriptionId()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private PhoneState getState(int subId) {
-        for (PhoneState state : mPhoneStates) {
-            if (state.mSubId == subId) {
-                return state;
-            }
-        }
-        Log.e(TAG, "Unexpected subscription " + subId);
-        return null;
-    }
-
-    private PhoneState inflatePhoneState(int subId) {
-        PhoneState state = new PhoneState(subId, mContext);
-        if (mMobileSignalGroup != null) {
-            mMobileSignalGroup.addView(state.mMobileGroup);
-        }
-        mPhoneStates.add(state);
-        return state;
-    }
-
-    @Override
-    public void setIsAirplaneMode(IconState icon) {
-        mIsAirplaneMode = icon.visible && !mBlockAirplane;
-        mAirplaneIconId = icon.icon;
-        mAirplaneContentDescription = icon.contentDescription;
-
-        apply();
-    }
-
-    @Override
-    public void setMobileDataEnabled(boolean enabled) {
-        // Don't care.
-    }
-
-    @Override
-    public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
-        // Standard group layout onPopulateAccessibilityEvent() implementations
-        // ignore content description, so populate manually
-        if (mEthernetVisible && mEthernetGroup != null &&
-                mEthernetGroup.getContentDescription() != null)
-            event.getText().add(mEthernetGroup.getContentDescription());
-        if (mWifiVisible && mWifiGroup != null && mWifiGroup.getContentDescription() != null)
-            event.getText().add(mWifiGroup.getContentDescription());
-        for (PhoneState state : mPhoneStates) {
-            state.populateAccessibilityEvent(event);
-        }
-        return super.dispatchPopulateAccessibilityEventInternal(event);
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-
-        if (mEthernet != null) {
-            mEthernet.setImageDrawable(null);
-            mEthernetDark.setImageDrawable(null);
-            mLastEthernetIconId = -1;
-        }
-
-        if (mWifi != null) {
-            mWifi.setImageDrawable(null);
-            mWifiDark.setImageDrawable(null);
-            mLastWifiStrengthId = -1;
-        }
-
-        for (PhoneState state : mPhoneStates) {
-            if (state.mMobileType != null) {
-                state.mMobileType.setImageDrawable(null);
-                state.mLastMobileTypeId = -1;
-            }
-        }
-
-        if (mAirplane != null) {
-            mAirplane.setImageDrawable(null);
-            mLastAirplaneIconId = -1;
-        }
-
-        apply();
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    // Run after each indicator change.
-    private void apply() {
-        if (mWifiGroup == null) return;
-
-        if (mVpnVisible) {
-            if (mLastVpnIconId != mVpnIconId) {
-                setIconForView(mVpn, mVpnIconId);
-                mLastVpnIconId = mVpnIconId;
-            }
-            mIconLogger.onIconShown(SLOT_VPN);
-            mVpn.setVisibility(View.VISIBLE);
-        } else {
-            mIconLogger.onIconHidden(SLOT_VPN);
-            mVpn.setVisibility(View.GONE);
-        }
-        if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE"));
-
-        if (mEthernetVisible) {
-            if (mLastEthernetIconId != mEthernetIconId) {
-                setIconForView(mEthernet, mEthernetIconId);
-                setIconForView(mEthernetDark, mEthernetIconId);
-                mLastEthernetIconId = mEthernetIconId;
-            }
-            mEthernetGroup.setContentDescription(mEthernetDescription);
-            mIconLogger.onIconShown(SLOT_ETHERNET);
-            mEthernetGroup.setVisibility(View.VISIBLE);
-        } else {
-            mIconLogger.onIconHidden(SLOT_ETHERNET);
-            mEthernetGroup.setVisibility(View.GONE);
-        }
-
-        if (DEBUG) Log.d(TAG,
-                String.format("ethernet: %s",
-                    (mEthernetVisible ? "VISIBLE" : "GONE")));
-
-        if (mWifiVisible) {
-            if (mWifiStrengthId != mLastWifiStrengthId) {
-                setIconForView(mWifi, mWifiStrengthId);
-                setIconForView(mWifiDark, mWifiStrengthId);
-                mLastWifiStrengthId = mWifiStrengthId;
-            }
-            mIconLogger.onIconShown(SLOT_WIFI);
-            mWifiGroup.setContentDescription(mWifiDescription);
-            mWifiGroup.setVisibility(View.VISIBLE);
-        } else {
-            mIconLogger.onIconHidden(SLOT_WIFI);
-            mWifiGroup.setVisibility(View.GONE);
-        }
-
-        if (DEBUG) Log.d(TAG,
-                String.format("wifi: %s sig=%d",
-                    (mWifiVisible ? "VISIBLE" : "GONE"),
-                    mWifiStrengthId));
-
-        mWifiActivityIn.setVisibility(mWifiIn ? View.VISIBLE : View.GONE);
-        mWifiActivityOut.setVisibility(mWifiOut ? View.VISIBLE : View.GONE);
-
-        boolean anyMobileVisible = false;
-        int firstMobileTypeId = 0;
-        for (PhoneState state : mPhoneStates) {
-            if (state.apply(anyMobileVisible)) {
-                if (!anyMobileVisible) {
-                    firstMobileTypeId = state.mMobileTypeId;
-                    anyMobileVisible = true;
-                }
-            }
-        }
-        if (anyMobileVisible) {
-            mIconLogger.onIconShown(SLOT_MOBILE);
-        } else {
-            mIconLogger.onIconHidden(SLOT_MOBILE);
-        }
-
-        if (mIsAirplaneMode) {
-            if (mLastAirplaneIconId != mAirplaneIconId) {
-                setIconForView(mAirplane, mAirplaneIconId);
-                mLastAirplaneIconId = mAirplaneIconId;
-            }
-            mAirplane.setContentDescription(mAirplaneContentDescription);
-            mIconLogger.onIconShown(SLOT_AIRPLANE);
-            mAirplane.setVisibility(View.VISIBLE);
-        } else {
-            mIconLogger.onIconHidden(SLOT_AIRPLANE);
-            mAirplane.setVisibility(View.GONE);
-        }
-
-        if (mIsAirplaneMode && mWifiVisible) {
-            mWifiAirplaneSpacer.setVisibility(View.VISIBLE);
-        } else {
-            mWifiAirplaneSpacer.setVisibility(View.GONE);
-        }
-
-        if ((anyMobileVisible && firstMobileTypeId != 0) && mWifiVisible) {
-            mWifiSignalSpacer.setVisibility(View.VISIBLE);
-        } else {
-            mWifiSignalSpacer.setVisibility(View.GONE);
-        }
-
-        boolean anythingVisible = mWifiVisible || mIsAirplaneMode
-                || anyMobileVisible || mVpnVisible || mEthernetVisible;
-        setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
-    }
-
-    /**
-     * Sets the given drawable id on the view. This method will also scale the icon by
-     * {@link #mIconScaleFactor} if appropriate.
-     */
-    private void setIconForView(ImageView imageView, @DrawableRes int iconId) {
-        // Using the imageView's context to retrieve the Drawable so that theme is preserved.
-        Drawable icon = imageView.getContext().getDrawable(iconId);
-
-        if (mIconScaleFactor == 1.f) {
-            imageView.setImageDrawable(icon);
-        } else {
-            imageView.setImageDrawable(new ScalingDrawableWrapper(icon, mIconScaleFactor));
-        }
-    }
-
-
-    @Override
-    public void onDarkChanged(Rect tintArea, float darkIntensity, int tint) {
-        boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
-                || !mTintArea.equals(tintArea);
-        mIconTint = tint;
-        mDarkIntensity = darkIntensity;
-        mTintArea.set(tintArea);
-        if (changed && isAttachedToWindow()) {
-            applyIconTint();
-        }
-    }
-
-    private void applyIconTint() {
-        setTint(mVpn, DarkIconDispatcher.getTint(mTintArea, mVpn, mIconTint));
-        setTint(mAirplane, DarkIconDispatcher.getTint(mTintArea, mAirplane, mIconTint));
-        applyDarkIntensity(
-                DarkIconDispatcher.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
-                mWifi, mWifiDark);
-        setTint(mWifiActivityIn,
-                DarkIconDispatcher.getTint(mTintArea, mWifiActivityIn, mIconTint));
-        setTint(mWifiActivityOut,
-                DarkIconDispatcher.getTint(mTintArea, mWifiActivityOut, mIconTint));
-        applyDarkIntensity(
-                DarkIconDispatcher.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
-                mEthernet, mEthernetDark);
-        for (int i = 0; i < mPhoneStates.size(); i++) {
-            mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity, mTintArea);
-        }
-    }
-
-    private void applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon) {
-        lightIcon.setAlpha(1 - darkIntensity);
-        darkIcon.setAlpha(darkIntensity);
-    }
-
-    private void setTint(ImageView v, int tint) {
-        v.setImageTintList(ColorStateList.valueOf(tint));
-    }
-
-    private int currentVpnIconId(boolean isBranded) {
-        return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
-    }
-
-    private class PhoneState {
-        private final int mSubId;
-        private boolean mMobileVisible = false;
-        private int mMobileStrengthId = 0, mMobileTypeId = 0;
-        private int mLastMobileStrengthId = -1;
-        private int mLastMobileTypeId = -1;
-        private String mMobileDescription, mMobileTypeDescription;
-
-        private ViewGroup mMobileGroup;
-        private ImageView mMobile, mMobileType, mMobileRoaming;
-        private View mMobileRoamingSpace;
-        public boolean mRoaming;
-        private ImageView mMobileActivityIn;
-        private ImageView mMobileActivityOut;
-        public boolean mActivityIn;
-        public boolean mActivityOut;
-        private SignalDrawable mMobileSignalDrawable;
-
-        public PhoneState(int subId, Context context) {
-            ViewGroup root = (ViewGroup) LayoutInflater.from(context)
-                    .inflate(R.layout.mobile_signal_group, null);
-            setViews(root);
-            mSubId = subId;
-        }
-
-        public void setViews(ViewGroup root) {
-            mMobileGroup    = root;
-            mMobile         = root.findViewById(R.id.mobile_signal);
-            mMobileType     = root.findViewById(R.id.mobile_type);
-            mMobileRoaming  = root.findViewById(R.id.mobile_roaming);
-            mMobileRoamingSpace  = root.findViewById(R.id.mobile_roaming_space);
-            mMobileActivityIn = root.findViewById(R.id.mobile_in);
-            mMobileActivityOut = root.findViewById(R.id.mobile_out);
-            mMobileSignalDrawable = new SignalDrawable(mMobile.getContext());
-            mMobile.setImageDrawable(mMobileSignalDrawable);
-        }
-
-        public boolean apply(boolean isSecondaryIcon) {
-            if (mMobileVisible && !mIsAirplaneMode) {
-                if (mLastMobileStrengthId != mMobileStrengthId) {
-                    mMobile.getDrawable().setLevel(mMobileStrengthId);
-                    mLastMobileStrengthId = mMobileStrengthId;
-                }
-
-                if (mLastMobileTypeId != mMobileTypeId) {
-                    mMobileType.setImageResource(mMobileTypeId);
-                    mLastMobileTypeId = mMobileTypeId;
-                }
-
-                mMobileGroup.setContentDescription(mMobileTypeDescription
-                        + " " + mMobileDescription);
-                mMobileGroup.setVisibility(View.VISIBLE);
-            } else {
-                mMobileGroup.setVisibility(View.GONE);
-            }
-
-            // When this isn't next to wifi, give it some extra padding between the signals.
-            mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
-                    0, 0, 0);
-            mMobile.setPaddingRelative(mMobileDataIconStartPadding, 0, 0, 0);
-
-            if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
-                        (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
-
-            mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
-            mMobileRoaming.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
-            mMobileRoamingSpace.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
-            mMobileActivityIn.setVisibility(mActivityIn ? View.VISIBLE : View.GONE);
-            mMobileActivityOut.setVisibility(mActivityOut ? View.VISIBLE : View.GONE);
-
-            return mMobileVisible;
-        }
-
-        public void populateAccessibilityEvent(AccessibilityEvent event) {
-            if (mMobileVisible && mMobileGroup != null
-                    && mMobileGroup.getContentDescription() != null) {
-                event.getText().add(mMobileGroup.getContentDescription());
-            }
-        }
-
-        public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
-            mMobileSignalDrawable.setDarkIntensity(darkIntensity);
-            setTint(mMobileType, DarkIconDispatcher.getTint(tintArea, mMobileType, tint));
-            setTint(mMobileRoaming, DarkIconDispatcher.getTint(tintArea, mMobileRoaming,
-                    tint));
-            setTint(mMobileActivityIn,
-                    DarkIconDispatcher.getTint(tintArea, mMobileActivityIn, tint));
-            setTint(mMobileActivityOut,
-                    DarkIconDispatcher.getTint(tintArea, mMobileActivityOut, tint));
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 9e91133..573c1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,8 +21,8 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.Set;
 
@@ -54,7 +54,7 @@
     /**
      * Notifies StatusBarService a smart reply is sent.
      */
-    public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+    public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply,
             boolean generatedByAssistant) {
         mCallback.onSmartReplySent(entry, reply);
         mSendingKeys.add(entry.key);
@@ -70,7 +70,7 @@
      * Notifies StatusBarService a smart action is clicked.
      */
     public void smartActionClicked(
-            NotificationData.Entry entry, int actionIndex, Notification.Action action,
+            NotificationEntry entry, int actionIndex, Notification.Action action,
             boolean generatedByAssistant) {
         final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
         final int rank = mEntryManager.getNotificationData().getRank(entry.key);
@@ -95,7 +95,7 @@
     /**
      * Smart Replies and Actions have been added to the UI.
      */
-    public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount,
+    public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount,
             int actionCount, boolean generatedByAssistant) {
         try {
             mBarService.onNotificationSmartSuggestionsAdded(
@@ -105,7 +105,7 @@
         }
     }
 
-    public void stopSending(final NotificationData.Entry entry) {
+    public void stopSending(final NotificationEntry entry) {
         if (entry != null) {
             mSendingKeys.remove(entry.notification.getKey());
         }
@@ -121,6 +121,6 @@
          * @param entry the entry for the notification
          * @param reply the reply that was sent
          */
-        void onSmartReplySent(NotificationData.Entry entry, CharSequence reply);
+        void onSmartReplySent(NotificationEntry entry, CharSequence reply);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java
deleted file mode 100644
index 0652227..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import com.android.internal.statusbar.StatusBarIcon;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Holds an array of {@link com.android.internal.statusbar.StatusBarIcon}s and draws them
- * in a linear layout
- */
-public class StatusBarIconContainer {
-    private final List<StatusBarIcon> mIcons = new ArrayList<>();
-
-    public StatusBarIconContainer(List<StatusBarIcon> icons) {
-        mIcons.addAll(icons);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 3c13354..19ed13e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -206,6 +206,10 @@
         mIconScale = SYSTEM_ICON_SCALE;
     }
 
+    public float getIconScaleFullyDark() {
+        return (float) mStatusBarIconDrawingSizeDark / mStatusBarIconDrawingSize;
+    }
+
     public float getIconScale() {
         return mIconScale;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 087b655..54bce1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -331,7 +331,8 @@
          *
          * @param newState the new {@link StatusBarState}
          */
-        public void onStateChanged(int newState);
+        default void onStateChanged(int newState) {
+        }
 
         /**
          * Callback to be notified when Dozing changes. Dozing is stored separately from state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 7b42dd9..5605f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -20,7 +20,6 @@
 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;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
@@ -30,6 +29,7 @@
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -72,24 +72,21 @@
 
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onEntryInflated(NotificationData.Entry entry, int inflatedFlags) {
+            public void onEntryInflated(NotificationEntry entry, int inflatedFlags) {
                 showAlertingView(entry, inflatedFlags);
             }
 
             @Override
-            public void onEntryUpdated(NotificationData.Entry entry) {
+            public void onPostEntryUpdated(NotificationEntry entry) {
                 updateAlertState(entry);
             }
 
             @Override
             public void onEntryRemoved(
-                    @Nullable NotificationData.Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
-                stopAlerting(key);
+                stopAlerting(entry.key);
             }
         });
     }
@@ -105,7 +102,7 @@
      * @param entry         entry to add
      * @param inflatedFlags flags representing content views that were inflated
      */
-    private void showAlertingView(NotificationData.Entry entry,
+    private void showAlertingView(NotificationEntry entry,
             @NotificationInflater.InflationFlag int inflatedFlags) {
         if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
@@ -127,7 +124,7 @@
         }
     }
 
-    private void updateAlertState(NotificationData.Entry entry) {
+    private void updateAlertState(NotificationEntry entry) {
         boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
         AlertingNotificationManager alertManager;
         boolean shouldAlert;
@@ -153,8 +150,15 @@
         }
     }
 
-    private static boolean alertAgain(
-            NotificationData.Entry oldEntry, Notification newNotification) {
+    /**
+     * Checks whether an update for a notification warrants an alert for the user.
+     *
+     * @param oldEntry the entry for this notification.
+     * @param newNotification the new notification for this entry.
+     * @return whether this notification should alert the user.
+     */
+    public static boolean alertAgain(
+            NotificationEntry oldEntry, Notification newNotification) {
         return oldEntry == null || !oldEntry.hasInterrupted()
                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
deleted file mode 100644
index a51896e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ /dev/null
@@ -1,1072 +0,0 @@
-/*
- * Copyright (C) 2008 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.notification;
-
-import static android.app.Notification.CATEGORY_ALARM;
-import static android.app.Notification.CATEGORY_CALL;
-import static android.app.Notification.CATEGORY_EVENT;
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Person;
-import android.content.Context;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.InflationTask;
-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.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * The list of currently displaying notifications.
- */
-public class NotificationData {
-
-    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
-
-    /**
-     * These dependencies are late init-ed
-     */
-    private KeyguardEnvironment mEnvironment;
-    private NotificationMediaManager mMediaManager;
-
-    private HeadsUpManager mHeadsUpManager;
-
-    public static final class Entry {
-        private static final long LAUNCH_COOLDOWN = 2000;
-        private static final long REMOTE_INPUT_COOLDOWN = 500;
-        private static final long INITIALIZATION_DELAY = 400;
-        private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
-        private static final int COLOR_INVALID = 1;
-        public final String key;
-        public StatusBarNotification notification;
-        public NotificationChannel channel;
-        public long lastAudiblyAlertedMs;
-        public boolean noisy;
-        public boolean ambient;
-        public int importance;
-        public StatusBarIconView icon;
-        public StatusBarIconView expandedIcon;
-        private boolean interruption;
-        public boolean autoRedacted; // whether the redacted notification was generated by us
-        public int targetSdk;
-        private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
-        public CharSequence remoteInputText;
-        public List<SnoozeCriterion> snoozeCriteria;
-        public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
-        /** Smart Actions provided by the NotificationAssistantService. */
-        @NonNull
-        public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
-        public CharSequence[] smartReplies = new CharSequence[0];
-        @VisibleForTesting
-        public int suppressedVisualEffects;
-        public boolean suspended;
-
-        private Entry parent; // our parent (if we're in a group)
-        private ArrayList<Entry> children = new ArrayList<Entry>();
-        private ExpandableNotificationRow row; // the outer expanded view
-
-        private int mCachedContrastColor = COLOR_INVALID;
-        private int mCachedContrastColorIsFor = COLOR_INVALID;
-        private InflationTask mRunningTask = null;
-        private Throwable mDebugThrowable;
-        public CharSequence remoteInputTextWhenReset;
-        public long lastRemoteInputSent = NOT_LAUNCHED_YET;
-        public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
-        public CharSequence headsUpStatusBarText;
-        public CharSequence headsUpStatusBarTextPublic;
-
-        private long initializationTime = -1;
-
-        /**
-         * Whether or not this row represents a system notification. Note that if this is
-         * {@code null}, that means we were either unable to retrieve the info or have yet to
-         * retrieve the info.
-         */
-        public Boolean mIsSystemNotification;
-
-        /**
-         * Has the user sent a reply through this Notification.
-         */
-        private boolean hasSentReply;
-
-        /**
-         * Whether this notification should be displayed as a bubble.
-         */
-        private boolean mIsBubble;
-
-        /**
-         * Whether the user has dismissed this notification when it was in bubble form.
-         */
-        private boolean mUserDismissedBubble;
-
-        public Entry(StatusBarNotification n) {
-            this(n, null);
-        }
-
-        public Entry(StatusBarNotification n, @Nullable Ranking ranking) {
-            this.key = n.getKey();
-            this.notification = n;
-            if (ranking != null) {
-                populateFromRanking(ranking);
-            }
-        }
-
-        public void populateFromRanking(@NonNull Ranking ranking) {
-            channel = ranking.getChannel();
-            lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
-            importance = ranking.getImportance();
-            ambient = ranking.isAmbient();
-            snoozeCriteria = ranking.getSnoozeCriteria();
-            userSentiment = ranking.getUserSentiment();
-            systemGeneratedSmartActions = ranking.getSmartActions() == null
-                    ? Collections.emptyList() : ranking.getSmartActions();
-            smartReplies = ranking.getSmartReplies() == null
-                    ? new CharSequence[0]
-                    : ranking.getSmartReplies().toArray(new CharSequence[0]);
-            suppressedVisualEffects = ranking.getSuppressedVisualEffects();
-            suspended = ranking.isSuspended();
-        }
-
-        public void setInterruption() {
-            interruption = true;
-        }
-
-        public boolean hasInterrupted() {
-            return interruption;
-        }
-
-        public void setIsBubble(boolean bubbleable) {
-            mIsBubble = bubbleable;
-        }
-
-        public boolean isBubble() {
-            return mIsBubble;
-        }
-
-        public void setBubbleDismissed(boolean userDismissed) {
-            mUserDismissedBubble = userDismissed;
-        }
-
-        public boolean isBubbleDismissed() {
-            return mUserDismissedBubble;
-        }
-
-        /**
-         * Resets the notification entry to be re-used.
-         */
-        public void reset() {
-            if (row != null) {
-                row.reset();
-            }
-        }
-
-        public ExpandableNotificationRow getRow() {
-            return row;
-        }
-
-        //TODO: This will go away when we have a way to bind an entry to a row
-        public void setRow(ExpandableNotificationRow row) {
-            this.row = row;
-        }
-
-        @Nullable
-        public List<Entry> getChildren() {
-            if (children.size() <= 0) {
-                return null;
-            }
-
-            return children;
-        }
-
-        public void notifyFullScreenIntentLaunched() {
-            setInterruption();
-            lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
-        }
-
-        public boolean hasJustLaunchedFullScreenIntent() {
-            return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
-        }
-
-        public boolean hasJustSentRemoteInput() {
-            return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
-        }
-
-        public boolean hasFinishedInitialization() {
-            return initializationTime == -1 ||
-                    SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
-        }
-
-        /**
-         * Create the icons for a notification
-         * @param context the context to create the icons with
-         * @param sbn the notification
-         * @throws InflationException
-         */
-        public void createIcons(Context context, StatusBarNotification sbn)
-                throws InflationException {
-            Notification n = sbn.getNotification();
-            final Icon smallIcon = n.getSmallIcon();
-            if (smallIcon == null) {
-                throw new InflationException("No small icon in notification from "
-                        + sbn.getPackageName());
-            }
-
-            // Construct the icon.
-            icon = new StatusBarIconView(context,
-                    sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-            icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
-            // Construct the expanded icon.
-            expandedIcon = new StatusBarIconView(context,
-                    sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-            expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-            final StatusBarIcon ic = new StatusBarIcon(
-                    sbn.getUser(),
-                    sbn.getPackageName(),
-                    smallIcon,
-                    n.iconLevel,
-                    n.number,
-                    StatusBarIconView.contentDescForNotification(context, n));
-            if (!icon.set(ic) || !expandedIcon.set(ic)) {
-                icon = null;
-                expandedIcon = null;
-                throw new InflationException("Couldn't create icon: " + ic);
-            }
-            expandedIcon.setVisibility(View.INVISIBLE);
-            expandedIcon.setOnVisibilityChangedListener(
-                    newVisibility -> {
-                        if (row != null) {
-                            row.setIconsVisible(newVisibility != View.VISIBLE);
-                        }
-                    });
-        }
-
-        public void setIconTag(int key, Object tag) {
-            if (icon != null) {
-                icon.setTag(key, tag);
-                expandedIcon.setTag(key, tag);
-            }
-        }
-
-        /**
-         * Update the notification icons.
-         *
-         * @param context the context to create the icons with.
-         * @param sbn the notification to read the icon from.
-         * @throws InflationException
-         */
-        public void updateIcons(Context context, StatusBarNotification sbn)
-                throws InflationException {
-            if (icon != null) {
-                // Update the icon
-                Notification n = sbn.getNotification();
-                final StatusBarIcon ic = new StatusBarIcon(
-                        notification.getUser(),
-                        notification.getPackageName(),
-                        n.getSmallIcon(),
-                        n.iconLevel,
-                        n.number,
-                        StatusBarIconView.contentDescForNotification(context, n));
-                icon.setNotification(sbn);
-                expandedIcon.setNotification(sbn);
-                if (!icon.set(ic) || !expandedIcon.set(ic)) {
-                    throw new InflationException("Couldn't update icon: " + ic);
-                }
-            }
-        }
-
-        public int getContrastedColor(Context context, boolean isLowPriority,
-                int backgroundColor) {
-            int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
-                    notification.getNotification().color;
-            if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
-                return mCachedContrastColor;
-            }
-            final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
-                    backgroundColor);
-            mCachedContrastColorIsFor = rawColor;
-            mCachedContrastColor = contrasted;
-            return mCachedContrastColor;
-        }
-
-        /**
-         * Abort all existing inflation tasks
-         */
-        public void abortTask() {
-            if (mRunningTask != null) {
-                mRunningTask.abort();
-                mRunningTask = null;
-            }
-        }
-
-        public void setInflationTask(InflationTask abortableTask) {
-            // abort any existing inflation
-            InflationTask existing = mRunningTask;
-            abortTask();
-            mRunningTask = abortableTask;
-            if (existing != null && mRunningTask != null) {
-                mRunningTask.supersedeTask(existing);
-            }
-        }
-
-        public void onInflationTaskFinished() {
-            mRunningTask = null;
-        }
-
-        @VisibleForTesting
-        public InflationTask getRunningTask() {
-            return mRunningTask;
-        }
-
-        /**
-         * Set a throwable that is used for debugging
-         *
-         * @param debugThrowable the throwable to save
-         */
-        public void setDebugThrowable(Throwable debugThrowable) {
-            mDebugThrowable = debugThrowable;
-        }
-
-        public Throwable getDebugThrowable() {
-            return mDebugThrowable;
-        }
-
-        public void onRemoteInputInserted() {
-            lastRemoteInputSent = NOT_LAUNCHED_YET;
-            remoteInputTextWhenReset = null;
-        }
-
-        public void setHasSentReply() {
-            hasSentReply = true;
-        }
-
-        public boolean isLastMessageFromReply() {
-            if (!hasSentReply) {
-                return false;
-            }
-            Bundle extras = notification.getNotification().extras;
-            CharSequence[] replyTexts = extras.getCharSequenceArray(
-                    Notification.EXTRA_REMOTE_INPUT_HISTORY);
-            if (!ArrayUtils.isEmpty(replyTexts)) {
-                return true;
-            }
-            Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
-            if (messages != null && messages.length > 0) {
-                Parcelable message = messages[messages.length - 1];
-                if (message instanceof Bundle) {
-                    Notification.MessagingStyle.Message lastMessage =
-                            Notification.MessagingStyle.Message.getMessageFromBundle(
-                                    (Bundle) message);
-                    if (lastMessage != null) {
-                        Person senderPerson = lastMessage.getSenderPerson();
-                        if (senderPerson == null) {
-                            return true;
-                        }
-                        Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
-                        return Objects.equals(user, senderPerson);
-                    }
-                }
-            }
-            return false;
-        }
-
-        public void setInitializationTime(long time) {
-            if (initializationTime == -1) {
-                initializationTime = time;
-            }
-        }
-
-        public void sendAccessibilityEvent(int eventType) {
-            if (row != null) {
-                row.sendAccessibilityEvent(eventType);
-            }
-        }
-
-        /**
-         * Used by NotificationMediaManager to determine... things
-         * @return {@code true} if we are a media notification
-         */
-        public boolean isMediaNotification() {
-            if (row == null) return false;
-
-            return row.isMediaRow();
-        }
-
-        /**
-         * We are a top level child if our parent is the list of notifications duh
-         * @return {@code true} if we're a top level notification
-         */
-        public boolean isTopLevelChild() {
-            return row != null && row.isTopLevelChild();
-        }
-
-        public void resetUserExpansion() {
-            if (row != null) row.resetUserExpansion();
-        }
-
-        public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
-            if (row != null) row.freeContentViewWhenSafe(inflationFlag);
-        }
-
-        public void setAmbientPulsing(boolean pulsing) {
-            if (row != null) row.setAmbientPulsing(pulsing);
-        }
-
-        public boolean rowExists() {
-            return row != null;
-        }
-
-        public boolean isRowDismissed() {
-            return row != null && row.isDismissed();
-        }
-
-        public boolean isRowRemoved() {
-            return row != null && row.isRemoved();
-        }
-
-        /**
-         * @return {@code true} if the row is null or removed
-         */
-        public boolean isRemoved() {
-            //TODO: recycling invalidates this
-            return row == null || row.isRemoved();
-        }
-
-        /**
-         * @return {@code true} if the row is null or dismissed
-         */
-        public boolean isDismissed() {
-            //TODO: recycling
-            return row == null || row.isDismissed();
-        }
-
-        public boolean isRowPinned() {
-            return row != null && row.isPinned();
-        }
-
-        public void setRowPinned(boolean pinned) {
-            if (row != null) row.setPinned(pinned);
-        }
-
-        public boolean isRowAnimatingAway() {
-            return row != null && row.isHeadsUpAnimatingAway();
-        }
-
-        public boolean isRowHeadsUp() {
-            return row != null && row.isHeadsUp();
-        }
-
-        public void setHeadsUp(boolean shouldHeadsUp) {
-            if (row != null) row.setHeadsUp(shouldHeadsUp);
-        }
-
-        public boolean mustStayOnScreen() {
-            return row != null && row.mustStayOnScreen();
-        }
-
-        public void setHeadsUpIsVisible() {
-            if (row != null) row.setHeadsUpIsVisible();
-        }
-
-        //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
-        public ExpandableNotificationRow getHeadsUpAnimationView() {
-            return row;
-        }
-
-        public void setUserLocked(boolean userLocked) {
-            if (row != null) row.setUserLocked(userLocked);
-        }
-
-        public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
-            if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
-        }
-
-        public void setGroupExpansionChanging(boolean changing) {
-            if (row != null) row.setGroupExpansionChanging(changing);
-        }
-
-        public void notifyHeightChanged(boolean needsAnimation) {
-            if (row != null) row.notifyHeightChanged(needsAnimation);
-        }
-
-        public void closeRemoteInput() {
-            if (row != null) row.closeRemoteInput();
-        }
-
-        public boolean areChildrenExpanded() {
-            return row != null && row.areChildrenExpanded();
-        }
-
-        public boolean keepInParent() {
-            return row != null && row.keepInParent();
-        }
-
-        //TODO: probably less confusing to say "is group fully visible"
-        public boolean isGroupNotFullyVisible() {
-            return row == null || row.isGroupNotFullyVisible();
-        }
-
-        public NotificationGuts getGuts() {
-            if (row != null) return row.getGuts();
-            return null;
-        }
-
-        public boolean hasLowPriorityStateUpdated() {
-            return row != null && row.hasLowPriorityStateUpdated();
-        }
-
-        public void removeRow() {
-            if (row != null) row.setRemoved();
-        }
-
-        public boolean isSummaryWithChildren() {
-            return row != null && row.isSummaryWithChildren();
-        }
-
-        public void setKeepInParent(boolean keep) {
-            if (row != null) row.setKeepInParent(keep);
-        }
-
-        public void onDensityOrFontScaleChanged() {
-            if (row != null) row.onDensityOrFontScaleChanged();
-        }
-
-        public boolean areGutsExposed() {
-            return row != null && row.getGuts() != null && row.getGuts().isExposed();
-        }
-
-        public boolean isChildInGroup() {
-            return parent == null;
-        }
-
-        public void setLowPriorityStateUpdated(boolean updated) {
-            if (row != null) row.setLowPriorityStateUpdated(updated);
-        }
-
-        /**
-         * @return Can the underlying notification be cleared? This can be different from whether the
-         *         notification can be dismissed in case notifications are sensitive on the lockscreen.
-         * @see #canViewBeDismissed()
-         */
-        public boolean isClearable() {
-            if (notification == null || !notification.isClearable()) {
-                return false;
-            }
-            if (children.size() > 0) {
-                for (int i = 0; i < children.size(); i++) {
-                    Entry child =  children.get(i);
-                    if (!child.isClearable()) {
-                        return false;
-                    }
-                }
-            }
-            return true;
-        }
-
-        public boolean canViewBeDismissed() {
-            if (row == null) return true;
-            return row.canViewBeDismissed();
-        }
-
-        boolean isExemptFromDndVisualSuppression() {
-            if (isNotificationBlockedByPolicy(notification.getNotification())) {
-                return false;
-            }
-
-            if ((notification.getNotification().flags
-                    & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-                return true;
-            }
-            if (notification.getNotification().isMediaNotification()) {
-                return true;
-            }
-            if (mIsSystemNotification != null && mIsSystemNotification) {
-                return true;
-            }
-            return false;
-        }
-
-        private boolean shouldSuppressVisualEffect(int effect) {
-            if (isExemptFromDndVisualSuppression()) {
-                return false;
-            }
-            return (suppressedVisualEffects & effect) != 0;
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressFullScreenIntent() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressPeek() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressStatusBar() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressAmbient() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressNotificationList() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
-        }
-    }
-
-    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
-    private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
-    private final ArrayList<Entry> mFilteredForUser = new ArrayList<>();
-
-    private final NotificationGroupManager mGroupManager
-            = Dependency.get(NotificationGroupManager.class);
-
-    private RankingMap mRankingMap;
-    private final Ranking mTmpRanking = new Ranking();
-
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
-    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
-        private final Ranking mRankingA = new Ranking();
-        private final Ranking mRankingB = new Ranking();
-
-        @Override
-        public int compare(Entry a, Entry b) {
-            final StatusBarNotification na = a.notification;
-            final StatusBarNotification nb = b.notification;
-            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
-            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
-            int aRank = 0;
-            int bRank = 0;
-
-            if (mRankingMap != null) {
-                // RankingMap as received from NoMan
-                getRanking(a.key, mRankingA);
-                getRanking(b.key, mRankingB);
-                aImportance = mRankingA.getImportance();
-                bImportance = mRankingB.getImportance();
-                aRank = mRankingA.getRank();
-                bRank = mRankingB.getRank();
-            }
-
-            String mediaNotification = getMediaManager().getMediaNotificationKey();
-
-            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
-            final boolean aMedia = a.key.equals(mediaNotification)
-                    && aImportance > NotificationManager.IMPORTANCE_MIN;
-            final boolean bMedia = b.key.equals(mediaNotification)
-                    && bImportance > NotificationManager.IMPORTANCE_MIN;
-
-            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH &&
-                    isSystemNotification(na);
-            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH &&
-                    isSystemNotification(nb);
-
-            boolean isHeadsUp = a.row.isHeadsUp();
-            if (isHeadsUp != b.row.isHeadsUp()) {
-                return isHeadsUp ? -1 : 1;
-            } else if (isHeadsUp) {
-                // Provide consistent ranking with headsUpManager
-                return mHeadsUpManager.compare(a, b);
-            } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
-                return a.row.isAmbientPulsing() ? -1 : 1;
-            } else if (aMedia != bMedia) {
-                // Upsort current media notification.
-                return aMedia ? -1 : 1;
-            } else if (aSystemMax != bSystemMax) {
-                // Upsort PRIORITY_MAX system notifications
-                return aSystemMax ? -1 : 1;
-            } else if (aRank != bRank) {
-                return aRank - bRank;
-            } else {
-                return Long.compare(nb.getNotification().when, na.getNotification().when);
-            }
-        }
-    };
-
-    private KeyguardEnvironment getEnvironment() {
-        if (mEnvironment == null) {
-            mEnvironment = Dependency.get(KeyguardEnvironment.class);
-        }
-        return mEnvironment;
-    }
-
-    private NotificationMediaManager getMediaManager() {
-        if (mMediaManager == null) {
-            mMediaManager = Dependency.get(NotificationMediaManager.class);
-        }
-        return mMediaManager;
-    }
-
-    /**
-     * 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()}
-     * when the environment changes.
-     * <p>
-     * Don't hold on to or modify the returned list.
-     */
-    public ArrayList<Entry> getActiveNotifications() {
-        return mSortedAndFiltered;
-    }
-
-    public ArrayList<Entry> getNotificationsForCurrentUser() {
-        mFilteredForUser.clear();
-
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-                final StatusBarNotification sbn = entry.notification;
-                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
-                    continue;
-                }
-                mFilteredForUser.add(entry);
-            }
-        }
-        return mFilteredForUser;
-    }
-
-    public Entry get(String key) {
-        return mEntries.get(key);
-    }
-
-    public void add(Entry entry) {
-        synchronized (mEntries) {
-            mEntries.put(entry.notification.getKey(), entry);
-        }
-        mGroupManager.onEntryAdded(entry);
-
-        updateRankingAndSort(mRankingMap);
-    }
-
-    public Entry remove(String key, RankingMap ranking) {
-        Entry removed;
-        synchronized (mEntries) {
-            removed = mEntries.remove(key);
-        }
-        if (removed == null) return null;
-        mGroupManager.onEntryRemoved(removed);
-        updateRankingAndSort(ranking);
-        return removed;
-    }
-
-    /** Updates the given notification entry with the provided ranking. */
-    public void update(Entry entry, RankingMap ranking, StatusBarNotification notification) {
-        updateRanking(ranking);
-        final StatusBarNotification oldNotification = entry.notification;
-        entry.notification = notification;
-        mGroupManager.onEntryUpdated(entry, oldNotification);
-    }
-
-    public void updateRanking(RankingMap ranking) {
-        updateRankingAndSort(ranking);
-    }
-
-    public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-                if (uid == entry.notification.getUid()
-                        && pkg.equals(entry.notification.getPackageName())
-                        && key.equals(entry.key)) {
-                    if (showIcon) {
-                        entry.mActiveAppOps.add(appOp);
-                    } else {
-                        entry.mActiveAppOps.remove(appOp);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if this notification should be displayed in the high-priority notifications
-     * section (and on the lockscreen and status bar).
-     */
-    public boolean isHighPriority(StatusBarNotification statusBarNotification) {
-        if (mRankingMap != null) {
-            getRanking(statusBarNotification.getKey(), mTmpRanking);
-            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
-                    || statusBarNotification.getNotification().isForegroundService()
-                    || statusBarNotification.getNotification().hasMediaSession()) {
-                return true;
-            }
-            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
-                for (Entry child : mGroupManager.getLogicalChildren(statusBarNotification)) {
-                    if (isHighPriority(child.notification)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    public boolean isAmbient(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isAmbient();
-        }
-        return false;
-    }
-
-    public int getVisibilityOverride(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getVisibilityOverride();
-        }
-        return Ranking.VISIBILITY_NO_OVERRIDE;
-    }
-
-    /**
-     * Categories that are explicitly called out on DND settings screens are always blocked, if
-     * DND has flagged them, even if they are foreground or system notifications that might
-     * otherwise visually bypass DND.
-     */
-    private static boolean isNotificationBlockedByPolicy(Notification n) {
-        if (isCategory(CATEGORY_CALL, n)
-                || isCategory(CATEGORY_MESSAGE, n)
-                || isCategory(CATEGORY_ALARM, n)
-                || isCategory(CATEGORY_EVENT, n)
-                || isCategory(CATEGORY_REMINDER, n)) {
-            return true;
-        }
-        return false;
-    }
-
-    private static boolean isCategory(String category, Notification n) {
-        return Objects.equals(n.category, category);
-    }
-
-    public int getImportance(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getImportance();
-        }
-        return NotificationManager.IMPORTANCE_UNSPECIFIED;
-    }
-
-    public String getOverrideGroupKey(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getOverrideGroupKey();
-        }
-        return null;
-    }
-
-    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getSnoozeCriteria();
-        }
-        return null;
-    }
-
-    public NotificationChannel getChannel(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getChannel();
-        }
-        return null;
-    }
-
-    public int getRank(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getRank();
-        }
-        return 0;
-    }
-
-    public boolean shouldHide(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isSuspended();
-        }
-        return false;
-    }
-
-    private void updateRankingAndSort(RankingMap ranking) {
-        if (ranking != null) {
-            mRankingMap = ranking;
-            synchronized (mEntries) {
-                final int N = mEntries.size();
-                for (int i = 0; i < N; i++) {
-                    Entry entry = mEntries.valueAt(i);
-                    if (!getRanking(entry.key, mTmpRanking)) {
-                        continue;
-                    }
-                    final StatusBarNotification oldSbn = entry.notification.cloneLight();
-                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
-                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
-                        entry.notification.setOverrideGroupKey(overrideGroupKey);
-                        mGroupManager.onEntryUpdated(entry, oldSbn);
-                    }
-                    entry.populateFromRanking(mTmpRanking);
-                }
-            }
-        }
-        filterAndSort();
-    }
-
-    /**
-     * Get the ranking from the current ranking map.
-     *
-     * @param key the key to look up
-     * @param outRanking the ranking to populate
-     *
-     * @return {@code true} if the ranking was properly obtained.
-     */
-    @VisibleForTesting
-    protected boolean getRanking(String key, Ranking outRanking) {
-        return mRankingMap.getRanking(key, outRanking);
-    }
-
-    // TODO: This should not be public. Instead the Environment should notify this class when
-    // anything changed, and this class should call back the UI so it updates itself.
-    public void filterAndSort() {
-        mSortedAndFiltered.clear();
-
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-
-                if (mNotificationFilter.shouldFilterOut(entry)) {
-                    continue;
-                }
-
-                mSortedAndFiltered.add(entry);
-            }
-        }
-
-        Collections.sort(mSortedAndFiltered, mRankingComparator);
-    }
-
-    public void dump(PrintWriter pw, String indent) {
-        int N = mSortedAndFiltered.size();
-        pw.print(indent);
-        pw.println("active notifications: " + N);
-        int active;
-        for (active = 0; active < N; active++) {
-            NotificationData.Entry e = mSortedAndFiltered.get(active);
-            dumpEntry(pw, indent, active, e);
-        }
-        synchronized (mEntries) {
-            int M = mEntries.size();
-            pw.print(indent);
-            pw.println("inactive notifications: " + (M - active));
-            int inactiveCount = 0;
-            for (int i = 0; i < M; i++) {
-                Entry entry = mEntries.valueAt(i);
-                if (!mSortedAndFiltered.contains(entry)) {
-                    dumpEntry(pw, indent, inactiveCount, entry);
-                    inactiveCount++;
-                }
-            }
-        }
-    }
-
-    private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
-        getRanking(e.key, mTmpRanking);
-        pw.print(indent);
-        pw.println("  [" + i + "] key=" + e.key + " icon=" + e.icon);
-        StatusBarNotification n = e.notification;
-        pw.print(indent);
-        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" +
-                mTmpRanking.getImportance());
-        pw.print(indent);
-        pw.println("      notification=" + n.getNotification());
-    }
-
-    private static boolean isSystemNotification(StatusBarNotification sbn) {
-        String sbnPackage = sbn.getPackageName();
-        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
-    }
-
-    /**
-     * Provides access to keyguard state and user settings dependent data.
-     */
-    public interface KeyguardEnvironment {
-        boolean isDeviceProvisioned();
-        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 1d06ce0..839b06c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -19,6 +19,7 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
 
 /**
@@ -29,25 +30,38 @@
      * Called when a new notification is posted. At this point, the notification is "pending": its
      * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
      */
-    default void onPendingEntryAdded(NotificationData.Entry entry) {
+    default void onPendingEntryAdded(NotificationEntry entry) {
+    }
+
+    // TODO: Combine this with onPreEntryUpdated into "onBeforeEntryFiltered" or similar
+    /**
+     * Called when a new entry is created but before it has been filtered or displayed to the user.
+     */
+    default void onBeforeNotificationAdded(NotificationEntry entry) {
     }
 
     /**
      * Called when a new entry is created.
      */
-    default void onNotificationAdded(NotificationData.Entry entry) {
+    default void onNotificationAdded(NotificationEntry entry) {
     }
 
     /**
-     * Called when a notification was updated.
+     * Called when a notification is updated, before any filtering of notifications have occurred.
      */
-    default void onEntryUpdated(NotificationData.Entry entry) {
+    default void onPreEntryUpdated(NotificationEntry entry) {
+    }
+
+    /**
+     * Called when a notification was updated, after any filtering of notifications have occurred.
+     */
+    default void onPostEntryUpdated(NotificationEntry entry) {
     }
 
     /**
      * Called when a notification's views are inflated for the first time.
      */
-    default void onEntryInflated(NotificationData.Entry entry,
+    default void onEntryInflated(NotificationEntry entry,
             @NotificationInflater.InflationFlag int inflatedFlags) {
     }
 
@@ -57,7 +71,7 @@
      *
      * @param entry notification data entry that was reinflated.
      */
-    default void onEntryReinflated(NotificationData.Entry entry) {
+    default void onEntryReinflated(NotificationEntry entry) {
     }
 
     /**
@@ -71,20 +85,14 @@
      * because the developer retracted it).
      * @param entry notification data entry that was removed.  Null if no entry existed for the
      *              removed key at the time of removal.
-     * @param key key of notification that was removed
-     * @param old StatusBarNotification of the notification before it was removed
      * @param visibility logging data related to the visibility of the notification at the time of
      *                   removal, if it was removed by a user action.  Null if it was not removed by
      *                   a user action.
-     * @param lifetimeExtended true if something is artificially extending how long the notification
      * @param removedByUser true if the notification was removed by a user action
      */
     default void onEntryRemoved(
-            @Nullable NotificationData.Entry entry,
-            String key,
-            StatusBarNotification old,
+            NotificationEntry entry,
             @Nullable NotificationVisibility visibility,
-            boolean lifetimeExtended,
             boolean removedByUser) {
     }
 }
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 e7344f8..989e781 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,18 +15,12 @@
  */
 package com.android.systemui.statusbar.notification;
 
-import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
-
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -34,18 +28,17 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
 
@@ -54,7 +47,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 /**
  * NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
@@ -65,55 +57,31 @@
         Dumpable,
         NotificationInflater.InflationCallback,
         NotificationUpdateHandler,
-        VisualStabilityManager.Callback,
-        BubbleController.BubbleDismissListener {
+        VisualStabilityManager.Callback {
     private static final String TAG = "NotificationEntryMgr";
-    protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
+    private final Context mContext;
+    @VisibleForTesting
+    protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
 
-    protected final Context mContext;
-    protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
-
-    private final NotificationGutsManager mGutsManager =
-            Dependency.get(NotificationGutsManager.class);
-    private final DeviceProvisionedController mDeviceProvisionedController =
-            Dependency.get(DeviceProvisionedController.class);
     private final ForegroundServiceController mForegroundServiceController =
             Dependency.get(ForegroundServiceController.class);
-    private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
 
     // Lazily retrieved dependencies
     private NotificationRemoteInputManager mRemoteInputManager;
     private NotificationRowBinder mNotificationRowBinder;
 
-    private final Handler mDeferredNotificationViewUpdateHandler;
-    private Runnable mUpdateNotificationViewsCallback;
-
     private NotificationPresenter mPresenter;
-    protected PowerManager mPowerManager;
     private NotificationListenerService.RankingMap mLatestRankingMap;
-    protected HeadsUpManager mHeadsUpManager;
+    @VisibleForTesting
     protected NotificationData mNotificationData;
-    protected NotificationListContainer mListContainer;
+
     @VisibleForTesting
     final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
             = new ArrayList<>();
     private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
 
-    private final DeviceProvisionedController.DeviceProvisionedListener
-            mDeviceProvisionedListener =
-            new DeviceProvisionedController.DeviceProvisionedListener() {
-                @Override
-                public void onDeviceProvisionedChanged() {
-                    updateNotifications();
-                }
-            };
-
-    public void destroy() {
-        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
-    }
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NotificationEntryManager state:");
@@ -121,7 +89,7 @@
         if (mPendingNotifications.size() == 0) {
             pw.println("null");
         } else {
-            for (NotificationData.Entry entry : mPendingNotifications.values()) {
+            for (NotificationEntry entry : mPendingNotifications.values()) {
                 pw.println(entry.notification);
             }
         }
@@ -129,10 +97,7 @@
 
     public NotificationEntryManager(Context context) {
         mContext = context;
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mBubbleController.setDismissListener(this /* bubbleEventListener */);
         mNotificationData = new NotificationData();
-        mDeferredNotificationViewUpdateHandler = new Handler();
     }
 
     /** Adds a {@link NotificationEntryListener}. */
@@ -157,16 +122,17 @@
         return mNotificationRowBinder;
     }
 
+    // TODO: Remove this once we can always use a mocked row binder in our tests
+    @VisibleForTesting
+    void setRowBinder(NotificationRowBinder notificationRowBinder) {
+        mNotificationRowBinder = notificationRowBinder;
+    }
+
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
             HeadsUpManager headsUpManager) {
         mPresenter = presenter;
-        mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
-        mHeadsUpManager = headsUpManager;
-        mNotificationData.setHeadsUpManager(mHeadsUpManager);
-        mListContainer = listContainer;
-
-        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+        mNotificationData.setHeadsUpManager(headsUpManager);
     }
 
     /** Adds multiple {@link NotificationLifetimeExtender}s. */
@@ -186,14 +152,6 @@
         return mNotificationData;
     }
 
-    protected Context getContext() {
-        return mContext;
-    }
-
-    protected NotificationPresenter getPresenter() {
-        return mPresenter;
-    }
-
     @Override
     public void onReorderingAllowed() {
         updateNotifications();
@@ -208,30 +166,13 @@
                 n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
     }
 
-    @Override
-    public void onStackDismissed() {
-        updateNotifications();
-    }
-
-    @Override
-    public void onBubbleDismissed(String key) {
-        NotificationData.Entry entry = mNotificationData.get(key);
-        if (entry != null) {
-            entry.setBubbleDismissed(true);
-            if (!DEBUG_DEMOTE_TO_NOTIF) {
-                performRemoveNotification(entry.notification);
-            }
-        }
-        updateNotifications();
-    }
-
     private void abortExistingInflation(String key) {
         if (mPendingNotifications.containsKey(key)) {
-            NotificationData.Entry entry = mPendingNotifications.get(key);
+            NotificationEntry entry = mPendingNotifications.get(key);
             entry.abortTask();
             mPendingNotifications.remove(key);
         }
-        NotificationData.Entry addedEntry = mNotificationData.get(key);
+        NotificationEntry addedEntry = mNotificationData.get(key);
         if (addedEntry != null) {
             addedEntry.abortTask();
         }
@@ -252,32 +193,8 @@
         }
     }
 
-    private void addEntry(NotificationData.Entry shadeEntry) {
-        if (shadeEntry == null) {
-            return;
-        }
-        // Add the expanded view and icon.
-        mNotificationData.add(shadeEntry);
-        tagForeground(shadeEntry.notification);
-        updateNotifications();
-        for (NotificationEntryListener listener : mNotificationEntryListeners) {
-            listener.onNotificationAdded(shadeEntry);
-        }
-
-        maybeScheduleUpdateNotificationViews(shadeEntry);
-    }
-
-    private void maybeScheduleUpdateNotificationViews(NotificationData.Entry entry) {
-        long audibleAlertTimeout = RECENTLY_ALERTED_THRESHOLD_MS
-                - (System.currentTimeMillis() - entry.lastAudiblyAlertedMs);
-        if (audibleAlertTimeout > 0) {
-            mDeferredNotificationViewUpdateHandler.postDelayed(
-                    mUpdateNotificationViewsCallback, audibleAlertTimeout);
-        }
-    }
-
     @Override
-    public void onAsyncInflationFinished(NotificationData.Entry entry,
+    public void onAsyncInflationFinished(NotificationEntry 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
@@ -288,7 +205,14 @@
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onEntryInflated(entry, inflatedFlags);
                 }
-                addEntry(entry);
+                mNotificationData.add(entry);
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onBeforeNotificationAdded(entry);
+                }
+                updateNotifications();
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onNotificationAdded(entry);
+                }
             } else {
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onEntryReinflated(entry);
@@ -310,11 +234,10 @@
             @Nullable NotificationVisibility visibility,
             boolean forceRemove,
             boolean removedByUser) {
-        final NotificationData.Entry entry = mNotificationData.get(key);
+        final NotificationEntry entry = mNotificationData.get(key);
 
         abortExistingInflation(key);
 
-        StatusBarNotification old = null;
         boolean lifetimeExtended = false;
 
         if (entry != null) {
@@ -341,31 +264,20 @@
 
                 if (entry.rowExists()) {
                     entry.removeRow();
-                    mListContainer.cleanUpViewStateForEntry(entry);
                 }
 
                 // Let's remove the children if this was a summary
                 handleGroupSummaryRemoved(key);
 
-                old = removeNotificationViews(key, ranking);
+                mNotificationData.remove(key, ranking);
+                updateNotifications();
+                Dependency.get(LeakDetector.class).trackGarbage(entry);
+
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onEntryRemoved(entry, visibility, removedByUser);
+                }
             }
         }
-
-        for (NotificationEntryListener listener : mNotificationEntryListeners) {
-            listener.onEntryRemoved(entry, key, old, visibility, lifetimeExtended, removedByUser);
-        }
-    }
-
-    private StatusBarNotification removeNotificationViews(String key,
-            NotificationListenerService.RankingMap ranking) {
-        NotificationData.Entry entry = mNotificationData.remove(key, ranking);
-        if (entry == null) {
-            Log.w(TAG, "removeNotification for unknown key: " + key);
-            return null;
-        }
-        updateNotifications();
-        Dependency.get(LeakDetector.class).trackGarbage(entry);
-        return entry.notification;
     }
 
     /**
@@ -379,19 +291,19 @@
      *
      */
     private void handleGroupSummaryRemoved(String key) {
-        NotificationData.Entry entry = mNotificationData.get(key);
+        NotificationEntry entry = mNotificationData.get(key);
         if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
             if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
                 // We don't want to remove children for autobundled notifications as they are not
                 // always cancelled. We only remove them if they were dismissed by the user.
                 return;
             }
-            List<NotificationData.Entry> childEntries = entry.getChildren();
+            List<NotificationEntry> childEntries = entry.getChildren();
             if (childEntries == null) {
                 return;
             }
             for (int i = 0; i < childEntries.size(); i++) {
-                NotificationData.Entry childEntry = childEntries.get(i);
+                NotificationEntry childEntry = childEntries.get(i);
                 boolean isForeground = (entry.notification.getNotification().flags
                         & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                 boolean keepForReply =
@@ -410,39 +322,6 @@
         }
     }
 
-    public void updateNotificationsOnDensityOrFontScaleChanged() {
-        ArrayList<NotificationData.Entry> userNotifications =
-                mNotificationData.getNotificationsForCurrentUser();
-        for (int i = 0; i < userNotifications.size(); i++) {
-            NotificationData.Entry entry = userNotifications.get(i);
-            entry.onDensityOrFontScaleChanged();
-            boolean exposedGuts = entry.areGutsExposed();
-            if (exposedGuts) {
-                mGutsManager.onDensityOrFontScaleChanged(entry);
-            }
-        }
-    }
-
-    private NotificationData.Entry createNotificationEntry(
-            StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
-            throws InflationException {
-        if (DEBUG) {
-            Log.d(TAG, "createNotificationEntry(notification=" + sbn + " " + ranking);
-        }
-
-        NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
-        if (BubbleController.shouldAutoBubble(getContext(), entry)) {
-            entry.setIsBubble(true);
-        }
-
-        Dependency.get(LeakDetector.class).trackInstance(entry);
-        entry.createIcons(mContext, sbn);
-        // Construct the expanded view.
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(sbn),
-                mNotificationData.get(entry.key) != null);
-        return entry;
-    }
-
     private void addNotificationInternal(StatusBarNotification notification,
             NotificationListenerService.RankingMap rankingMap) throws InflationException {
         String key = notification.getKey();
@@ -453,7 +332,14 @@
         mNotificationData.updateRanking(rankingMap);
         NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
         rankingMap.getRanking(key, ranking);
-        NotificationData.Entry entry = createNotificationEntry(notification, ranking);
+
+        NotificationEntry entry = new NotificationEntry(notification, ranking);
+
+        Dependency.get(LeakDetector.class).trackInstance(entry);
+        // Construct the expanded view.
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
+                mNotificationData.get(entry.key) != null);
+
         abortExistingInflation(key);
 
         mPendingNotifications.put(key, entry);
@@ -462,19 +348,6 @@
         }
     }
 
-    @VisibleForTesting
-    void tagForeground(StatusBarNotification notification) {
-        ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
-                notification.getUserId(), notification.getPackageName());
-        if (activeOps != null) {
-            int N = activeOps.size();
-            for (int i = 0; i < N; i++) {
-                updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
-                        notification.getPackageName(), true);
-            }
-        }
-    }
-
     @Override
     public void addNotification(StatusBarNotification notification,
             NotificationListenerService.RankingMap ranking) {
@@ -485,22 +358,13 @@
         }
     }
 
-    public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
-        String foregroundKey = mForegroundServiceController.getStandardLayoutKey(
-                UserHandle.getUserId(uid), pkg);
-        if (foregroundKey != null) {
-            mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
-            updateNotifications();
-        }
-    }
-
     private void updateNotificationInternal(StatusBarNotification notification,
             NotificationListenerService.RankingMap ranking) throws InflationException {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
         abortExistingInflation(key);
-        NotificationData.Entry entry = mNotificationData.get(key);
+        NotificationEntry entry = mNotificationData.get(key);
         if (entry == null) {
             return;
         }
@@ -513,18 +377,15 @@
 
         mNotificationData.update(entry, ranking, notification);
 
-        entry.updateIcons(mContext, notification);
         getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
                 mNotificationData.get(entry.key) != null);
 
-        updateNotifications();
-
-        if (!notification.isClearable()) {
-            // The user may have performed a dismiss action on the notification, since it's
-            // not clearable we should snap it back.
-            mListContainer.snapViewIfNeeded(entry);
+        for (NotificationEntryListener listener : mNotificationEntryListeners) {
+            listener.onPreEntryUpdated(entry);
         }
 
+        updateNotifications();
+
         if (DEBUG) {
             // Is this for you?
             boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
@@ -533,10 +394,8 @@
         }
 
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
-            listener.onEntryUpdated(entry);
+            listener.onPostEntryUpdated(entry);
         }
-
-        maybeScheduleUpdateNotificationViews(entry);
     }
 
     @Override
@@ -551,19 +410,20 @@
 
     public void updateNotifications() {
         mNotificationData.filterAndSort();
-
-        mPresenter.updateNotificationViews();
+        if (mPresenter != null) {
+            mPresenter.updateNotificationViews();
+        }
     }
 
     public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
-        List<NotificationData.Entry> entries = new ArrayList<>();
+        List<NotificationEntry> entries = new ArrayList<>();
         entries.addAll(mNotificationData.getActiveNotifications());
         entries.addAll(mPendingNotifications.values());
 
         // Has a copy of the current UI adjustments.
         ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
         ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
-        for (NotificationData.Entry entry : entries) {
+        for (NotificationEntry entry : entries) {
             NotificationUiAdjustment adjustment =
                     NotificationUiAdjustment.extractFromNotificationEntry(entry);
             oldAdjustments.put(entry.key, adjustment);
@@ -575,7 +435,7 @@
         updateRankingOfPendingNotifications(rankingMap);
 
         // By comparing the old and new UI adjustments, reinflate the view accordingly.
-        for (NotificationData.Entry entry : entries) {
+        for (NotificationEntry entry : entries) {
             getRowBinder().onNotificationRankingUpdated(
                     entry,
                     oldImportances.get(entry.key),
@@ -593,7 +453,7 @@
             return;
         }
         NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
-        for (NotificationData.Entry pendingNotification : mPendingNotifications.values()) {
+        for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
             rankingMap.getRanking(pendingNotification.key, tmpRanking);
             pendingNotification.populateFromRanking(tmpRanking);
         }
@@ -604,7 +464,7 @@
      * notifications whose views have not yet been inflated. In general, the system pretends like
      * these don't exist, although there are a couple exceptions.
      */
-    public Iterable<NotificationData.Entry> getPendingNotificationsIterator() {
+    public Iterable<NotificationEntry> getPendingNotificationsIterator() {
         return mPendingNotifications.values();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 700382a..154d7b35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -28,6 +28,8 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -82,7 +84,7 @@
     /**
      * @return true if the provided notification should NOT be shown right now.
      */
-    public boolean shouldFilterOut(NotificationData.Entry entry) {
+    public boolean shouldFilterOut(NotificationEntry entry) {
         final StatusBarNotification sbn = entry.notification;
         if (!(getEnvironment().isDeviceProvisioned()
                 || showNotificationEvenIfUnprovisioned(sbn))) {
@@ -132,6 +134,10 @@
             }
         }
 
+        if (entry.isBubble() && !entry.showInShadeWhenBubble()) {
+            return true;
+        }
+
         return false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 8bd0e9a..c50f10b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -37,6 +37,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -134,12 +135,35 @@
     }
 
     /**
+     * Whether the notification should appear as a bubble with a fly-out on top of the screen.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should bubble up, false otherwise
+     */
+    public boolean shouldBubbleUp(NotificationEntry entry) {
+        StatusBarNotification sbn = entry.notification;
+        if (!entry.isBubble()) {
+            if (DEBUG) {
+                Log.d(TAG, "No bubble up: notification " + sbn.getKey()
+                        + " is bubble? " + entry.isBubble());
+            }
+            return false;
+        }
+
+        if (!canHeadsUpCommon(entry)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
      * Whether the notification should peek in from the top and alert the user.
      *
      * @param entry the entry to check
      * @return true if the entry should heads up, false otherwise
      */
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
+    public boolean shouldHeadsUp(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (getShadeController().isDozing()) {
@@ -149,10 +173,12 @@
             return false;
         }
 
-        // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
-        // message bubble from the bubble to go through heads up path
         boolean inShade = mStatusBarStateController.getState() == SHADE;
-        if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+        if (entry.isBubble() && inShade) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: in unlocked shade where notification is shown as a "
+                        + "bubble: " + sbn.getKey());
+            }
             return false;
         }
 
@@ -163,9 +189,13 @@
             return false;
         }
 
-        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+        if (!canHeadsUpCommon(entry)) {
+            return false;
+        }
+
+        if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
             if (DEBUG) {
-                Log.d(TAG, "No heads up: no huns or vr mode");
+                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
             }
             return false;
         }
@@ -185,34 +215,6 @@
             return false;
         }
 
-        if (entry.shouldSuppressPeek()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (entry.hasJustLaunchedFullScreenIntent()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
-            }
-            return false;
-        }
-
         if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
             return false;
         }
@@ -227,7 +229,7 @@
      * @param entry the entry to check
      * @return true if the entry should ambient pulse, false otherwise
      */
-    public boolean shouldPulse(NotificationData.Entry entry) {
+    public boolean shouldPulse(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (!getShadeController().isDozing()) {
@@ -273,14 +275,14 @@
 
     /**
      * Common checks between heads up alerting and ambient pulse alerting.  See
-     * {@link #shouldHeadsUp(NotificationData.Entry)} and
-     * {@link #shouldPulse(NotificationData.Entry)}.  Notifications that fail any of these checks
+     * {@link #shouldHeadsUp(NotificationEntry)} and
+     * {@link #shouldPulse(NotificationEntry)}.  Notifications that fail any of these checks
      * should not alert at all.
      *
      * @param entry the entry to check
      * @return true if these checks pass, false if the notification should not alert
      */
-    protected boolean canAlertCommon(NotificationData.Entry entry) {
+    protected boolean canAlertCommon(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (mNotificationFilter.shouldFilterOut(entry)) {
@@ -301,6 +303,49 @@
         return true;
     }
 
+    /**
+     * Common checks between heads up alerting and bubble fly out alerting. See
+     * {@link #shouldHeadsUp(NotificationEntry)} and
+     * {@link #shouldBubbleUp(NotificationEntry)}. Notifications that fail any of these
+     * checks should not interrupt the user on screen.
+     *
+     * @param entry the entry to check
+     * @return true if these checks pass, false if the notification should not interrupt on screen
+     */
+    public boolean canHeadsUpCommon(NotificationEntry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: no huns or vr mode");
+            }
+            return false;
+        }
+
+        if (entry.shouldSuppressPeek()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.hasJustLaunchedFullScreenIntent()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        return true;
+    }
+
     private boolean isSnoozedPackage(StatusBarNotification sbn) {
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
@@ -325,7 +370,7 @@
          * @param sbn   notification that might be heads upped
          * @return false if the notification can not be heads upped
          */
-        boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+        boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn);
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
new file mode 100644
index 0000000..88f4ca2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 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.notification;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+
+/**
+ * Root controller for the list of notifications in the shade.
+ *
+ * TODO: Much of the code in NotificationPresenter should eventually move in here. It will proxy
+ * domain-specific behavior (ARC, etc) to subcontrollers.
+ */
+public class NotificationListController {
+    private final NotificationEntryManager mEntryManager;
+    private final NotificationListContainer mListContainer;
+    private final ForegroundServiceController mForegroundServiceController;
+    private final DeviceProvisionedController mDeviceProvisionedController;
+
+    public NotificationListController(
+            NotificationEntryManager entryManager,
+            NotificationListContainer listContainer,
+            ForegroundServiceController foregroundServiceController,
+            DeviceProvisionedController deviceProvisionedController) {
+        mEntryManager = checkNotNull(entryManager);
+        mListContainer = checkNotNull(listContainer);
+        mForegroundServiceController = checkNotNull(foregroundServiceController);
+        mDeviceProvisionedController = checkNotNull(deviceProvisionedController);
+    }
+
+    /**
+     * Causes the controller to register listeners on its dependencies. This method must be called
+     * before the controller is ready to perform its duties.
+     */
+    public void bind() {
+        mEntryManager.addNotificationEntryListener(mEntryListener);
+        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+    }
+
+    /** Should be called when the list controller is being destroyed. */
+    public void destroy() {
+        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
+    }
+
+    @SuppressWarnings("FieldCanBeLocal")
+    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+        @Override
+        public void onEntryRemoved(
+                NotificationEntry entry,
+                NotificationVisibility visibility,
+                boolean removedByUser) {
+            mListContainer.cleanUpViewStateForEntry(entry);
+        }
+
+        @Override
+        public void onBeforeNotificationAdded(NotificationEntry entry) {
+            tagForeground(entry.notification);
+        }
+    };
+
+    private final DeviceProvisionedListener mDeviceProvisionedListener =
+            new DeviceProvisionedListener() {
+                @Override
+                public void onDeviceProvisionedChanged() {
+                    mEntryManager.updateNotifications();
+                }
+            };
+
+    // TODO: This method is horrifically inefficient
+    private void tagForeground(StatusBarNotification notification) {
+        ArraySet<Integer> activeOps =
+                mForegroundServiceController.getAppOps(
+                        notification.getUserId(), notification.getPackageName());
+        if (activeOps != null) {
+            int len = activeOps.size();
+            for (int i = 0; i < len; i++) {
+                updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
+                        notification.getPackageName(), true);
+            }
+        }
+    }
+
+    /** When an app op changes, propagate that change to notifications. */
+    public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
+        String foregroundKey =
+                mForegroundServiceController.getStandardLayoutKey(UserHandle.getUserId(uid), pkg);
+        if (foregroundKey != null) {
+            mEntryManager
+                    .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
+            mEntryManager.updateNotifications();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index 0bde99b..cc302b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
@@ -125,17 +126,22 @@
     /**
      * Inflates the views for the given entry (possibly asynchronously).
      */
-    public void inflateViews(NotificationData.Entry entry, Runnable onDismissRunnable,
-            boolean isUpdate) {
+    public void inflateViews(
+            NotificationEntry entry,
+            Runnable onDismissRunnable,
+            boolean isUpdate)
+            throws InflationException {
         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
         PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                 entry.notification.getUser().getIdentifier());
 
         final StatusBarNotification sbn = entry.notification;
         if (entry.rowExists()) {
+            entry.updateIcons(mContext, sbn);
             entry.reset();
             updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate);
         } else {
+            entry.createIcons(mContext, sbn);
             new RowInflaterTask().inflate(mContext, parent, entry,
                     row -> {
                         bindRow(entry, pmUser, sbn, row, onDismissRunnable);
@@ -144,7 +150,7 @@
         }
     }
 
-    private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
+    private void bindRow(NotificationEntry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row,
             Runnable onDismissRunnable) {
         row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
@@ -192,7 +198,7 @@
      * reinflating them.
      */
     public void onNotificationRankingUpdated(
-            NotificationData.Entry entry,
+            NotificationEntry entry,
             @Nullable Integer oldImportance,
             NotificationUiAdjustment oldAdjustment,
             NotificationUiAdjustment newAdjustment,
@@ -219,7 +225,7 @@
 
     //TODO: This method associates a row with an entry, but eventually needs to not do that
     private void updateNotification(
-            NotificationData.Entry entry,
+            NotificationEntry entry,
             PackageManager pmUser,
             StatusBarNotification sbn,
             ExpandableNotificationRow row,
@@ -287,7 +293,7 @@
          * @param sbn    notification
          * @param row    row for the notification
          */
-        void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
+        void onBindRow(NotificationEntry entry, PackageManager pmUser,
                 StatusBarNotification sbn, ExpandableNotificationRow row);
     }
 }
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 a194eef..f09c57d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification;
 
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
 /**
  * An object that can determine the visibility of a Notification.
  */
@@ -27,5 +29,5 @@
      * @param entry
      * @return true if row is in a visible location
      */
-    boolean isInVisibleLocation(NotificationData.Entry entry);
+    boolean isInVisibleLocation(NotificationEntry entry);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 8e6a93d..c886685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -21,6 +21,7 @@
 import androidx.collection.ArraySet;
 
 import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
@@ -52,7 +53,7 @@
     public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onEntryReinflated(NotificationData.Entry entry) {
+            public void onEntryReinflated(NotificationEntry entry) {
                 if (entry.hasLowPriorityStateUpdated()) {
                     onLowPriorityUpdated(entry);
                     if (mPresenter != null) {
@@ -163,7 +164,7 @@
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         if (isHeadsUp) {
             // Heads up notifications should in general be allowed to reorder if they are out of
             // view and stay at the current location if they aren't.
@@ -171,7 +172,7 @@
         }
     }
 
-    private void onLowPriorityUpdated(NotificationData.Entry entry) {
+    private void onLowPriorityUpdated(NotificationEntry entry) {
         mLowPriorityReorderingViews.add(entry.getRow());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
new file mode 100644
index 0000000..8c29fb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2019 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.notification.collection;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The list of currently displaying notifications.
+ */
+public class NotificationData {
+
+    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+    /**
+     * These dependencies are late init-ed
+     */
+    private KeyguardEnvironment mEnvironment;
+    private NotificationMediaManager mMediaManager;
+
+    private HeadsUpManager mHeadsUpManager;
+
+    private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>();
+    private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
+    private final ArrayList<NotificationEntry> mFilteredForUser = new ArrayList<>();
+
+    private final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
+
+    private RankingMap mRankingMap;
+    private final Ranking mTmpRanking = new Ranking();
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    private final Comparator<NotificationEntry> mRankingComparator =
+            new Comparator<NotificationEntry>() {
+        private final Ranking mRankingA = new Ranking();
+        private final Ranking mRankingB = new Ranking();
+
+        @Override
+        public int compare(NotificationEntry a, NotificationEntry b) {
+            final StatusBarNotification na = a.notification;
+            final StatusBarNotification nb = b.notification;
+            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
+            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
+            int aRank = 0;
+            int bRank = 0;
+
+            if (mRankingMap != null) {
+                // RankingMap as received from NoMan
+                getRanking(a.key, mRankingA);
+                getRanking(b.key, mRankingB);
+                aImportance = mRankingA.getImportance();
+                bImportance = mRankingB.getImportance();
+                aRank = mRankingA.getRank();
+                bRank = mRankingB.getRank();
+            }
+
+            String mediaNotification = getMediaManager().getMediaNotificationKey();
+
+            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
+            final boolean aMedia = a.key.equals(mediaNotification)
+                    && aImportance > NotificationManager.IMPORTANCE_MIN;
+            final boolean bMedia = b.key.equals(mediaNotification)
+                    && bImportance > NotificationManager.IMPORTANCE_MIN;
+
+            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH
+                    && isSystemNotification(na);
+            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
+                    && isSystemNotification(nb);
+
+            boolean isHeadsUp = a.getRow().isHeadsUp();
+            if (isHeadsUp != b.getRow().isHeadsUp()) {
+                return isHeadsUp ? -1 : 1;
+            } else if (isHeadsUp) {
+                // Provide consistent ranking with headsUpManager
+                return mHeadsUpManager.compare(a, b);
+            } else if (a.getRow().isAmbientPulsing() != b.getRow().isAmbientPulsing()) {
+                return a.getRow().isAmbientPulsing() ? -1 : 1;
+            } else if (aMedia != bMedia) {
+                // Upsort current media notification.
+                return aMedia ? -1 : 1;
+            } else if (aSystemMax != bSystemMax) {
+                // Upsort PRIORITY_MAX system notifications
+                return aSystemMax ? -1 : 1;
+            } else if (aRank != bRank) {
+                return aRank - bRank;
+            } else {
+                return Long.compare(nb.getNotification().when, na.getNotification().when);
+            }
+        }
+    };
+
+    private KeyguardEnvironment getEnvironment() {
+        if (mEnvironment == null) {
+            mEnvironment = Dependency.get(KeyguardEnvironment.class);
+        }
+        return mEnvironment;
+    }
+
+    private NotificationMediaManager getMediaManager() {
+        if (mMediaManager == null) {
+            mMediaManager = Dependency.get(NotificationMediaManager.class);
+        }
+        return mMediaManager;
+    }
+
+    /**
+     * 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()}
+     * when the environment changes.
+     * <p>
+     * Don't hold on to or modify the returned list.
+     */
+    public ArrayList<NotificationEntry> getActiveNotifications() {
+        return mSortedAndFiltered;
+    }
+
+    public ArrayList<NotificationEntry> getNotificationsForCurrentUser() {
+        mFilteredForUser.clear();
+
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                final StatusBarNotification sbn = entry.notification;
+                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+                    continue;
+                }
+                mFilteredForUser.add(entry);
+            }
+        }
+        return mFilteredForUser;
+    }
+
+    public NotificationEntry get(String key) {
+        return mEntries.get(key);
+    }
+
+    public void add(NotificationEntry entry) {
+        synchronized (mEntries) {
+            mEntries.put(entry.notification.getKey(), entry);
+        }
+        mGroupManager.onEntryAdded(entry);
+
+        updateRankingAndSort(mRankingMap);
+    }
+
+    public NotificationEntry remove(String key, RankingMap ranking) {
+        NotificationEntry removed;
+        synchronized (mEntries) {
+            removed = mEntries.remove(key);
+        }
+        if (removed == null) return null;
+        mGroupManager.onEntryRemoved(removed);
+        updateRankingAndSort(ranking);
+        return removed;
+    }
+
+    /** Updates the given notification entry with the provided ranking. */
+    public void update(
+            NotificationEntry entry,
+            RankingMap ranking,
+            StatusBarNotification notification) {
+        updateRanking(ranking);
+        final StatusBarNotification oldNotification = entry.notification;
+        entry.notification = notification;
+        mGroupManager.onEntryUpdated(entry, oldNotification);
+    }
+
+    public void updateRanking(RankingMap ranking) {
+        updateRankingAndSort(ranking);
+    }
+
+    public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                if (uid == entry.notification.getUid()
+                        && pkg.equals(entry.notification.getPackageName())
+                        && key.equals(entry.key)) {
+                    if (showIcon) {
+                        entry.mActiveAppOps.add(appOp);
+                    } else {
+                        entry.mActiveAppOps.remove(appOp);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if this notification should be displayed in the high-priority notifications
+     * section (and on the lockscreen and status bar).
+     */
+    public boolean isHighPriority(StatusBarNotification statusBarNotification) {
+        if (mRankingMap != null) {
+            getRanking(statusBarNotification.getKey(), mTmpRanking);
+            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+                    || statusBarNotification.getNotification().isForegroundService()
+                    || statusBarNotification.getNotification().hasMediaSession()) {
+                return true;
+            }
+            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
+                final ArrayList<NotificationEntry> logicalChildren =
+                        mGroupManager.getLogicalChildren(statusBarNotification);
+                for (NotificationEntry child : logicalChildren) {
+                    if (isHighPriority(child.notification)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean isAmbient(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isAmbient();
+        }
+        return false;
+    }
+
+    public int getVisibilityOverride(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getVisibilityOverride();
+        }
+        return Ranking.VISIBILITY_NO_OVERRIDE;
+    }
+
+    public int getImportance(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getImportance();
+        }
+        return NotificationManager.IMPORTANCE_UNSPECIFIED;
+    }
+
+    public String getOverrideGroupKey(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getOverrideGroupKey();
+        }
+        return null;
+    }
+
+    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getSnoozeCriteria();
+        }
+        return null;
+    }
+
+    public NotificationChannel getChannel(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getChannel();
+        }
+        return null;
+    }
+
+    public int getRank(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getRank();
+        }
+        return 0;
+    }
+
+    public boolean shouldHide(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isSuspended();
+        }
+        return false;
+    }
+
+    private void updateRankingAndSort(RankingMap ranking) {
+        if (ranking != null) {
+            mRankingMap = ranking;
+            synchronized (mEntries) {
+                final int len = mEntries.size();
+                for (int i = 0; i < len; i++) {
+                    NotificationEntry entry = mEntries.valueAt(i);
+                    if (!getRanking(entry.key, mTmpRanking)) {
+                        continue;
+                    }
+                    final StatusBarNotification oldSbn = entry.notification.cloneLight();
+                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
+                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+                        entry.notification.setOverrideGroupKey(overrideGroupKey);
+                        mGroupManager.onEntryUpdated(entry, oldSbn);
+                    }
+                    entry.populateFromRanking(mTmpRanking);
+                }
+            }
+        }
+        filterAndSort();
+    }
+
+    /**
+     * Get the ranking from the current ranking map.
+     *
+     * @param key the key to look up
+     * @param outRanking the ranking to populate
+     *
+     * @return {@code true} if the ranking was properly obtained.
+     */
+    @VisibleForTesting
+    protected boolean getRanking(String key, Ranking outRanking) {
+        return mRankingMap.getRanking(key, outRanking);
+    }
+
+    // TODO: This should not be public. Instead the Environment should notify this class when
+    // anything changed, and this class should call back the UI so it updates itself.
+    public void filterAndSort() {
+        mSortedAndFiltered.clear();
+
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+
+                if (mNotificationFilter.shouldFilterOut(entry)) {
+                    continue;
+                }
+
+                mSortedAndFiltered.add(entry);
+            }
+        }
+
+        Collections.sort(mSortedAndFiltered, mRankingComparator);
+    }
+
+    public void dump(PrintWriter pw, String indent) {
+        int filteredLen = mSortedAndFiltered.size();
+        pw.print(indent);
+        pw.println("active notifications: " + filteredLen);
+        int active;
+        for (active = 0; active < filteredLen; active++) {
+            NotificationEntry e = mSortedAndFiltered.get(active);
+            dumpEntry(pw, indent, active, e);
+        }
+        synchronized (mEntries) {
+            int totalLen = mEntries.size();
+            pw.print(indent);
+            pw.println("inactive notifications: " + (totalLen - active));
+            int inactiveCount = 0;
+            for (int i = 0; i < totalLen; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                if (!mSortedAndFiltered.contains(entry)) {
+                    dumpEntry(pw, indent, inactiveCount, entry);
+                    inactiveCount++;
+                }
+            }
+        }
+    }
+
+    private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
+        getRanking(e.key, mTmpRanking);
+        pw.print(indent);
+        pw.println("  [" + i + "] key=" + e.key + " icon=" + e.icon);
+        StatusBarNotification n = e.notification;
+        pw.print(indent);
+        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
+                + mTmpRanking.getImportance());
+        pw.print(indent);
+        pw.println("      notification=" + n.getNotification());
+    }
+
+    private static boolean isSystemNotification(StatusBarNotification sbn) {
+        String sbnPackage = sbn.getPackageName();
+        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
+    }
+
+    /**
+     * Provides access to keyguard state and user settings dependent data.
+     */
+    public interface KeyguardEnvironment {
+        boolean isDeviceProvisioned();
+        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
new file mode 100644
index 0000000..ee551ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -0,0 +1,726 @@
+/*
+ * Copyright (C) 2019 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.notification.collection;
+
+import static android.app.Notification.CATEGORY_ALARM;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_EVENT;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager.Policy;
+import android.app.Person;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a notification that the system UI knows about
+ *
+ * Whenever the NotificationManager tells us about the existence of a new notification, we wrap it
+ * in a NotificationEntry. Thus, every notification has an associated NotificationEntry, even if
+ * that notification is never displayed to the user (for example, if it's filtered out for some
+ * reason).
+ *
+ * Entries store information about the current state of the notification. Essentially:
+ * anything that needs to persist or be modifiable even when the notification's views don't
+ * exist. Any other state should be stored on the views/view controllers themselves.
+ *
+ * At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
+ * clean this up in the future.
+ */
+public final class NotificationEntry {
+    private static final long LAUNCH_COOLDOWN = 2000;
+    private static final long REMOTE_INPUT_COOLDOWN = 500;
+    private static final long INITIALIZATION_DELAY = 400;
+    private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
+    private static final int COLOR_INVALID = 1;
+    public final String key;
+    public StatusBarNotification notification;
+    public NotificationChannel channel;
+    public long lastAudiblyAlertedMs;
+    public boolean noisy;
+    public boolean ambient;
+    public int importance;
+    public StatusBarIconView icon;
+    public StatusBarIconView expandedIcon;
+    private boolean interruption;
+    public boolean autoRedacted; // whether the redacted notification was generated by us
+    public int targetSdk;
+    private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
+    public CharSequence remoteInputText;
+    public List<SnoozeCriterion> snoozeCriteria;
+    public int userSentiment = NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+    /** Smart Actions provided by the NotificationAssistantService. */
+    @NonNull
+    public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
+    public CharSequence[] smartReplies = new CharSequence[0];
+    @VisibleForTesting
+    public int suppressedVisualEffects;
+    public boolean suspended;
+
+    private NotificationEntry parent; // our parent (if we're in a group)
+    private ArrayList<NotificationEntry> children = new ArrayList<NotificationEntry>();
+    private ExpandableNotificationRow row; // the outer expanded view
+
+    private int mCachedContrastColor = COLOR_INVALID;
+    private int mCachedContrastColorIsFor = COLOR_INVALID;
+    private InflationTask mRunningTask = null;
+    private Throwable mDebugThrowable;
+    public CharSequence remoteInputTextWhenReset;
+    public long lastRemoteInputSent = NOT_LAUNCHED_YET;
+    public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+    public CharSequence headsUpStatusBarText;
+    public CharSequence headsUpStatusBarTextPublic;
+
+    private long initializationTime = -1;
+
+    /**
+     * Whether or not this row represents a system notification. Note that if this is
+     * {@code null}, that means we were either unable to retrieve the info or have yet to
+     * retrieve the info.
+     */
+    public Boolean mIsSystemNotification;
+
+    /**
+     * Has the user sent a reply through this Notification.
+     */
+    private boolean hasSentReply;
+
+    /**
+     * Whether this notification should be displayed as a bubble.
+     */
+    private boolean mIsBubble;
+
+    /**
+     * Whether this notification should be shown in the shade when it is also displayed as a bubble.
+     *
+     * <p>When a notification is a bubble we don't show it in the shade once the bubble has been
+     * expanded</p>
+     */
+    private boolean mShowInShadeWhenBubble;
+
+    /**
+     * Whether the user has dismissed this notification when it was in bubble form.
+     */
+    private boolean mUserDismissedBubble;
+
+    public NotificationEntry(StatusBarNotification n) {
+        this(n, null);
+    }
+
+    public NotificationEntry(
+            StatusBarNotification n,
+            @Nullable NotificationListenerService.Ranking ranking) {
+        this.key = n.getKey();
+        this.notification = n;
+        if (ranking != null) {
+            populateFromRanking(ranking);
+        }
+    }
+
+    public void populateFromRanking(@NonNull NotificationListenerService.Ranking ranking) {
+        channel = ranking.getChannel();
+        lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
+        importance = ranking.getImportance();
+        ambient = ranking.isAmbient();
+        snoozeCriteria = ranking.getSnoozeCriteria();
+        userSentiment = ranking.getUserSentiment();
+        systemGeneratedSmartActions = ranking.getSmartActions() == null
+                ? Collections.emptyList() : ranking.getSmartActions();
+        smartReplies = ranking.getSmartReplies() == null
+                ? new CharSequence[0]
+                : ranking.getSmartReplies().toArray(new CharSequence[0]);
+        suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+        suspended = ranking.isSuspended();
+    }
+
+    public void setInterruption() {
+        interruption = true;
+    }
+
+    public boolean hasInterrupted() {
+        return interruption;
+    }
+
+    public void setIsBubble(boolean bubbleable) {
+        mIsBubble = bubbleable;
+    }
+
+    public boolean isBubble() {
+        return mIsBubble;
+    }
+
+    public void setBubbleDismissed(boolean userDismissed) {
+        mUserDismissedBubble = userDismissed;
+    }
+
+    public boolean isBubbleDismissed() {
+        return mUserDismissedBubble;
+    }
+
+    /**
+     * Sets whether this notification should be shown in the shade when it is also displayed as a
+     * bubble.
+     */
+    public void setShowInShadeWhenBubble(boolean showInShade) {
+        mShowInShadeWhenBubble = showInShade;
+    }
+
+    /**
+     * Whether this notification should be shown in the shade when it is also displayed as a
+     * bubble.
+     */
+    public boolean showInShadeWhenBubble() {
+        // We always show it in the shade if non-clearable
+        return !isClearable() || mShowInShadeWhenBubble;
+    }
+
+    /**
+     * Resets the notification entry to be re-used.
+     */
+    public void reset() {
+        if (row != null) {
+            row.reset();
+        }
+    }
+
+    public ExpandableNotificationRow getRow() {
+        return row;
+    }
+
+    //TODO: This will go away when we have a way to bind an entry to a row
+    public void setRow(ExpandableNotificationRow row) {
+        this.row = row;
+    }
+
+    @Nullable
+    public List<NotificationEntry> getChildren() {
+        if (children.size() <= 0) {
+            return null;
+        }
+
+        return children;
+    }
+
+    public void notifyFullScreenIntentLaunched() {
+        setInterruption();
+        lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
+    }
+
+    public boolean hasJustLaunchedFullScreenIntent() {
+        return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
+    }
+
+    public boolean hasJustSentRemoteInput() {
+        return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
+    }
+
+    public boolean hasFinishedInitialization() {
+        return initializationTime == -1
+                || SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
+    }
+
+    /**
+     * Create the icons for a notification
+     * @param context the context to create the icons with
+     * @param sbn the notification
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    public void createIcons(Context context, StatusBarNotification sbn)
+            throws InflationException {
+        Notification n = sbn.getNotification();
+        final Icon smallIcon = n.getSmallIcon();
+        if (smallIcon == null) {
+            throw new InflationException("No small icon in notification from "
+                    + sbn.getPackageName());
+        }
+
+        // Construct the icon.
+        icon = new StatusBarIconView(context,
+                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+        icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+        // Construct the expanded icon.
+        expandedIcon = new StatusBarIconView(context,
+                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+        expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        final StatusBarIcon ic = new StatusBarIcon(
+                sbn.getUser(),
+                sbn.getPackageName(),
+                smallIcon,
+                n.iconLevel,
+                n.number,
+                StatusBarIconView.contentDescForNotification(context, n));
+        if (!icon.set(ic) || !expandedIcon.set(ic)) {
+            icon = null;
+            expandedIcon = null;
+            throw new InflationException("Couldn't create icon: " + ic);
+        }
+        expandedIcon.setVisibility(View.INVISIBLE);
+        expandedIcon.setOnVisibilityChangedListener(
+                newVisibility -> {
+                    if (row != null) {
+                        row.setIconsVisible(newVisibility != View.VISIBLE);
+                    }
+                });
+    }
+
+    public void setIconTag(int key, Object tag) {
+        if (icon != null) {
+            icon.setTag(key, tag);
+            expandedIcon.setTag(key, tag);
+        }
+    }
+
+    /**
+     * Update the notification icons.
+     *
+     * @param context the context to create the icons with.
+     * @param sbn the notification to read the icon from.
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    public void updateIcons(Context context, StatusBarNotification sbn)
+            throws InflationException {
+        if (icon != null) {
+            // Update the icon
+            Notification n = sbn.getNotification();
+            final StatusBarIcon ic = new StatusBarIcon(
+                    notification.getUser(),
+                    notification.getPackageName(),
+                    n.getSmallIcon(),
+                    n.iconLevel,
+                    n.number,
+                    StatusBarIconView.contentDescForNotification(context, n));
+            icon.setNotification(sbn);
+            expandedIcon.setNotification(sbn);
+            if (!icon.set(ic) || !expandedIcon.set(ic)) {
+                throw new InflationException("Couldn't update icon: " + ic);
+            }
+        }
+    }
+
+    public int getContrastedColor(Context context, boolean isLowPriority,
+            int backgroundColor) {
+        int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
+                notification.getNotification().color;
+        if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
+            return mCachedContrastColor;
+        }
+        final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
+                backgroundColor);
+        mCachedContrastColorIsFor = rawColor;
+        mCachedContrastColor = contrasted;
+        return mCachedContrastColor;
+    }
+
+    /**
+     * Abort all existing inflation tasks
+     */
+    public void abortTask() {
+        if (mRunningTask != null) {
+            mRunningTask.abort();
+            mRunningTask = null;
+        }
+    }
+
+    public void setInflationTask(InflationTask abortableTask) {
+        // abort any existing inflation
+        InflationTask existing = mRunningTask;
+        abortTask();
+        mRunningTask = abortableTask;
+        if (existing != null && mRunningTask != null) {
+            mRunningTask.supersedeTask(existing);
+        }
+    }
+
+    public void onInflationTaskFinished() {
+        mRunningTask = null;
+    }
+
+    @VisibleForTesting
+    public InflationTask getRunningTask() {
+        return mRunningTask;
+    }
+
+    /**
+     * Set a throwable that is used for debugging
+     *
+     * @param debugThrowable the throwable to save
+     */
+    public void setDebugThrowable(Throwable debugThrowable) {
+        mDebugThrowable = debugThrowable;
+    }
+
+    public Throwable getDebugThrowable() {
+        return mDebugThrowable;
+    }
+
+    public void onRemoteInputInserted() {
+        lastRemoteInputSent = NOT_LAUNCHED_YET;
+        remoteInputTextWhenReset = null;
+    }
+
+    public void setHasSentReply() {
+        hasSentReply = true;
+    }
+
+    public boolean isLastMessageFromReply() {
+        if (!hasSentReply) {
+            return false;
+        }
+        Bundle extras = notification.getNotification().extras;
+        CharSequence[] replyTexts = extras.getCharSequenceArray(
+                Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        if (!ArrayUtils.isEmpty(replyTexts)) {
+            return true;
+        }
+        Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+        if (messages != null && messages.length > 0) {
+            Parcelable message = messages[messages.length - 1];
+            if (message instanceof Bundle) {
+                Notification.MessagingStyle.Message lastMessage =
+                        Notification.MessagingStyle.Message.getMessageFromBundle(
+                                (Bundle) message);
+                if (lastMessage != null) {
+                    Person senderPerson = lastMessage.getSenderPerson();
+                    if (senderPerson == null) {
+                        return true;
+                    }
+                    Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+                    return Objects.equals(user, senderPerson);
+                }
+            }
+        }
+        return false;
+    }
+
+    public void setInitializationTime(long time) {
+        if (initializationTime == -1) {
+            initializationTime = time;
+        }
+    }
+
+    public void sendAccessibilityEvent(int eventType) {
+        if (row != null) {
+            row.sendAccessibilityEvent(eventType);
+        }
+    }
+
+    /**
+     * Used by NotificationMediaManager to determine... things
+     * @return {@code true} if we are a media notification
+     */
+    public boolean isMediaNotification() {
+        if (row == null) return false;
+
+        return row.isMediaRow();
+    }
+
+    /**
+     * We are a top level child if our parent is the list of notifications duh
+     * @return {@code true} if we're a top level notification
+     */
+    public boolean isTopLevelChild() {
+        return row != null && row.isTopLevelChild();
+    }
+
+    public void resetUserExpansion() {
+        if (row != null) row.resetUserExpansion();
+    }
+
+    public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) {
+        if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+    }
+
+    public void setAmbientPulsing(boolean pulsing) {
+        if (row != null) row.setAmbientPulsing(pulsing);
+    }
+
+    public boolean rowExists() {
+        return row != null;
+    }
+
+    public boolean isRowDismissed() {
+        return row != null && row.isDismissed();
+    }
+
+    public boolean isRowRemoved() {
+        return row != null && row.isRemoved();
+    }
+
+    /**
+     * @return {@code true} if the row is null or removed
+     */
+    public boolean isRemoved() {
+        //TODO: recycling invalidates this
+        return row == null || row.isRemoved();
+    }
+
+    /**
+     * @return {@code true} if the row is null or dismissed
+     */
+    public boolean isDismissed() {
+        //TODO: recycling
+        return row == null || row.isDismissed();
+    }
+
+    public boolean isRowPinned() {
+        return row != null && row.isPinned();
+    }
+
+    public void setRowPinned(boolean pinned) {
+        if (row != null) row.setPinned(pinned);
+    }
+
+    public boolean isRowAnimatingAway() {
+        return row != null && row.isHeadsUpAnimatingAway();
+    }
+
+    public boolean isRowHeadsUp() {
+        return row != null && row.isHeadsUp();
+    }
+
+    public void setHeadsUp(boolean shouldHeadsUp) {
+        if (row != null) row.setHeadsUp(shouldHeadsUp);
+    }
+
+    public boolean mustStayOnScreen() {
+        return row != null && row.mustStayOnScreen();
+    }
+
+    public void setHeadsUpIsVisible() {
+        if (row != null) row.setHeadsUpIsVisible();
+    }
+
+    //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+    public ExpandableNotificationRow getHeadsUpAnimationView() {
+        return row;
+    }
+
+    public void setUserLocked(boolean userLocked) {
+        if (row != null) row.setUserLocked(userLocked);
+    }
+
+    public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+        if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+    }
+
+    public void setGroupExpansionChanging(boolean changing) {
+        if (row != null) row.setGroupExpansionChanging(changing);
+    }
+
+    public void notifyHeightChanged(boolean needsAnimation) {
+        if (row != null) row.notifyHeightChanged(needsAnimation);
+    }
+
+    public void closeRemoteInput() {
+        if (row != null) row.closeRemoteInput();
+    }
+
+    public boolean areChildrenExpanded() {
+        return row != null && row.areChildrenExpanded();
+    }
+
+    public boolean keepInParent() {
+        return row != null && row.keepInParent();
+    }
+
+    //TODO: probably less confusing to say "is group fully visible"
+    public boolean isGroupNotFullyVisible() {
+        return row == null || row.isGroupNotFullyVisible();
+    }
+
+    public NotificationGuts getGuts() {
+        if (row != null) return row.getGuts();
+        return null;
+    }
+
+    public boolean hasLowPriorityStateUpdated() {
+        return row != null && row.hasLowPriorityStateUpdated();
+    }
+
+    public void removeRow() {
+        if (row != null) row.setRemoved();
+    }
+
+    public boolean isSummaryWithChildren() {
+        return row != null && row.isSummaryWithChildren();
+    }
+
+    public void setKeepInParent(boolean keep) {
+        if (row != null) row.setKeepInParent(keep);
+    }
+
+    public void onDensityOrFontScaleChanged() {
+        if (row != null) row.onDensityOrFontScaleChanged();
+    }
+
+    public boolean areGutsExposed() {
+        return row != null && row.getGuts() != null && row.getGuts().isExposed();
+    }
+
+    public boolean isChildInGroup() {
+        return parent == null;
+    }
+
+    public void setLowPriorityStateUpdated(boolean updated) {
+        if (row != null) row.setLowPriorityStateUpdated(updated);
+    }
+
+    /**
+     * @return Can the underlying notification be cleared? This can be different from whether the
+     *         notification can be dismissed in case notifications are sensitive on the lockscreen.
+     * @see #canViewBeDismissed()
+     */
+    public boolean isClearable() {
+        if (notification == null || !notification.isClearable()) {
+            return false;
+        }
+        if (children.size() > 0) {
+            for (int i = 0; i < children.size(); i++) {
+                NotificationEntry child =  children.get(i);
+                if (!child.isClearable()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public boolean canViewBeDismissed() {
+        if (row == null) return true;
+        return row.canViewBeDismissed();
+    }
+
+    @VisibleForTesting
+    boolean isExemptFromDndVisualSuppression() {
+        if (isNotificationBlockedByPolicy(notification.getNotification())) {
+            return false;
+        }
+
+        if ((notification.getNotification().flags
+                & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+            return true;
+        }
+        if (notification.getNotification().isMediaNotification()) {
+            return true;
+        }
+        if (mIsSystemNotification != null && mIsSystemNotification) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean shouldSuppressVisualEffect(int effect) {
+        if (isExemptFromDndVisualSuppression()) {
+            return false;
+        }
+        return (suppressedVisualEffects & effect) != 0;
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressFullScreenIntent() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_PEEK}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressPeek() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressStatusBar() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_AMBIENT}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressAmbient() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressNotificationList() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+    }
+
+    /**
+     * Categories that are explicitly called out on DND settings screens are always blocked, if
+     * DND has flagged them, even if they are foreground or system notifications that might
+     * otherwise visually bypass DND.
+     */
+    private static boolean isNotificationBlockedByPolicy(Notification n) {
+        return isCategory(CATEGORY_CALL, n)
+                || isCategory(CATEGORY_MESSAGE, n)
+                || isCategory(CATEGORY_ALARM, n)
+                || isCategory(CATEGORY_EVENT, n)
+                || isCategory(CATEGORY_REMINDER, n);
+    }
+
+    private static boolean isCategory(String category, Notification n) {
+        return Objects.equals(n.category, category);
+    }
+}
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 610d300..3eec38e 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
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.statusbar.notification.logging;
 
-import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -34,9 +33,9 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -116,11 +115,11 @@
             //    notifications.
             // 3. Report newly visible and no-longer visible notifications.
             // 4. Keep currently visible notifications for next report.
-            ArrayList<NotificationData.Entry> activeNotifications = mEntryManager
+            ArrayList<NotificationEntry> activeNotifications = mEntryManager
                     .getNotificationData().getActiveNotifications();
             int N = activeNotifications.size();
             for (int i = 0; i < N; i++) {
-                NotificationData.Entry entry = activeNotifications.get(i);
+                NotificationEntry entry = activeNotifications.get(i);
                 String key = entry.notification.getKey();
                 boolean isVisible = mListContainer.isInVisibleLocation(entry);
                 NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
@@ -168,14 +167,11 @@
         entryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
             public void onEntryRemoved(
-                    @Nullable NotificationData.Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
-                if (removedByUser && visibility != null && entry != null) {
-                    logNotificationClear(key, entry.notification, visibility);
+                if (removedByUser && visibility != null) {
+                    logNotificationClear(entry.key, entry.notification, visibility);
                 }
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8b0a682..c34d567 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -105,7 +105,7 @@
     private final DoubleTapHelper mDoubleTapHelper;
 
     private boolean mDimmed;
-    private boolean mDark;
+    protected boolean mDark;
 
     protected int mBgTint = NO_COLOR;
     private float mBgAlpha = 1f;
@@ -140,7 +140,6 @@
     private FalsingManager mFalsingManager;
 
     private float mNormalBackgroundVisibilityAmount;
-    private ValueAnimator mFadeInFromDarkAnimator;
     private float mDimmedBackgroundFadeInAmount = -1;
     private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
             = new ValueAnimator.AnimatorUpdateListener() {
@@ -150,22 +149,6 @@
             mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
         }
     };
-    private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            super.onAnimationEnd(animation);
-            mFadeInFromDarkAnimator = null;
-            mDimmedBackgroundFadeInAmount = -1;
-            updateBackground();
-        }
-    };
-    private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
-            = new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            updateOutlineAlpha();
-        }
-    };
     private FakeShadowView mFakeShadow;
     private int mCurrentBackgroundTint;
     private int mTargetTint;
@@ -465,22 +448,11 @@
         mDark = dark;
         updateBackground();
         updateBackgroundTint(false);
-        if (!dark && fade && !shouldHideBackground()) {
-            fadeInFromDark(delay);
-        }
-        updateOutlineAlpha();
     }
 
     private void updateOutlineAlpha() {
-        if (mDark) {
-            setOutlineAlpha(0f);
-            return;
-        }
         float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
         alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
-        if (mFadeInFromDarkAnimator != null) {
-            alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
-        }
         setOutlineAlpha(alpha);
     }
 
@@ -638,36 +610,6 @@
     }
 
     /**
-     * Fades in the background when exiting dark mode.
-     */
-    private void fadeInFromDark(long delay) {
-        final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
-        background.setAlpha(0f);
-        mBackgroundVisibilityUpdater.onAnimationUpdate(null);
-        background.animate()
-                .alpha(1f)
-                .setDuration(DARK_ANIMATION_LENGTH)
-                .setStartDelay(delay)
-                .setInterpolator(Interpolators.ALPHA_IN)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        // Jump state if we are cancelled
-                        background.setAlpha(1f);
-                    }
-                })
-                .setUpdateListener(mBackgroundVisibilityUpdater)
-                .start();
-        mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
-        mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
-        mFadeInFromDarkAnimator.setStartDelay(delay);
-        mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
-        mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
-        mFadeInFromDarkAnimator.start();
-    }
-
-    /**
      * Fades the background when the dimmed state changes.
      */
     private void fadeDimmedBackground() {
@@ -708,9 +650,7 @@
             public void onAnimationEnd(Animator animation) {
                 updateBackground();
                 mBackgroundAnimator = null;
-                if (mFadeInFromDarkAnimator == null) {
-                    mDimmedBackgroundFadeInAmount = -1;
-                }
+                mDimmedBackgroundFadeInAmount = -1;
             }
         });
         mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
@@ -736,7 +676,7 @@
             mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
         } else if (mDimmed) {
             // When groups are animating to the expanded state from the lockscreen, show the
-            // normal background instead of the dimmed background
+            // normal background instead of the dimmed background.
             final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
             mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
             mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
@@ -760,7 +700,7 @@
     }
 
     protected boolean shouldHideBackground() {
-        return mDark;
+        return false;
     }
 
     private void cancelFadeAnimations() {
@@ -1023,14 +963,11 @@
 
     /**
      * @param withTint should a possible tint be factored in?
-     * @param withOverRide should the value be interpolated with {@link #mOverrideTint}
+     * @param withOverride should the value be interpolated with {@link #mOverrideTint}
      * @return the calculated background color
      */
-    private int calculateBgColor(boolean withTint, boolean withOverRide) {
-        if (withTint && mDark) {
-            return getContext().getColor(R.color.notification_material_background_dark_color);
-        }
-        if (withOverRide && mOverrideTint != NO_COLOR) {
+    private int calculateBgColor(boolean withTint, boolean withOverride) {
+        if (withOverride && mOverrideTint != NO_COLOR) {
             int defaultTint = calculateBgColor(withTint, false);
             return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
         }
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 a58c7cd..296c061 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
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
 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;
@@ -86,10 +85,9 @@
 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.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 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;
@@ -106,6 +104,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
 
@@ -122,6 +121,7 @@
     private static final int MENU_VIEW_INDEX = 0;
     private static final String TAG = "ExpandableNotifRow";
     public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
+    private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
     private boolean mUpdateBackgroundOnUpdate;
 
     /**
@@ -132,7 +132,6 @@
     }
 
     private LayoutListener mLayoutListener;
-    private boolean mDark;
     private boolean mLowPriorityStateUpdated;
     private final NotificationInflater mNotificationInflater;
     private int mIconTransformContentShift;
@@ -146,7 +145,6 @@
     private int mNotificationMinHeight;
     private int mNotificationMinHeightLarge;
     private int mNotificationMaxHeight;
-    private int mNotificationAmbientHeight;
     private int mIncreasedPaddingBetweenElements;
     private int mNotificationLaunchHeight;
     private boolean mMustStayOnScreen;
@@ -199,7 +197,7 @@
     private ExpansionLogger mLogger;
     private String mLoggingKey;
     private NotificationGuts mGuts;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private StatusBarNotification mStatusBarNotification;
     private String mAppName;
 
@@ -451,7 +449,7 @@
      *
      * @param entry the entry this row is tied to
      */
-    public void setEntry(@NonNull NotificationData.Entry entry) {
+    public void setEntry(@NonNull NotificationEntry entry) {
         mEntry = entry;
         mStatusBarNotification = entry.notification;
         cacheIsSystemNotification();
@@ -677,15 +675,14 @@
         if (headsUpWrapper != null) {
             headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
         }
-        layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight,
-                mNotificationAmbientHeight);
+        layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight, headsUpHeight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
         return mStatusBarNotification;
     }
 
-    public NotificationData.Entry getEntry() {
+    public NotificationEntry getEntry() {
         return mEntry;
     }
 
@@ -1422,7 +1419,7 @@
 
     public void performDismiss(boolean fromAccessibility) {
         if (isOnlyChildInGroup()) {
-            NotificationData.Entry groupSummary =
+            NotificationEntry groupSummary =
                     mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
             if (groupSummary.isClearable()) {
                 // If this is the only child in the group, dismiss the group, but don't try to show
@@ -1647,8 +1644,6 @@
                 R.dimen.notification_min_height_increased);
         mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
-        mNotificationAmbientHeight = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_ambient_height);
         mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
@@ -1693,17 +1688,31 @@
     /** Sets the last time the notification being displayed audibly alerted the user. */
     public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
-            boolean recentlyAudiblyAlerted = System.currentTimeMillis() - lastAudiblyAlertedMs
-                    < NotificationEntryManager.RECENTLY_ALERTED_THRESHOLD_MS;
-            if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
-                mChildrenContainer.getHeaderView().setRecentlyAudiblyAlerted(
-                        recentlyAudiblyAlerted);
+            long timeSinceAlertedAudibly = System.currentTimeMillis() - lastAudiblyAlertedMs;
+            boolean alertedRecently =
+                    timeSinceAlertedAudibly < RECENTLY_ALERTED_THRESHOLD_MS;
+
+            applyAudiblyAlertedRecently(alertedRecently);
+
+            removeCallbacks(mExpireRecentlyAlertedFlag);
+            if (alertedRecently) {
+                long timeUntilNoLongerRecent =
+                        RECENTLY_ALERTED_THRESHOLD_MS - timeSinceAlertedAudibly;
+                postDelayed(mExpireRecentlyAlertedFlag, timeUntilNoLongerRecent);
             }
-            mPrivateLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
-            mPublicLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
         }
     }
 
+    private final Runnable mExpireRecentlyAlertedFlag = () -> applyAudiblyAlertedRecently(false);
+
+    private void applyAudiblyAlertedRecently(boolean audiblyAlertedRecently) {
+        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
+            mChildrenContainer.getHeaderView().setRecentlyAudiblyAlerted(audiblyAlertedRecently);
+        }
+        mPrivateLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
+        mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
+    }
+
     public View.OnClickListener getAppOpsOnClickListener() {
         return mOnAppOpsClickListener;
     }
@@ -1994,8 +2003,10 @@
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark == dark) {
+            return;
+        }
         super.setDark(dark, fade, delay);
-        mDark = dark;
         if (!mIsAmbientPulsing) {
             // Only fade the showing view of the pulsing notification.
             fade = false;
@@ -2004,9 +2015,6 @@
         if (showing != null) {
             showing.setDark(dark, fade, delay);
         }
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.setDark(dark, fade, delay);
-        }
         updateShelfIconColor();
     }
 
@@ -2307,7 +2315,7 @@
     }
 
     private boolean isShownAsBubble() {
-        return mEntry.isBubble() && (mStatusBarState == SHADE || mStatusBarState == -1);
+        return mEntry.isBubble() && !mEntry.showInShadeWhenBubble() && !mEntry.isBubbleDismissed();
     }
 
     /**
@@ -2538,7 +2546,7 @@
     /**
      * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
      *         otherwise some state might not be updated. To request about the general clearability
-     *         see {@link NotificationData.Entry#isClearable()}.
+     *         see {@link NotificationEntry#isClearable()}.
      */
     public boolean canViewBeDismissed() {
         return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
@@ -2969,7 +2977,7 @@
     }
 
     public interface OnExpandClickListener {
-        void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
+        void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index 33badaf..90ff4a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -39,14 +39,10 @@
     private final NotificationDozeHelper mDozer;
     private final ViewGroup mParent;
 
-    private float mOverflowNumberSizeDark;
-    private int mOverflowNumberPaddingDark;
     private float mOverflowNumberSize;
     private int mOverflowNumberPadding;
 
     private int mOverflowNumberColor;
-    private int mOverflowNumberColorDark;
-    private float mDarkAmount = 0f;
 
     public HybridGroupManager(Context ctx, ViewGroup parent) {
         mContext = ctx;
@@ -59,12 +55,8 @@
         Resources res = mContext.getResources();
         mOverflowNumberSize = res.getDimensionPixelSize(
                 R.dimen.group_overflow_number_size);
-        mOverflowNumberSizeDark = res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_size_dark);
         mOverflowNumberPadding = res.getDimensionPixelSize(
                 R.dimen.group_overflow_number_padding);
-        mOverflowNumberPaddingDark = mOverflowNumberPadding + res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_extra_padding_dark);
     }
 
     private HybridNotificationView inflateHybridViewWithStyle(int style) {
@@ -86,13 +78,11 @@
     }
 
     private void updateOverFlowNumberColor(TextView numberView) {
-        numberView.setTextColor(NotificationUtils.interpolateColors(
-                mOverflowNumberColor, mOverflowNumberColorDark, mDarkAmount));
+        numberView.setTextColor(mOverflowNumberColor);
     }
 
-    public void setOverflowNumberColor(TextView numberView, int colorRegular, int colorDark) {
+    public void setOverflowNumberColor(TextView numberView, int colorRegular) {
         mOverflowNumberColor = colorRegular;
-        mOverflowNumberColorDark = colorDark;
         if (numberView != null) {
             updateOverFlowNumberColor(numberView);
         }
@@ -107,7 +97,7 @@
     public HybridNotificationView bindAmbientFromNotification(HybridNotificationView reusableView,
             Notification notification) {
         return bindFromNotificationWithStyle(reusableView, notification,
-                R.style.HybridNotification_Ambient);
+                R.style.HybridNotification);
     }
 
     private HybridNotificationView bindFromNotificationWithStyle(
@@ -150,6 +140,11 @@
                 R.plurals.notification_group_overflow_description, number), number);
 
         reusableView.setContentDescription(contentDescription);
+        reusableView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mOverflowNumberSize);
+        reusableView.setPaddingRelative(reusableView.getPaddingStart(),
+                reusableView.getPaddingTop(), mOverflowNumberPadding,
+                reusableView.getPaddingBottom());
+        updateOverFlowNumberColor(reusableView);
         return reusableView;
     }
 
@@ -163,16 +158,4 @@
         }
         return titleView;
     }
-
-    public void setOverflowNumberDark(TextView view, boolean dark, boolean fade, long delay) {
-        mDozer.setIntensityDark((f)->{
-            mDarkAmount = f;
-            updateOverFlowNumberColor(view);
-        }, dark, fade, delay, view);
-        view.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                dark ? mOverflowNumberSizeDark : mOverflowNumberSize);
-        int paddingEnd = dark ? mOverflowNumberPaddingDark : mOverflowNumberPadding;
-        view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), paddingEnd,
-                view.getPaddingBottom());
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 607d96d..6df72fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -20,11 +20,13 @@
         .USER_SENTIMENT_NEGATIVE;
 
 import android.content.Context;
+import android.metrics.LogMaker;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -58,6 +60,8 @@
      */
     private boolean mIsShadeExpanded;
 
+    private MetricsLogger mMetricsLogger = new MetricsLogger();
+
     @Inject
     public NotificationBlockingHelperManager(Context context) {
         mContext = context;
@@ -100,6 +104,11 @@
             mBlockingHelperRow = row;
             mBlockingHelperRow.setBlockingHelperShowing(true);
 
+            // Log triggering of blocking helper by the system. This log line
+            // should be emitted before the "display" log line.
+            mMetricsLogger.write(
+                    getLogMaker().setSubtype(MetricsEvent.BLOCKING_HELPER_TRIGGERED_BY_SYSTEM));
+
             // We don't care about the touch origin (x, y) since we're opening guts without any
             // explicit user interaction.
             manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
@@ -153,6 +162,13 @@
                 || mNonBlockablePkgs.contains(makeChannelKey(packageName, channelName));
     }
 
+    private LogMaker getLogMaker() {
+        return mBlockingHelperRow.getStatusBarNotification()
+            .getLogMaker()
+            .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+            .setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
+    }
+
     // Format must stay in sync with frameworks/base/core/res/res/values/config.xml
     // config_nonBlockableNotificationPackages
     private String makeChannelKey(String pkg, String channel) {
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 02a310c..c161da3 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
@@ -46,8 +46,8 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -1231,7 +1231,7 @@
         updateAllSingleLineViews();
     }
 
-    public void onNotificationUpdated(NotificationData.Entry entry) {
+    public void onNotificationUpdated(NotificationEntry entry) {
         mStatusBarNotification = entry.notification;
         mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
@@ -1292,7 +1292,7 @@
         }
     }
 
-    private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
+    private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
         if (mRemoteInputController == null) {
             return;
         }
@@ -1313,7 +1313,7 @@
     @VisibleForTesting
     static SmartRepliesAndActions chooseSmartRepliesAndActions(
             SmartReplyConstants smartReplyConstants,
-            final NotificationData.Entry entry) {
+            final NotificationEntry entry) {
         boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
                 && (!smartReplyConstants.requiresTargetingP()
                 || entry.targetSdk >= Build.VERSION_CODES.P));
@@ -1370,7 +1370,7 @@
                 smartReplies, smartActions, freeformRemoteInputActionPair != null);
     }
 
-    private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
+    private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
         View bigContentView = mExpandedChild;
         if (bigContentView != null) {
             mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
@@ -1402,7 +1402,7 @@
         mCachedHeadsUpRemoteInput = null;
     }
 
-    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
+    private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
             boolean hasRemoteInput, PendingIntent existingPendingIntent,
             RemoteInputView cachedView, NotificationViewWrapper wrapper) {
         View actionContainerCandidate = view.findViewById(
@@ -1470,7 +1470,7 @@
     }
 
     private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
                     applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
@@ -1489,14 +1489,14 @@
                 }
             }
         }
-        if (mHeadsUpChild != null) {
+        if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) {
             mHeadsUpSmartReplyView =
                     applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, entry);
         }
     }
 
     private SmartReplyView applySmartReplyView(View view,
-            SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
+            SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) {
         View smartReplyContainerCandidate = view.findViewById(
                 com.android.internal.R.id.smart_reply_container);
         if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
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 ac4e583..bd1dfb1 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
@@ -48,7 +48,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -116,7 +116,7 @@
         mNotificationActivityStarter = notificationActivityStarter;
     }
 
-    public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+    public void onDensityOrFontScaleChanged(NotificationEntry entry) {
         setExposedGuts(entry.getGuts());
         bindGuts(entry.getRow());
     }
@@ -429,7 +429,7 @@
     }
 
     @Override
-    public boolean shouldExtendLifetime(NotificationData.Entry entry) {
+    public boolean shouldExtendLifetime(NotificationEntry entry) {
         return entry != null
                 &&(mNotificationGutsExposed != null
                     && entry.getGuts() != null
@@ -438,7 +438,7 @@
     }
 
     @Override
-    public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) {
+    public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
         if (shouldExtend) {
             mKeyToRemoveOnGutsClosed = entry.key;
             if (Log.isLoggable(TAG, Log.DEBUG)) {
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 9908049..42ebfce 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
@@ -37,7 +37,7 @@
 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.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.Assert;
@@ -614,7 +614,7 @@
             @Nullable InflationCallback endListener, ExpandableNotificationRow row,
             boolean redactAmbient) {
         Assert.isMainThread();
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         NotificationContentView privateLayout = row.getPrivateLayout();
         NotificationContentView publicLayout = row.getPublicLayout();
         if (runningInflations.isEmpty()) {
@@ -724,7 +724,7 @@
          * @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,
+        void onAsyncInflationFinished(NotificationEntry entry,
                 @InflationFlag int inflatedFlags);
 
         /**
@@ -782,7 +782,7 @@
             mRedactAmbient = redactAmbient;
             mRemoteViewClickHandler = remoteViewClickHandler;
             mCallback = callback;
-            NotificationData.Entry entry = row.getEntry();
+            NotificationEntry entry = row.getEntry();
             entry.setInflationTask(this);
         }
 
@@ -857,7 +857,7 @@
         }
 
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry,
+        public void onAsyncInflationFinished(NotificationEntry entry,
                 @InflationFlag int inflatedFlags) {
             mRow.getEntry().onInflationTaskFinished();
             mRow.onNotificationUpdated();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index b1eab80..5253e38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -123,11 +123,15 @@
     private OnClickListener mOnKeepShowing = v -> {
         mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
         closeControls(v);
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
     };
 
     private OnClickListener mOnToggleSilent = v -> {
         Runnable saveImportance = () -> {
             swapContent(ACTION_TOGGLE_SILENT, true /* animate */);
+            mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_ALERT_ME));
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -139,6 +143,8 @@
     private OnClickListener mOnStopOrMinimizeNotifications = v -> {
         Runnable saveImportance = () -> {
             swapContent(ACTION_BLOCK, true /* animate */);
+            mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_BLOCKED));
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -153,6 +159,8 @@
         logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
         mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
         swapContent(ACTION_UNDO, true /* animate */);
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
     };
 
     public NotificationInfo(Context context, AttributeSet attrs) {
@@ -251,6 +259,9 @@
         bindHeader();
         bindPrompt();
         bindButtons();
+
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_DISPLAY));
     }
 
     private void bindHeader() throws RemoteException {
@@ -588,6 +599,8 @@
         confirmation.setAlpha(1f);
         header.setVisibility(VISIBLE);
         header.setAlpha(1f);
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_DISMISS));
     }
 
     @Override
@@ -733,4 +746,8 @@
             }
         }
     }
+
+    private LogMaker getLogMaker() {
+        return mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 1741a0b..0160c547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -25,7 +25,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * An inflater task that asynchronously inflates a ExpandableNotificationRow
@@ -36,14 +36,14 @@
     private static final boolean TRACE_ORIGIN = true;
 
     private RowInflationFinishedListener mListener;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private boolean mCancelled;
     private Throwable mInflateOrigin;
 
     /**
      * Inflates a new notificationView. This should not be called twice on this object
      */
-    public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
+    public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
             RowInflationFinishedListener listener) {
         if (TRACE_ORIGIN) {
             mInflateOrigin = new Throwable("inflate requested here");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 670908f..1b5013d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -25,7 +25,7 @@
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -75,7 +75,6 @@
     private int mIntrinsicPadding;
     private int mExpandAnimationTopChange;
     private ExpandableNotificationRow mExpandingNotification;
-    private int mDarkTopPadding;
     private float mDarkAmount;
     private boolean mAppearing;
 
@@ -351,14 +350,15 @@
     }
 
     public boolean hasPulsingNotifications() {
-        return mPulsing;
+        return mPulsing && mAmbientPulseManager != null
+                && mAmbientPulseManager.hasNotifications();
     }
 
     public void setPulsing(boolean hasPulsing) {
         mPulsing = hasPulsing;
     }
 
-    public boolean isPulsing(NotificationData.Entry entry) {
+    public boolean isPulsing(NotificationEntry entry) {
         if (!mPulsing || mAmbientPulseManager == null) {
             return false;
         }
@@ -458,14 +458,6 @@
         return mDarkAmount != 0;
     }
 
-    public void setDarkTopPadding(int darkTopPadding) {
-        mDarkTopPadding = darkTopPadding;
-    }
-
-    public int getDarkTopPadding() {
-        return mDarkTopPadding;
-    }
-
     public void setAppearing(boolean appearing) {
         mAppearing = appearing;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 5118036..8ffada4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -316,7 +316,7 @@
         StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
         final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
                 notification.getNotification());
-        RemoteViews header = builder.makeNotificationHeader(false /* ambient */);
+        RemoteViews header = builder.makeNotificationHeader();
         if (mNotificationHeader == null) {
             mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
             final View expandButton = mNotificationHeader.findViewById(
@@ -344,7 +344,7 @@
             builder = Notification.Builder.recoverBuilder(getContext(),
                     notification.getNotification());
         }
-        header = builder.makeNotificationHeader(true /* ambient */);
+        header = builder.makeNotificationHeader();
         if (mNotificationHeaderAmbient == null) {
             mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this);
             mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(),
@@ -1171,12 +1171,6 @@
         return mIsLowPriority && !mContainingNotification.isExpanded();
     }
 
-    public void setDark(boolean dark, boolean fade, long delay) {
-        if (mOverflowNumber != null) {
-            mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay);
-        }
-    }
-
     public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
         if (mNotificationHeader != null) {
             removeView(mNotificationHeader);
@@ -1227,8 +1221,7 @@
 
     public void onNotificationUpdated() {
         mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
-                mContainingNotification.getNotificationColor(),
-                mContainingNotification.getNotificationColorAmbient());
+                mContainingNotification.getNotificationColor());
     }
 
     public int getPositionInLinearLayout(View childInGroup) {
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 f0a2653..f771be0 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
@@ -16,15 +16,14 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -113,19 +112,12 @@
     void setMaxDisplayedNotifications(int maxNotifications);
 
     /**
-     * Handle snapping a non-dismissable row back if the user tried to dismiss it.
-     *
-     * @param entry the entry whose row needs to snap back
-     */
-    void snapViewIfNeeded(NotificationData.Entry entry);
-
-    /**
      * Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
      *
      * @param entry entry to get the view parent for
      * @return the view parent for entry
      */
-    ViewGroup getViewParentForNotification(NotificationData.Entry entry);
+    ViewGroup getViewParentForNotification(NotificationEntry entry);
 
     /**
      * Resets the currently exposed menu view.
@@ -148,7 +140,7 @@
      *
      * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
      */
-    void cleanUpViewStateForEntry(NotificationData.Entry entry);
+    void cleanUpViewStateForEntry(NotificationEntry entry);
 
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 4f0831f1..c5ab9f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,7 +18,8 @@
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -26,35 +27,43 @@
 
 import java.util.HashSet;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A class that manages the roundness for notification views
  */
-class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+@Singleton
+class NotificationRoundnessManager implements OnHeadsUpChangedListener,
+        AmbientPulseManager.OnAmbientChangedListener {
 
+    private final ActivatableNotificationView[] mFirstInSectionViews;
+    private final ActivatableNotificationView[] mLastInSectionViews;
+    private final ActivatableNotificationView[] mTmpFirstInSectionViews;
+    private final ActivatableNotificationView[] mTmpLastInSectionViews;
     private boolean mExpanded;
-    private ActivatableNotificationView[] mFirstInSectionViews;
-    private ActivatableNotificationView[] mLastInSectionViews;
-    private ActivatableNotificationView[] mTmpFirstInSectionViews;
-    private ActivatableNotificationView[] mTmpLastInSectionViews;
     private HashSet<ExpandableView> mAnimatedChildren;
     private Runnable mRoundingChangedCallback;
     private ExpandableNotificationRow mTrackedHeadsUp;
+    private ActivatableNotificationView mTrackedAmbient;
     private float mAppearFraction;
 
-    NotificationRoundnessManager() {
+    @Inject
+    NotificationRoundnessManager(AmbientPulseManager ambientPulseManager) {
         mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
+        ambientPulseManager.addListener(this);
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+    public void onHeadsUpPinned(NotificationEntry headsUp) {
         updateView(headsUp.getRow(), false /* animate */);
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+    public void onHeadsUpUnPinned(NotificationEntry headsUp) {
         updateView(headsUp.getRow(), true /* animate */);
     }
 
@@ -63,6 +72,17 @@
         updateView(row, false /* animate */);
     }
 
+    @Override
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing) {
+        ActivatableNotificationView row = entry.getRow();
+        if (isPulsing) {
+            mTrackedAmbient = row;
+        } else if (mTrackedAmbient == row) {
+            mTrackedAmbient = null;
+        }
+        updateView(row, false /* animate */);
+    }
+
     private void updateView(ActivatableNotificationView view, boolean animate) {
         boolean changed = updateViewWithoutCallback(view, animate);
         if (changed) {
@@ -125,6 +145,9 @@
             // rounded.
             return 1.0f;
         }
+        if (view == mTrackedAmbient) {
+            return 1.0f;
+        }
         return 0.0f;
     }
 
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 8deb7d5..d066567 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
@@ -96,12 +96,13 @@
 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.NotificationEntryListener;
 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.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -112,6 +113,7 @@
 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.HeadsUpTouchHelper;
@@ -199,12 +201,7 @@
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
     private int mMaxTopPadding;
-    private int mRegularTopPadding;
-    private int mDarkTopPadding;
-    // Current padding, will be either mRegularTopPadding or mDarkTopPadding
     private int mTopPadding;
-    // Distance between AOD separator and shelf
-    private int mDarkShelfPadding;
     private int mBottomMargin;
     private int mBottomInset = 0;
     private float mQsExpansionFraction;
@@ -316,7 +313,7 @@
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
     private HeadsUpManagerPhone mHeadsUpManager;
-    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private final NotificationRoundnessManager mRoundnessManager;
     private boolean mTrackingHeadsUp;
     private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
@@ -429,7 +426,7 @@
     private int mHeadsUpInset;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private NotificationIconAreaController mIconAreaController;
-    private float mVerticalPanelTranslation;
+    private float mHorizontalPanelTranslation;
     private final NotificationLockscreenUserManager mLockscreenUserManager =
             Dependency.get(NotificationLockscreenUserManager.class);
     protected final NotificationGutsManager mGutsManager =
@@ -439,7 +436,8 @@
             Dependency.get(NotificationEntryManager.class);
     private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface(
             ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    @VisibleForTesting
+    protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     private final NotificationRemoteInputManager mRemoteInputManager =
             Dependency.get(NotificationRemoteInputManager.class);
     private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
@@ -457,12 +455,17 @@
 
     private final NotificationGutsManager
             mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
+    /**
+     * If the {@link NotificationShelf} should be visible when dark.
+     */
+    private boolean mShowDarkShelf;
 
     @Inject
     public NotificationStackScrollLayout(
             @Named(VIEW_CONTEXT) Context context,
             AttributeSet attrs,
-            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
+            NotificationRoundnessManager notificationRoundnessManager) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
 
@@ -473,6 +476,7 @@
         }
 
         mAmbientState = new AmbientState(context);
+        mRoundnessManager = notificationRoundnessManager;
         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);
@@ -489,7 +493,6 @@
                 res.getBoolean(R.bool.config_drawNotificationBackground);
         mFadeNotificationsOnDismiss =
                 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
-        mDarkShelfPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
         mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
         mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
         addOnExpandedHeightListener(mRoundnessManager::setExpanded);
@@ -517,6 +520,17 @@
                 mLowPriorityBeforeSpeedBump = "1".equals(newValue);
             }
         }, LOW_PRIORITY);
+
+        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onPostEntryUpdated(NotificationEntry entry) {
+                if (!entry.notification.isClearable()) {
+                    // The user may have performed a dismiss action on the notification, since it's
+                    // not clearable we should snap it back.
+                    snapViewIfNeeded(entry);
+                }
+            }
+        });
     }
 
     @Override
@@ -590,14 +604,14 @@
   @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
   public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
-            public void setRemoteInputActive(NotificationData.Entry entry,
+            public void setRemoteInputActive(NotificationEntry entry,
                     boolean remoteInputActive) {
                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
                 entry.notifyHeightChanged(true /* needsAnimation */);
                 updateFooter();
             }
 
-            public void lockScrollTo(NotificationData.Entry entry) {
+            public void lockScrollTo(NotificationEntry entry) {
                 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
             }
 
@@ -667,7 +681,7 @@
         int lockScreenTop = mSections[0].getCurrentBounds().top;
         int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom;
         int darkLeft = getWidth() / 2;
-        int darkTop = mRegularTopPadding;
+        int darkTop = mTopPadding;
 
         float yProgress = 1 - mInterpolatedDarkAmount;
         float xProgress = mDarkXInterpolator.getInterpolation(
@@ -910,7 +924,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    public boolean isInVisibleLocation(NotificationData.Entry entry) {
+    public boolean isInVisibleLocation(NotificationEntry entry) {
         ExpandableNotificationRow row = entry.getRow();
         ExpandableViewState childViewState = row.getViewState();
 
@@ -935,8 +949,6 @@
 
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     private void updateAlgorithmHeightAndPadding() {
-        mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
-                mInterpolatedDarkAmount);
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
@@ -1074,10 +1086,8 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void setTopPadding(int topPadding, boolean animate) {
-        if (mRegularTopPadding != topPadding) {
-            mRegularTopPadding = topPadding;
-            mDarkTopPadding = topPadding + mDarkShelfPadding;
-            mAmbientState.setDarkTopPadding(mDarkTopPadding);
+        if (mTopPadding != topPadding) {
+            mTopPadding = topPadding;
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
             if (animate && mAnimationsEnabled && mIsExpanded) {
@@ -1184,7 +1194,7 @@
             mIsClipped = clipped;
         }
 
-        if (mPulsing) {
+        if (mPulsing || mAmbientState.isFullyDark() && mShowDarkShelf) {
             setClipBounds(null);
         } else if (mAmbientState.isDarkAtAll()) {
             setClipBounds(mBackgroundAnimationRect);
@@ -1224,13 +1234,13 @@
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getTopHeadsUpPinnedHeight() {
-        NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+        NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
         if (topEntry == null) {
             return 0;
         }
         ExpandableNotificationRow row = topEntry.getRow();
         if (row.isChildInGroup()) {
-            final NotificationData.Entry groupSummary
+            final NotificationEntry groupSummary
                     = mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null) {
                 row = groupSummary.getRow();
@@ -1405,7 +1415,7 @@
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
-                    NotificationData.Entry entry = row.getEntry();
+                    NotificationEntry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
                             && mHeadsUpManager.getTopEntry().getRow() != row
                             && mGroupManager.getGroupSummary(
@@ -1538,9 +1548,8 @@
                 true /* isDismissAll */);
     }
 
-    @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void snapViewIfNeeded(NotificationData.Entry entry) {
+    private void snapViewIfNeeded(NotificationEntry entry) {
         ExpandableNotificationRow child = entry.getRow();
         boolean animate = mIsExpanded || isPinnedHeadsUp(child);
         // If the child is showing the notification menu snap to that
@@ -1550,7 +1559,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+    public ViewGroup getViewParentForNotification(NotificationEntry entry) {
         return this;
     }
 
@@ -2043,17 +2052,14 @@
         }
         mIntrinsicContentHeight = height;
 
-        // We don't want to use the toppadding since that might be interpolated and we want
-        // to take the final value of the animation.
-        int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding;
-        mContentHeight = height + topPadding + mBottomMargin;
+        mContentHeight = height + mTopPadding + mBottomMargin;
         updateScrollability();
         clampScrollPosition();
         mAmbientState.setLayoutMaxHeight(mContentHeight);
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private boolean isPulsing(NotificationData.Entry entry) {
+    private boolean isPulsing(NotificationEntry entry) {
         return mAmbientState.isPulsing(entry);
     }
 
@@ -2210,13 +2216,16 @@
         int top = 0;
         if (section != null) {
             ActivatableNotificationView firstView = section.getFirstVisibleChild();
-            // Round Y up to avoid seeing the background during animation
-            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
-            if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
-                // we're ending up at the same location as we are now, let's just skip the animation
-                top = finalTranslationY;
-            } else {
-                top = (int) Math.ceil(firstView.getTranslationY());
+            if (firstView != null) {
+                // Round Y up to avoid seeing the background during animation
+                int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+                if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
+                    // we're ending up at the same location as we are now, let's just skip the
+                    // animation
+                    top = finalTranslationY;
+                } else {
+                    top = (int) Math.ceil(firstView.getTranslationY());
+                }
             }
         }
         return top;
@@ -2558,7 +2567,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     @Override
-    public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+    public void cleanUpViewStateForEntry(NotificationEntry entry) {
         View child = entry.getRow();
         if (child == mSwipeHelper.getTranslatingParentView()) {
             mSwipeHelper.clearTranslatingParentView();
@@ -2686,7 +2695,7 @@
     private boolean isChildInInvisibleGroup(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            NotificationData.Entry groupSummary =
+            NotificationEntry groupSummary =
                     mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null && groupSummary.getRow() != row) {
                 return row.getVisibility() == View.INVISIBLE;
@@ -4350,6 +4359,9 @@
         if (mAmbientState.isDark() == dark) {
             return;
         }
+        if (!dark) {
+            mShowDarkShelf = false;
+        }
         mAmbientState.setDark(dark);
         if (animate && mAnimationsEnabled) {
             mDarkNeedsAnimation = true;
@@ -4366,12 +4378,12 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updatePanelTranslation() {
-        setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
+        setTranslationX(mHorizontalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setVerticalPanelTranslation(float verticalPanelTranslation) {
-        mVerticalPanelTranslation = verticalPanelTranslation;
+    public void setHorizontalPanelTranslation(float verticalPanelTranslation) {
+        mHorizontalPanelTranslation = verticalPanelTranslation;
         updatePanelTranslation();
     }
 
@@ -4411,9 +4423,9 @@
         boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
         if (nowFullyDark != wasFullyDark) {
             updateContentHeight();
-        }
-        if (mIconAreaController != null) {
-            mIconAreaController.setDarkAmount(interpolatedDarkAmount);
+            if (nowFullyDark && mShowDarkShelf) {
+                updateDarkShelfVisibility();
+            }
         }
         if (!wasDarkAtAll && nowDarkAtAll) {
             resetExposedMenuView(true /* animate */, true /* animate */);
@@ -4424,6 +4436,22 @@
         requestChildrenUpdate();
     }
 
+    /**
+     * If the shelf should be visible when the device is in ambient mode (dozing.)
+     */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    public void setShowDarkShelf(boolean showDarkShelf) {
+        mShowDarkShelf = showDarkShelf;
+    }
+
+    private void updateDarkShelfVisibility() {
+        DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
+        if (dozeParameters.shouldControlScreenOff()) {
+            mShelf.fadeInTranslating();
+        }
+        updateClipping();
+    }
+
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void notifyDarkAnimationStart(boolean dark) {
         // We only swap the scaling factor if we're fully dark or fully awake to avoid
@@ -4705,7 +4733,7 @@
         mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
-    public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
         ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
         generateHeadsUpAnimation(row, isHeadsUp);
     }
@@ -5183,9 +5211,6 @@
         // another "changeViewPosition" call is ever added.
         changeViewPosition(mShelf,
                 getChildCount() - offsetFromEnd);
-
-        // Scrim opacity varies based on notification count
-        mScrimController.setNotificationCount(getNotGoneChildCount());
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5603,8 +5628,9 @@
       }
     };
 
+    @VisibleForTesting
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
+    protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
         @Override
         public void onMenuClicked(View view, int x, int y, MenuItem item) {
             if (mLongPressListener == null) {
@@ -5612,8 +5638,10 @@
             }
             if (view instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
-                        row.getStatusBarNotification().getPackageName());
+                mMetricsLogger.write(row.getStatusBarNotification().getLogMaker()
+                        .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
+                        .setType(MetricsEvent.TYPE_ACTION)
+                        );
             }
             mLongPressListener.onLongPress(view, x, y, item);
         }
@@ -5636,8 +5664,9 @@
         public void onMenuShown(View row) {
             if (row instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
-                        notificationRow.getStatusBarNotification().getPackageName());
+                mMetricsLogger.write(notificationRow.getStatusBarNotification().getLogMaker()
+                        .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
+                        .setType(MetricsEvent.TYPE_ACTION));
                 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
             }
             mSwipeHelper.onMenuShown(row);
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 f1d9549..975aee5 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
@@ -189,10 +189,13 @@
         boolean isFastNonDismissGesture =
                 gestureFastEnough && !gestureTowardsMenu && !isDismissGesture;
         boolean isMenuRevealingGestureAwayFromMenu = slowSwipedFarEnough || isFastNonDismissGesture;
-        if (isNonDismissGestureTowardsMenu
-                || (!isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu)) {
+        int menuSnapTarget = menuRow.getMenuSnapTarget();
+        boolean isNonFalseMenuRevealingGesture =
+                !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
+        if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
+                && menuSnapTarget != 0) {
             // Menu has not been snapped to previously and this is menu revealing gesture
-            snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
+            snapOpen(animView, menuSnapTarget, velocity);
             menuRow.onSnapOpen();
         } else if (isDismissGesture(ev) && !gestureTowardsMenu) {
             dismiss(animView, velocity);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 19fce48..b4c205a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -426,6 +426,9 @@
                     mTmpState.yTranslation += mPulsingAppearingTranslation;
                     mTmpState.alpha = 0;
                     mTmpState.applyToView(changingView);
+
+                    mTmpState.copyFrom(mShelf.getViewState());
+                    mTmpState.applyToView(mShelf);
                 }
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
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 f907b65..35763dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -215,7 +215,10 @@
             }
         }
 
-        if (mStatusBarStateController.isDozing()) {
+        // The shelf will be hidden when dozing with a custom clock, we must show notification
+        // icons in this occasion.
+        if (mStatusBarStateController.isDozing()
+                && mStatusBarComponent.getPanel().hasCustomClock()) {
             state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
             state &= ~DISABLE_NOTIFICATION_ICONS;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 6f2b63d..1049773 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -158,8 +158,7 @@
      * @return duration in millis.
      */
     public long getWallpaperAodDuration() {
-        if (mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(UserHandle.USER_CURRENT)
-                || shouldControlScreenOff()) {
+        if (shouldControlScreenOff()) {
             return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY;
         }
         return mAlwaysOnPolicy.wallpaperVisibilityDuration;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index d1e488a..876b902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -28,7 +28,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -143,7 +143,7 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         updateTopEntry();
         updateHeader(entry);
     }
@@ -206,11 +206,11 @@
     }
 
     private void updateTopEntry() {
-        NotificationData.Entry newEntry = null;
+        NotificationEntry newEntry = null;
         if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
             newEntry = mHeadsUpManager.getTopEntry();
         }
-        NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
+        NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
         mHeadsUpStatusBarView.setEntry(newEntry);
         if (newEntry != previousEntry) {
             boolean animateIsolation = false;
@@ -298,7 +298,7 @@
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
         updateTopEntry();
         updateHeader(entry);
     }
@@ -338,7 +338,7 @@
         });
     }
 
-    public void updateHeader(NotificationData.Entry entry) {
+    public void updateHeader(NotificationEntry entry) {
         ExpandableNotificationRow row = entry.getRow();
         float headerVisibleAmount = 1.0f;
         if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index f4cfd41..7f75223 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -41,8 +41,8 @@
 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.NotificationData;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -58,7 +58,7 @@
  */
 public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
         ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
-        OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener {
+        OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener, StateListener {
     private static final String TAG = "HeadsUpManagerPhone";
 
     private final View mStatusBarWindowView;
@@ -72,8 +72,8 @@
     private int mDisplayCutoutTouchableRegionSize;
     private boolean mTrackingHeadsUp;
     private HashSet<String> mSwipedOutKeys = new HashSet<>();
-    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
-    private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+    private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
+    private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
             = new ArraySet<>();
     private boolean mIsExpanded;
     private int[] mTmpTwoArray = new int[2];
@@ -83,7 +83,6 @@
     private boolean mIsObserving;
     private int mStatusBarState;
 
-    private final StateListener mStateListener = this::setStatusBarState;
     private AnimationStateHandler mAnimationStateHandler;
     private BubbleController mBubbleController = Dependency.get(BubbleController.class);
 
@@ -129,7 +128,7 @@
                 updateTouchableRegionListener();
             }
         });
-        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
         mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
             if (!hasBubbles) {
                 mBubbleGoingAway = true;
@@ -143,7 +142,7 @@
     }
 
     public void destroy() {
-        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(this);
     }
 
     private void initResources() {
@@ -187,7 +186,7 @@
             releaseAllImmediately();
             mReleaseOnExpandFinish = false;
         } else {
-            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+            for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
                 if (isAlerting(entry.key)) {
                     // Maybe the heads-up was removed already
                     removeAlertEntry(entry.key);
@@ -225,11 +224,9 @@
         }
     }
 
-    /**
-     * Set the current state of the statusbar.
-     */
-    private void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
+    @Override
+    public void onStateChanged(int newState) {
+        mStatusBarState = newState;
     }
 
     /**
@@ -252,7 +249,7 @@
      * @param remoteInputActive True to notify active, False to notify inactive.
      */
     public void setRemoteInputActive(
-            @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
+            @NonNull NotificationEntry entry, boolean remoteInputActive) {
         HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
         if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
             headsUpEntry.remoteInputActive = remoteInputActive;
@@ -268,7 +265,7 @@
      * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
      * area if it's pinned until it's hidden again.
      */
-    public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) {
+    public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
         if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
             ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
@@ -315,9 +312,9 @@
             return;
         }
         if (hasPinnedHeadsUp()) {
-            NotificationData.Entry topEntry = getTopEntry();
+            NotificationEntry topEntry = getTopEntry();
             if (topEntry.isChildInGroup()) {
-                final NotificationData.Entry groupSummary
+                final NotificationEntry groupSummary
                         = mGroupManager.getGroupSummary(topEntry.notification);
                 if (groupSummary != null) {
                     topEntry = groupSummary;
@@ -374,7 +371,7 @@
     @Override
     public void onReorderingAllowed() {
         mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
-        for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+        for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
             if (isAlerting(entry.key)) {
                 // Maybe the heads-up was removed already
                 removeAlertEntry(entry.key);
@@ -399,7 +396,7 @@
     }
 
     @Override
-    protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+    protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
           return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
                   || super.shouldHeadsUpBecomePinned(entry);
     }
@@ -488,7 +485,7 @@
             return super.isSticky() || mMenuShownPinned;
         }
 
-        public void setEntry(@NonNull final NotificationData.Entry entry) {
+        public void setEntry(@NonNull final NotificationEntry entry) {
            Runnable removeHeadsUpRunnable = () -> {
                 if (!mVisualStabilityManager.isReorderingAllowed()) {
                     mEntriesToRemoveWhenReorderingAllowed.add(entry);
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 9c1c71a..dd200da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,7 +21,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
@@ -83,7 +83,7 @@
                 } 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();
+                    NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
                     if (topEntry != null && topEntry.isRowPinned()) {
                         mPickedChild = topEntry.getRow();
                         mTouchingHeadsUpView = true;
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 a81b7e5..c68fdd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -91,11 +91,6 @@
     private int mBurnInPreventionOffsetY;
 
     /**
-     * Clock vertical padding when pulsing.
-     */
-    private int mPulsingPadding;
-
-    /**
      * Doze/AOD transition amount.
      */
     private float mDarkAmount;
@@ -105,10 +100,6 @@
      */
     private boolean mCurrentlySecure;
 
-    /**
-     * Dozing and receiving a notification (AOD notification.)
-     */
-    private boolean mPulsing;
     private float mEmptyDragAmount;
 
     /**
@@ -123,13 +114,11 @@
                 R.dimen.burn_in_prevention_offset_x);
         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_y);
-        mPulsingPadding = res.getDimensionPixelSize(
-                R.dimen.widget_pulsing_bottom_padding);
     }
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
             float panelExpansion, int parentHeight, int keyguardStatusHeight, float dark,
-            boolean secure, boolean pulsing, float emptyDragAmount) {
+            boolean secure, float emptyDragAmount) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
@@ -138,7 +127,6 @@
         mKeyguardStatusHeight = keyguardStatusHeight;
         mDarkAmount = dark;
         mCurrentlySecure = secure;
-        mPulsing = pulsing;
         mEmptyDragAmount = emptyDragAmount;
     }
 
@@ -146,7 +134,7 @@
         final int y = getClockY();
         result.clockY = y;
         result.clockAlpha = getClockAlpha(y);
-        result.stackScrollerPadding = y + (mPulsing ? mPulsingPadding : mKeyguardStatusHeight);
+        result.stackScrollerPadding = y + mKeyguardStatusHeight;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
@@ -185,9 +173,6 @@
     private int getClockY() {
         // Dark: Align the bottom edge of the clock at about half of the screen:
         float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
-        if (mPulsing) {
-            clockYDark -= mPulsingPadding;
-        }
         clockYDark = MathUtils.max(0, clockYDark);
 
         float clockYRegular = getExpandedClockPosition();
@@ -230,11 +215,6 @@
                 - mBurnInPreventionOffsetX;
     }
 
-    @VisibleForTesting
-    void setPulsingPadding(int padding) {
-        mPulsingPadding = padding;
-    }
-
     public static class Result {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index 927228e..925a19d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -22,7 +22,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 public class KeyguardEnvironmentImpl implements KeyguardEnvironment {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java
new file mode 100644
index 0000000..323e776
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 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.annotation.NonNull;
+import android.os.Bundle;
+import android.view.MotionEvent;
+
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.recents.OverviewProxyService;
+
+/**
+ * Assistant is triggered with this action
+ */
+public class NavigationAssistantAction extends NavigationGestureAction {
+    private static final String TAG = "NavigationAssistantActions";
+
+    private final AssistManager mAssistManager;
+
+    public NavigationAssistantAction(@NonNull NavigationBarView navigationBarView,
+            @NonNull OverviewProxyService service, AssistManager assistManager) {
+        super(navigationBarView, service);
+        mAssistManager = assistManager;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean disableProxyEvents() {
+        return true;
+    }
+
+    @Override
+    public void onGestureStart(MotionEvent event) {
+        mAssistManager.startAssist(new Bundle());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
index 9c8b1b1..7a42b03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -67,7 +67,7 @@
 
     @Override
     public boolean isEnabled() {
-        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED);
+        return true;
     }
 
     @Override
@@ -95,6 +95,11 @@
         }
     }
 
+    @Override
+    public boolean disableProxyEvents() {
+        return true;
+    }
+
     private void performBack() {
         sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
         sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
@@ -102,8 +107,7 @@
     }
 
     private boolean shouldExecuteBackOnUp() {
-        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED)
-                && getGlobalBoolean(BACK_AFTER_END_PROP);
+        return getGlobalBoolean(BACK_AFTER_END_PROP);
     }
 
     private void sendEvent(int action, int code) {
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 6d97d67..d3c6a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -269,7 +269,7 @@
             mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
         }
 
-        mNavigationBarView.setComponents(mStatusBar.getPanel());
+        mNavigationBarView.setComponents(mStatusBar.getPanel(), mAssistManager);
         mNavigationBarView.setDisabledFlags(mDisabledFlags1);
         mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
         mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
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 2fc7b78..8bf1c58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -69,6 +69,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.statusbar.phone.NavGesture;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
@@ -156,6 +157,7 @@
     private QuickStepAction mQuickStepAction;
     private NavigationBackAction mBackAction;
     private QuickSwitchAction mQuickSwitchAction;
+    private NavigationAssistantAction mAssistantAction;
 
     /**
      * Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -366,8 +368,12 @@
         return mBarTransitions.getLightTransitionsController();
     }
 
-    public void setComponents(NotificationPanelView panel) {
+    public void setComponents(NotificationPanelView panel, AssistManager assistManager) {
         mPanelView = panel;
+        if (mAssistantAction == null) {
+            mAssistantAction = new NavigationAssistantAction(this, mOverviewProxyService,
+                    assistManager);
+        }
         if (mGestureHelper instanceof QuickStepController) {
             ((QuickStepController) mGestureHelper).setComponents(this);
             updateNavigationGestures();
@@ -398,6 +404,10 @@
                 return mBackAction;
             case NavigationPrototypeController.ACTION_QUICKSWITCH:
                 return mQuickSwitchAction;
+            case NavigationPrototypeController.ACTION_ASSISTANT:
+                return mAssistantAction;
+            case NavigationPrototypeController.ACTION_NOTHING:
+                return null;
             default:
                 return defaultAction;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index 8c57fc3..a5d9382 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -24,6 +24,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.provider.Settings;
 import android.view.MotionEvent;
 
 import com.android.systemui.recents.OverviewProxyService;
@@ -32,6 +33,9 @@
  * A gesture action that would be triggered and reassigned by {@link QuickStepController}
  */
 public abstract class NavigationGestureAction {
+    private static final String ENABLE_TASK_STABILIZER_FLAG = "ENABLE_TASK_STABILIZER";
+
+    static private boolean sLastTaskStabilizationFlag;
 
     protected final NavigationBarView mNavigationBarView;
     protected final OverviewProxyService mProxySender;
@@ -45,6 +49,9 @@
             @NonNull OverviewProxyService service) {
         mNavigationBarView = navigationBarView;
         mProxySender = service;
+        sLastTaskStabilizationFlag = Settings.Global.getInt(
+                mNavigationBarView.getContext().getContentResolver(),
+                ENABLE_TASK_STABILIZER_FLAG, 0) != 0;
     }
 
     /**
@@ -74,6 +81,15 @@
      */
     public void startGesture(MotionEvent event) {
         mIsActive = true;
+
+        // Tell launcher that this action requires a stable task list or not
+        boolean flag = requiresStableTaskList();
+        if (flag != sLastTaskStabilizationFlag) {
+            Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
+                    ENABLE_TASK_STABILIZER_FLAG, flag ? 1 : 0);
+            sLastTaskStabilizationFlag = flag;
+        }
+
         onGestureStart(event);
     }
 
@@ -146,6 +162,13 @@
      */
     public abstract boolean isEnabled();
 
+    /**
+     * @return action requires a stable task list from launcher
+     */
+    protected boolean requiresStableTaskList() {
+        return false;
+    }
+
     protected void onDarkIntensityChange(float intensity) {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index fb6254b..a09e585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -22,7 +22,6 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -36,18 +35,20 @@
     private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
     private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
 
-    static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
     private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
     public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
+    @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
+            ACTION_QUICKSWITCH, ACTION_NOTHING, ACTION_ASSISTANT})
     @interface GestureAction {}
     static final int ACTION_DEFAULT = 0;
     static final int ACTION_QUICKSTEP = 1;
     static final int ACTION_QUICKSCRUB = 2;
     static final int ACTION_BACK = 3;
     static final int ACTION_QUICKSWITCH = 4;
+    static final int ACTION_NOTHING = 5;
+    static final int ACTION_ASSISTANT = 6;
 
     private OnPrototypeChangedListener mListener;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3839ed5..d364356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -31,9 +31,9 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
@@ -108,7 +108,7 @@
      * @param entry notification to check
      * @return true if the entry was transferred to and should inflate + alert
      */
-    public boolean isAlertTransferPending(@NonNull Entry entry) {
+    public boolean isAlertTransferPending(@NonNull NotificationEntry entry) {
         PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key);
         return alertInfo != null && alertInfo.isStillValid();
     }
@@ -172,16 +172,16 @@
     };
 
     @Override
-    public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
     }
 
     @Override
-    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
     }
 
-    private void onAlertStateChanged(Entry entry, boolean isAlerting,
+    private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
             AlertingNotificationManager alertManager) {
         if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) {
             handleSuppressedSummaryAlerted(entry, alertManager);
@@ -193,7 +193,7 @@
         // Called when a new notification has been posted but is not inflated yet. We use this to
         // see as early as we can if we need to abort a transfer.
         @Override
-        public void onPendingEntryAdded(Entry entry) {
+        public void onPendingEntryAdded(NotificationEntry entry) {
             String groupKey = mGroupManager.getGroupKey(entry.notification);
             GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
             if (groupAlertEntry != null) {
@@ -204,7 +204,7 @@
         // Called when the entry's reinflation has finished. If there is an alert pending, we
         // then show the alert.
         @Override
-        public void onEntryReinflated(Entry entry) {
+        public void onEntryReinflated(NotificationEntry entry) {
             PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key);
             if (alertInfo != null) {
                 if (alertInfo.isStillValid()) {
@@ -219,16 +219,13 @@
 
         @Override
         public void onEntryRemoved(
-                @Nullable Entry entry,
-                String key,
-                StatusBarNotification old,
+                @Nullable NotificationEntry entry,
                 NotificationVisibility visibility,
-                boolean lifetimeExtended,
                 boolean removedByUser) {
             // Removes any alerts pending on this entry. Note that this will not stop any inflation
             // tasks started by a transfer, so this should only be used as clean-up for when
             // inflation is stopped and the pending alert no longer needs to happen.
-            mPendingAlerts.remove(key);
+            mPendingAlerts.remove(entry.key);
         }
     };
 
@@ -244,8 +241,8 @@
             return 0;
         }
         int number = 0;
-        Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator();
-        for (Entry entry : values) {
+        Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+        for (NotificationEntry entry : values) {
             if (isPendingNotificationInGroup(entry, group) && onlySummaryAlerts(entry)) {
                 number++;
             }
@@ -263,8 +260,8 @@
         if (mEntryManager == null) {
             return false;
         }
-        Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator();
-        for (Entry entry : values) {
+        Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+        for (NotificationEntry entry : values) {
             if (isPendingNotificationInGroup(entry, group)) {
                 return true;
             }
@@ -279,7 +276,7 @@
      * @param group group to check
      * @return true if the notification will add to the group, false o/w
      */
-    private boolean isPendingNotificationInGroup(@NonNull Entry entry,
+    private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry,
             @NonNull NotificationGroup group) {
         String groupKey = mGroupManager.getGroupKey(group.summary.notification);
         return mGroupManager.isGroupChild(entry.notification)
@@ -296,7 +293,7 @@
      * @param summary the summary that is suppressed and alerting
      * @param alertManager the alert manager that manages the alerting summary
      */
-    private void handleSuppressedSummaryAlerted(@NonNull Entry summary,
+    private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
             @NonNull AlertingNotificationManager alertManager) {
         StatusBarNotification sbn = summary.notification;
         GroupAlertEntry groupAlertEntry =
@@ -312,7 +309,7 @@
             return;
         }
 
-        Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
+        NotificationEntry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
         if (child != null) {
             if (child.getRow().keepInParent()
                     || child.isRowRemoved()
@@ -336,7 +333,7 @@
      * @param toEntry entry to transfer to
      * @param alertManager alert manager for the alert type
      */
-    private void transferAlertState(@NonNull Entry fromEntry, @NonNull Entry toEntry,
+    private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
             @NonNull AlertingNotificationManager alertManager) {
         alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */);
         alertNotificationWhenPossible(toEntry, alertManager);
@@ -356,13 +353,13 @@
     private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
         if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
                 < ALERT_TRANSFER_TIMEOUT) {
-            Entry summary = groupAlertEntry.mGroup.summary;
+            NotificationEntry summary = groupAlertEntry.mGroup.summary;
             AlertingNotificationManager alertManager = getActiveAlertManager();
 
             if (!onlySummaryAlerts(summary)) {
                 return;
             }
-            ArrayList<Entry> children = mGroupManager.getLogicalChildren(summary.notification);
+            ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.notification);
             int numChildren = children.size();
             int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
             numChildren += numPendingChildren;
@@ -371,7 +368,7 @@
             }
             boolean releasedChild = false;
             for (int i = 0; i < children.size(); i++) {
-                Entry entry = children.get(i);
+                NotificationEntry entry = children.get(i);
                 if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
                     releasedChild = true;
                     alertManager.removeNotification(entry.key, true /* releaseImmediately */);
@@ -402,7 +399,7 @@
      * @param entry entry to show
      * @param alertManager alert manager for the alert type
      */
-    private void alertNotificationWhenPossible(@NonNull Entry entry,
+    private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
             @NonNull AlertingNotificationManager alertManager) {
         @InflationFlag int contentFlag = alertManager.getContentFlag();
         if (!entry.getRow().isInflationFlagSet(contentFlag)) {
@@ -422,7 +419,7 @@
         return mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
     }
 
-    private boolean onlySummaryAlerts(Entry entry) {
+    private boolean onlySummaryAlerts(NotificationEntry entry) {
         return entry.notification.getNotification().getGroupAlertBehavior()
                 == Notification.GROUP_ALERT_SUMMARY;
     }
@@ -442,7 +439,7 @@
          * the transfer is still valid if the notification is updated.
          */
         final StatusBarNotification mOriginalNotification;
-        final Entry mEntry;
+        final NotificationEntry mEntry;
 
         /**
          * The notification is still pending inflation but we've decided that we no longer need
@@ -453,7 +450,7 @@
          */
         boolean mAbortOnInflation;
 
-        PendingAlertInfo(Entry entry, AlertingNotificationManager alertManager) {
+        PendingAlertInfo(NotificationEntry entry, AlertingNotificationManager alertManager) {
             mOriginalNotification = entry.notification;
             mEntry = entry;
             mAlertManager = alertManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 3c1c076..bb9e418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -27,7 +27,7 @@
 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.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -97,7 +97,7 @@
         }
     }
 
-    public void onEntryRemoved(NotificationData.Entry removed) {
+    public void onEntryRemoved(NotificationEntry removed) {
         onEntryRemovedInternal(removed, removed.notification);
         mIsolatedEntries.remove(removed.key);
     }
@@ -109,7 +109,7 @@
      * @param sbn the notification the entry has, which doesn't need to be the same as it's internal
      *            notification
      */
-    private void onEntryRemovedInternal(NotificationData.Entry removed,
+    private void onEntryRemovedInternal(NotificationEntry removed,
             final StatusBarNotification sbn) {
         String groupKey = getGroupKey(sbn);
         final NotificationGroup group = mGroupMap.get(groupKey);
@@ -136,7 +136,7 @@
         }
     }
 
-    public void onEntryAdded(final NotificationData.Entry added) {
+    public void onEntryAdded(final NotificationEntry added) {
         if (added.isRowRemoved()) {
             added.setDebugThrowable(new Throwable());
         }
@@ -152,7 +152,7 @@
             }
         }
         if (isGroupChild) {
-            NotificationData.Entry existing = group.children.get(added.key);
+            NotificationEntry existing = group.children.get(added.key);
             if (existing != null && existing != added) {
                 Throwable existingThrowable = existing.getDebugThrowable();
                 Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
@@ -169,9 +169,9 @@
             group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
-                ArrayList<NotificationData.Entry> childrenCopy
+                ArrayList<NotificationEntry> childrenCopy
                         = new ArrayList<>(group.children.values());
-                for (NotificationData.Entry child : childrenCopy) {
+                for (NotificationEntry child : childrenCopy) {
                     onEntryBecomingChild(child);
                 }
                 for (OnGroupChangeListener listener : mListeners) {
@@ -181,7 +181,7 @@
         }
     }
 
-    private void onEntryBecomingChild(NotificationData.Entry entry) {
+    private void onEntryBecomingChild(NotificationEntry entry) {
         if (shouldIsolate(entry)) {
             isolateNotification(entry);
         }
@@ -221,7 +221,7 @@
         return count;
     }
 
-    private NotificationData.Entry getIsolatedChild(String groupKey) {
+    private NotificationEntry getIsolatedChild(String groupKey) {
         for (StatusBarNotification sbn : mIsolatedEntries.values()) {
             if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
                 return mGroupMap.get(sbn.getKey()).summary;
@@ -230,7 +230,7 @@
         return null;
     }
 
-    public void onEntryUpdated(NotificationData.Entry entry,
+    public void onEntryUpdated(NotificationEntry entry,
             StatusBarNotification oldNotification) {
         String oldKey = oldNotification.getGroupKey();
         String newKey = entry.notification.getGroupKey();
@@ -267,7 +267,7 @@
         if (!isOnlyChild(sbn)) {
             return false;
         }
-        NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
+        NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn);
         return logicalGroupSummary != null
                 && !logicalGroupSummary.notification.equals(sbn);
     }
@@ -343,7 +343,7 @@
      * Get the summary of a specified status bar notification. For isolated notification this return
      * itself.
      */
-    public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
+    public NotificationEntry getGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(getGroupKey(sbn));
     }
 
@@ -352,12 +352,12 @@
      * but the logical summary, i.e when a child is isolated, it still returns the summary as if
      * it wasn't isolated.
      */
-    public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
+    public NotificationEntry getLogicalGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(sbn.getGroupKey());
     }
 
     @Nullable
-    private NotificationData.Entry getGroupSummary(String groupKey) {
+    private NotificationEntry getGroupSummary(String groupKey) {
         NotificationGroup group = mGroupMap.get(groupKey);
         //TODO: see if this can become an Entry
         return group == null ? null
@@ -371,13 +371,13 @@
      * @param summary summary of a group
      * @return list of the children
      */
-    public ArrayList<NotificationData.Entry> getLogicalChildren(StatusBarNotification summary) {
+    public ArrayList<NotificationEntry> getLogicalChildren(StatusBarNotification summary) {
         NotificationGroup group = mGroupMap.get(summary.getGroupKey());
         if (group == null) {
             return null;
         }
-        ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values());
-        NotificationData.Entry isolatedChild = getIsolatedChild(summary.getGroupKey());
+        ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values());
+        NotificationEntry isolatedChild = getIsolatedChild(summary.getGroupKey());
         if (isolatedChild != null) {
             children.add(isolatedChild);
         }
@@ -443,24 +443,24 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         onAlertStateChanged(entry, isAmbient);
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         onAlertStateChanged(entry, isHeadsUp);
     }
 
-    private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting) {
+    private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting) {
         if (isAlerting) {
             if (shouldIsolate(entry)) {
                 isolateNotification(entry);
@@ -479,7 +479,7 @@
      * @return true if the entry should be isolated
      */
 
-    private boolean shouldIsolate(NotificationData.Entry entry) {
+    private boolean shouldIsolate(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
         NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
         if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
@@ -499,7 +499,7 @@
      *
      * @param entry the notification to isolate
      */
-    private void isolateNotification(NotificationData.Entry entry) {
+    private void isolateNotification(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         // We will be isolated now, so lets update the groups
@@ -523,7 +523,7 @@
      *
      * @param entry the notification to un-isolate
      */
-    private void stopIsolatingNotification(NotificationData.Entry entry) {
+    private void stopIsolatingNotification(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
         if (mIsolatedEntries.containsKey(sbn.getKey())) {
             // not isolated anymore, we need to update the groups
@@ -564,8 +564,8 @@
     }
 
     public static class NotificationGroup {
-        public final HashMap<String, NotificationData.Entry> children = new HashMap<>();
-        public NotificationData.Entry summary;
+        public final HashMap<String, NotificationEntry> children = new HashMap<>();
+        public NotificationEntry summary;
         public boolean expanded;
         /**
          * Is this notification group suppressed, i.e its summary is hidden
@@ -580,7 +580,7 @@
                             ? Log.getStackTraceString(summary.getDebugThrowable())
                             : "");
             result += "\n    children size: " + children.size();
-            for (NotificationData.Entry child : children.values()) {
+            for (NotificationEntry child : children.values()) {
                 result += "\n      " + child.notification
                 + (child.getDebugThrowable() != null
                         ? Log.getStackTraceString(child.getDebugThrowable())
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 056c8a7..077fcda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -23,9 +23,10 @@
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.tuner.TunerService;
 
@@ -36,13 +37,15 @@
  * A controller for the space in the status bar to the left of the system icons. This area is
  * normally reserved for notifications.
  */
-public class NotificationIconAreaController implements DarkReceiver {
+public class NotificationIconAreaController implements DarkReceiver,
+        StatusBarStateController.StateListener {
 
     public static final String LOW_PRIORITY = "low_priority";
 
     private final ContrastColorUtil mContrastColorUtil;
     private final NotificationEntryManager mEntryManager;
     private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
+    private final StatusBarStateController mStatusBarStateController;
     private final TunerService.Tunable mTunable = new TunerService.Tunable() {
         @Override
         public void onTuningChanged(String key, String newValue) {
@@ -86,11 +89,14 @@
     private final ViewClippingUtil.ClippingParameters mClippingParameters =
             view -> view instanceof StatusBarWindowView;
 
-    public NotificationIconAreaController(Context context, StatusBar statusBar) {
+    public NotificationIconAreaController(Context context, StatusBar statusBar,
+            StatusBarStateController statusBarStateController) {
         mStatusBar = statusBar;
         mContrastColorUtil = ContrastColorUtil.getInstance(context);
         mContext = context;
         mEntryManager = Dependency.get(NotificationEntryManager.class);
+        mStatusBarStateController = statusBarStateController;
+        mStatusBarStateController.addCallback(this);
 
         Dependency.get(TunerService.class).addTunable(mTunable, LOW_PRIORITY);
 
@@ -182,7 +188,7 @@
         return mStatusBar.getStatusBarHeight();
     }
 
-    protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
+    protected boolean shouldShowNotificationIcon(NotificationEntry entry,
             boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
             boolean hideRepliedMessages) {
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
@@ -247,7 +253,7 @@
      * @param hideDismissed should dismissed icons be hidden
      * @param hideRepliedMessages should messages that have been replied to be hidden
      */
-    private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
+    private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
             NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
             boolean hideDismissed, boolean hideRepliedMessages) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
@@ -257,7 +263,7 @@
         for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
-                NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
+                NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
                 if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
                         hideRepliedMessages)) {
                     toShow.add(function.apply(ent));
@@ -373,24 +379,6 @@
         v.setDecorColor(mIconTint);
     }
 
-    /**
-     * Dark amount, from 0 to 1, representing being awake or in AOD.
-     */
-    public void setDarkAmount(float darkAmount) {
-        mDarkAmount = darkAmount;
-        if (darkAmount == 0 || darkAmount == 1) {
-            ViewClippingUtil.setClippingDeactivated(mNotificationIcons, darkAmount != 0,
-                    mClippingParameters);
-        }
-        dozeTimeTick();
-
-        boolean fullyDark = darkAmount == 1f;
-        if (mFullyDark != fullyDark) {
-            mFullyDark = fullyDark;
-            updateShelfIcons();
-        }
-    }
-
     public void setDark(boolean dark) {
         mNotificationIcons.setDark(dark, false, 0);
         mShelfIcons.setDark(dark, false, 0);
@@ -408,10 +396,45 @@
      * Moves icons whenever the device wakes up in AOD, to avoid burn in.
      */
     public void dozeTimeTick() {
+        if (mNotificationIcons.getVisibility() != View.VISIBLE) {
+            return;
+        }
+
+        if (mDarkAmount == 0 && !mStatusBarStateController.isDozing()) {
+            mNotificationIcons.setTranslationX(0);
+            mNotificationIcons.setTranslationY(0);
+            return;
+        }
+
         int yOffset = (mKeyguardStatusBarHeight - getHeight()) / 2;
         int translationX = getBurnInOffset(mBurnInOffset, true /* xAxis */);
         int translationY = getBurnInOffset(mBurnInOffset, false /* xAxis */) + yOffset;
-        mNotificationIcons.setTranslationX(translationX * mDarkAmount);
-        mNotificationIcons.setTranslationY(translationY * mDarkAmount);
+        mNotificationIcons.setTranslationX(translationX);
+        mNotificationIcons.setTranslationY(translationY);
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        dozeTimeTick();
+    }
+
+    @Override
+    public void onDozeAmountChanged(float linear, float eased) {
+        boolean wasOrIsAwake = mDarkAmount == 0 || linear == 0;
+        boolean wasOrIsDozing = mDarkAmount == 1 || linear == 1;
+        mDarkAmount = linear;
+        if (wasOrIsAwake) {
+            ViewClippingUtil.setClippingDeactivated(mNotificationIcons, mDarkAmount != 0,
+                    mClippingParameters);
+        }
+        if (wasOrIsAwake || wasOrIsDozing) {
+            dozeTimeTick();
+        }
+
+        boolean fullyDark = mDarkAmount == 1f;
+        if (mFullyDark != fullyDark) {
+            mFullyDark = fullyDark;
+            updateShelfIcons();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 964b2210..009afca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -128,6 +128,7 @@
         }
     }.setDuration(CONTENT_FADE_DURATION);
 
+    private static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
     public static final int MAX_STATIC_ICONS = 4;
     private static final int MAX_DOTS = 1;
 
@@ -371,7 +372,8 @@
         float translationX = getActualPaddingStart();
         int firstOverflowIndex = -1;
         int childCount = getChildCount();
-        int maxVisibleIcons = mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
+        int maxVisibleIcons = mDark ? MAX_VISIBLE_ICONS_WHEN_DARK :
+                mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
         float layoutEnd = getLayoutEnd();
         float overflowStart = getMaxOverflowStart();
         mVisualOverflowStart = 0;
@@ -387,6 +389,9 @@
             boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
                     && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
             boolean noOverflowAfter = i == childCount - 1;
+            float drawingScale = mDark && view instanceof StatusBarIconView
+                    ? ((StatusBarIconView) view).getIconScaleFullyDark()
+                    : 1f;
             if (mOpenedAmount != 0.0f) {
                 noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
             }
@@ -402,7 +407,7 @@
                     mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
                 }
             }
-            translationX += iconState.iconAppearAmount * view.getWidth();
+            translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
         }
         mNumDots = 0;
         if (firstOverflowIndex != -1) {
@@ -432,6 +437,32 @@
             mFirstVisibleIconState = mIconStates.get(getChildAt(0));
         }
 
+        boolean center = mDark;
+        if (center && translationX < getLayoutEnd()) {
+            float initialTranslation =
+                    mFirstVisibleIconState == null ? 0 : mFirstVisibleIconState.xTranslation;
+
+            float contentWidth = 0;
+            if (mLastVisibleIconState != null) {
+                contentWidth = mLastVisibleIconState.xTranslation + mIconSize;
+                contentWidth = Math.min(getWidth(), contentWidth) - initialTranslation;
+            }
+            float availableSpace = getLayoutEnd() - getActualPaddingStart();
+            float delta = (availableSpace - contentWidth) / 2;
+
+            if (firstOverflowIndex != -1) {
+                // If we have an overflow, only count those half for centering because the dots
+                // don't have a lot of visual weight.
+                float deltaIgnoringOverflow = (getLayoutEnd() - mVisualOverflowStart) / 2;
+                delta = (deltaIgnoringOverflow + delta) / 2;
+            }
+            for (int i = 0; i < childCount; i++) {
+                View view = getChildAt(i);
+                IconState iconState = mIconStates.get(view);
+                iconState.xTranslation += delta;
+            }
+        }
+
         if (isLayoutRtl()) {
             for (int i = 0; i < childCount; i++) {
                 View view = getChildAt(i);
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 16576ad..0d5ebb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -50,6 +50,7 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.keyguard.KeyguardClockSwitch;
@@ -76,9 +77,9 @@
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -140,9 +141,11 @@
     private KeyguardAffordanceHelper mAffordanceHelper;
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private KeyguardStatusBarView mKeyguardStatusBar;
+    private ViewGroup mBigClockContainer;
     private QS mQs;
     private FrameLayout mQsFrame;
-    private KeyguardStatusView mKeyguardStatusView;
+    @VisibleForTesting
+    protected KeyguardStatusView mKeyguardStatusView;
     private View mQsNavbarScrim;
     protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
@@ -327,6 +330,13 @@
         mDisplayId = context.getDisplayId();
     }
 
+    /**
+     * Returns if there's a custom clock being presented.
+     */
+    public boolean hasCustomClock() {
+        return mKeyguardStatusView.hasCustomClock();
+    }
+
     private void setStatusBar(StatusBar bar) {
         mStatusBar = bar;
         mKeyguardBottomArea.setStatusBar(mStatusBar);
@@ -339,8 +349,8 @@
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
 
         KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
-        ViewGroup bigClockContainer = findViewById(R.id.big_clock_container);
-        keyguardClockSwitch.setBigClockContainer(bigClockContainer);
+        mBigClockContainer = findViewById(R.id.big_clock_container);
+        keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
 
         mNotificationContainerParent = findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
@@ -569,18 +579,23 @@
                     mKeyguardStatusView.getHeight(),
                     mInterpolatedDarkAmount,
                     mStatusBar.isKeyguardCurrentlySecure(),
-                    mPulsing,
                     mEmptyDragAmount);
             mClockPositionAlgorithm.run(mClockPositionResult);
             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
                     mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
                     mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+            // Move big clock up while pulling up the bouncer
+            PropertyAnimator.setProperty(mBigClockContainer, AnimatableProperty.Y,
+                    MathUtils.lerp(-mBigClockContainer.getHeight(), 0,
+                          Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(getExpandedFraction())),
+                    CLOCK_ANIMATION_PROPERTIES, animateClock);
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
-        mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
+        int burnInXOffset = mPulsing ? 0 : mClockPositionResult.clockX;
+        mNotificationStackScroller.setAntiBurnInOffsetX(burnInXOffset);
 
         mStackScrollerMeasuringPass++;
         requestScrollerTopPaddingUpdate(animate);
@@ -956,7 +971,7 @@
             handled = true;
         }
         handled |= super.onTouchEvent(event);
-        return mDozing ? handled : true;
+        return !mDozing || mPulsing || handled;
     }
 
     private boolean handleQsTouch(MotionEvent event) {
@@ -1233,7 +1248,7 @@
             updateDozingVisibilities(false /* animate */);
         }
 
-        resetVerticalPanelPosition();
+        resetHorizontalPanelPosition();
         updateQsState();
     }
 
@@ -2043,7 +2058,7 @@
         super.onConfigurationChanged(newConfig);
         mAffordanceHelper.onConfigurationChanged();
         if (newConfig.orientation != mLastOrientation) {
-            resetVerticalPanelPosition();
+            resetHorizontalPanelPosition();
         }
         mLastOrientation = newConfig.orientation;
     }
@@ -2489,12 +2504,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
 
         // When we're unpinning the notification via active edge they remain heads-upped,
         // we need to make sure that an animation happens in this case, otherwise the notification
@@ -2507,7 +2522,7 @@
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
     }
 
@@ -2529,7 +2544,7 @@
     @Override
     protected void onClosingFinished() {
         super.onClosingFinished();
-        resetVerticalPanelPosition();
+        resetHorizontalPanelPosition();
         setClosingWithAlphaFadeout(false);
     }
 
@@ -2546,7 +2561,7 @@
      */
     protected void updateVerticalPanelPosition(float x) {
         if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) {
-            resetVerticalPanelPosition();
+            resetHorizontalPanelPosition();
             return;
         }
         float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
@@ -2556,16 +2571,17 @@
             x = getWidth() / 2;
         }
         x = Math.min(rightMost, Math.max(leftMost, x));
-        setVerticalPanelTranslation(x -
-                (mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2));
+        float center =
+                mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
+        setHorizontalPanelTranslation(x - center);
     }
 
-    private void resetVerticalPanelPosition() {
-        setVerticalPanelTranslation(0f);
+    private void resetHorizontalPanelPosition() {
+        setHorizontalPanelTranslation(0f);
     }
 
-    protected void setVerticalPanelTranslation(float translation) {
-        mNotificationStackScroller.setVerticalPanelTranslation(translation);
+    protected void setHorizontalPanelTranslation(float translation) {
+        mNotificationStackScroller.setHorizontalPanelTranslation(translation);
         mQsFrame.setTranslationX(translation);
         int size = mVerticalTranslationListener.size();
         for (int i = 0; i < size; i++) {
@@ -2773,6 +2789,9 @@
         if (dozing == mDozing) return;
         mDozing = dozing;
         mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+        if (mDozing) {
+            mNotificationStackScroller.setShowDarkShelf(!hasCustomClock());
+        }
 
         if (mBarState == StatusBarState.KEYGUARD
                 || mBarState == StatusBarState.SHADE_LOCKED) {
@@ -2811,7 +2830,7 @@
             mAnimateNextPositionUpdate = false;
         }
         mNotificationStackScroller.setPulsing(pulsing, animatePulse);
-        mKeyguardStatusView.setPulsing(pulsing, animatePulse);
+        mKeyguardStatusView.setPulsing(pulsing);
         mKeyguardBottomArea.setPulsing(pulsing, animatePulse);
     }
 
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 021b430..f17145d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -142,8 +142,8 @@
     private boolean mIgnoreXTouchSlop;
     private boolean mExpandLatencyTracking;
     protected final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
-    protected final StatusBarStateController
-            mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+    protected final StatusBarStateController mStatusBarStateController =
+            Dependency.get(StatusBarStateController.class);
 
     protected void onExpandingFinished() {
         mBar.onExpandingFinished();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
index b18b79e..1999f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
@@ -47,6 +47,10 @@
         return mNavigationBarView.isQuickStepSwipeUpEnabled();
     }
 
+    protected boolean requiresStableTaskList() {
+        return true;
+    }
+
     @Override
     public void onGestureStart(MotionEvent event) {
         try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 0cec637..9e91ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -34,13 +34,20 @@
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.hardware.input.InputManager;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -64,7 +71,10 @@
 
     /** Experiment to swipe home button left to execute a back key press */
     private static final String HIDE_BACK_BUTTON_PROP = "quickstepcontroller_hideback";
+    private static final String ENABLE_CLICK_THROUGH_NAV_PROP = "quickstepcontroller_clickthrough";
     private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
+    private static final long CLICK_THROUGH_TAP_DELAY = 70;
+    private static final long CLICK_THROUGH_TAP_RESET_DELAY = 100;
 
     /** When the home-swipe-back gesture is disallowed, make it harder to pull */
     private static final float HORIZONTAL_GESTURE_DAMPING = 0.3f;
@@ -100,6 +110,9 @@
     private float mMinDragLimit;
     private float mDragDampeningFactor;
     private float mEdgeSwipeThreshold;
+    private boolean mClickThroughPressed;
+    private float mClickThroughPressX;
+    private float mClickThroughPressY;
 
     private NavigationGestureAction mCurrentAction;
     private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
@@ -117,6 +130,19 @@
         mOverviewEventSender = Dependency.get(OverviewProxyService.class);
     }
 
+    private final Runnable mClickThroughSendTap = new Runnable() {
+        @Override
+        public void run() {
+            sendTap(mClickThroughPressX, mClickThroughPressY);
+            mNavigationBarView.postDelayed(mClickThroughResetTap, CLICK_THROUGH_TAP_RESET_DELAY);
+        }
+    };
+
+    private final Runnable mClickThroughResetTap = () -> {
+        setWindowTouchable(true);
+        mClickThroughPressed = false;
+    };
+
     public void setComponents(NavigationBarView navigationBarView) {
         mNavigationBarView = navigationBarView;
 
@@ -320,6 +346,25 @@
             case MotionEvent.ACTION_UP:
                 if (mCurrentAction != null) {
                     mCurrentAction.endGesture();
+                } else if (action == MotionEvent.ACTION_UP
+                        && getBoolGlobalSetting(mContext, ENABLE_CLICK_THROUGH_NAV_PROP)
+                        && !mClickThroughPressed) {
+                    // Enable click through functionality where no gesture has been detected and not
+                    // passed the drag slop so inject a touch event at the same location
+                    // after making the navigation bar window untouchable. After a some time, the
+                    // navigation bar will be able to take input events again
+                    float diffX = Math.abs(event.getX() - mTouchDownX);
+                    float diffY = Math.abs(event.getY() - mTouchDownY);
+
+                    if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx()
+                            && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) {
+                        setWindowTouchable(false);
+                        mClickThroughPressX = event.getRawX();
+                        mClickThroughPressY = event.getRawY();
+                        mClickThroughPressed = true;
+                        mNavigationBarView.postDelayed(mClickThroughSendTap,
+                                CLICK_THROUGH_TAP_DELAY);
+                    }
                 }
 
                 // Return the hit target back to its original position
@@ -350,6 +395,19 @@
         return mCurrentAction != null || deadZoneConsumed;
     }
 
+    private void setWindowTouchable(boolean flag) {
+        final WindowManager.LayoutParams lp = (WindowManager.LayoutParams)
+                ((ViewGroup) mNavigationBarView.getParent()).getLayoutParams();
+        if (flag) {
+            lp.flags &= ~LayoutParams.FLAG_NOT_TOUCHABLE;
+        } else {
+            lp.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
+        }
+        final WindowManager wm = (WindowManager) mNavigationBarView.getContext()
+                .getSystemService(Context.WINDOW_SERVICE);
+        wm.updateViewLayout((View) mNavigationBarView.getParent(), lp);
+    }
+
     private boolean isEdgeSwipeAlongNavBar(int touchDown, boolean dragPositiveDirection) {
         // Detect edge swipe from side of 0 -> threshold
         if (dragPositiveDirection) {
@@ -562,6 +620,38 @@
         return false;
     }
 
+    private void sendTap(float x, float y) {
+        long now = SystemClock.uptimeMillis();
+        injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
+        injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_UP, now, x, y, 0.0f);
+    }
+
+    private int getInputDeviceId(int inputSource) {
+        int[] devIds = InputDevice.getDeviceIds();
+        for (int devId : devIds) {
+            InputDevice inputDev = InputDevice.getDevice(devId);
+            if (inputDev.supportsSource(inputSource)) {
+                return devId;
+            }
+        }
+        return 0;
+    }
+
+    private void injectMotionEvent(int inputSource, int action, long when, float x, float y,
+            float pressure) {
+        final float defaultSize = 1.0f;
+        final int defaultMetaState = 0;
+        final float defaultPrecisionX = 1.0f;
+        final float defaultPrecisionY = 1.0f;
+        final int defaultEdgeFlags = 0;
+        MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, defaultSize,
+                defaultMetaState, defaultPrecisionX, defaultPrecisionY,
+                getInputDeviceId(inputSource), defaultEdgeFlags);
+        event.setSource(inputSource);
+        InputManager.getInstance().injectInputEvent(event,
+                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+    }
+
     private boolean proxyMotionEvents(MotionEvent event) {
         final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
         event.transform(mTransformGlobalMatrix);
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 e25c829..bf143c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -88,16 +88,12 @@
     /**
      * Default alpha value for most scrims.
      */
-    public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
+    public static final float GRADIENT_SCRIM_ALPHA = 0.2f;
     /**
      * A scrim varies its opacity based on a busyness factor, for example
      * how many notifications are currently visible.
      */
     public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f;
-    /**
-     * Scrim opacity when a wallpaper doesn't support ambient mode.
-     */
-    public static final float PULSING_WALLPAPER_SCRIM_ALPHA = 0.6f;
 
     /**
      * The most common scrim, the one under the keyguard.
@@ -154,7 +150,6 @@
     private Callback mCallback;
     private boolean mWallpaperSupportsAmbientMode;
     private boolean mScreenOn;
-    private float mNotificationDensity;
 
     // Scrim blanking callbacks
     private Runnable mPendingFrameCallback;
@@ -245,7 +240,7 @@
         mCurrentInFrontTint = state.getFrontTint();
         mCurrentBehindTint = state.getBehindTint();
         mCurrentInFrontAlpha = state.getFrontAlpha();
-        mCurrentBehindAlpha = state.getBehindAlpha(mNotificationDensity);
+        mCurrentBehindAlpha = state.getBehindAlpha();
         applyExpansionToAlpha();
 
         // Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
@@ -275,9 +270,11 @@
             holdWakeLock();
         }
 
-        // AOD wallpapers should fade away after a while
-        if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
-                && mState == ScrimState.AOD) {
+        // AOD wallpapers should fade away after a while.
+        // Docking pulses may take a long time, wallpapers should also fade away after a while.
+        if (mWallpaperSupportsAmbientMode && (
+                mDozeParameters.getAlwaysOn() && mState == ScrimState.AOD
+                        || mState == ScrimState.PULSING && mCallback != null)) {
             if (!mWallpaperVisibilityTimedOut) {
                 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
                         AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
@@ -329,7 +326,7 @@
 
     @VisibleForTesting
     protected void onHideWallpaperTimeout() {
-        if (mState != ScrimState.AOD) {
+        if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
             return;
         }
 
@@ -364,7 +361,7 @@
             mExpansionFraction = fraction;
 
             final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
-                    || mState == ScrimState.KEYGUARD;
+                    || mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING;
             if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
                 return;
             }
@@ -409,11 +406,11 @@
             behindFraction = (float) Math.pow(behindFraction, 0.8f);
             mCurrentBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY;
             mCurrentInFrontAlpha = 0;
-        } else if (mState == ScrimState.KEYGUARD) {
+        } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) {
             // Either darken of make the scrim transparent when you
             // pull down the shade
             float interpolatedFract = getInterpolatedFraction();
-            float alphaBehind = mState.getBehindAlpha(mNotificationDensity);
+            float alphaBehind = mState.getBehindAlpha();
             if (mDarkenWhileDragging) {
                 mCurrentBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind,
                         interpolatedFract);
@@ -427,24 +424,6 @@
     }
 
     /**
-     * Keyguard and shade scrim opacity varies according to how many notifications are visible.
-     * @param notificationCount Number of visible notifications.
-     */
-    public void setNotificationCount(int notificationCount) {
-        final float maxNotificationDensity = 3;
-        float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f);
-        if (mNotificationDensity == notificationDensity) {
-            return;
-        }
-        mNotificationDensity = notificationDensity;
-
-        if (mState == ScrimState.KEYGUARD) {
-            applyExpansionToAlpha();
-            scheduleUpdate();
-        }
-    }
-
-    /**
      * Sets the given drawable as the background of the scrim that shows up behind the
      * notifications.
      */
@@ -504,7 +483,8 @@
 
         // We want to override the back scrim opacity for the AOD state
         // when it's time to fade the wallpaper away.
-        boolean aodWallpaperTimeout = mState == ScrimState.AOD && mWallpaperVisibilityTimedOut;
+        boolean aodWallpaperTimeout = (mState == ScrimState.AOD || mState == ScrimState.PULSING)
+                && mWallpaperVisibilityTimedOut;
         // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
         boolean occludedKeyguard = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
                 && mKeyguardOccluded;
@@ -562,8 +542,8 @@
         if (alpha == 0f) {
             scrim.setClickable(false);
         } else {
-            // Eat touch events (unless dozing or pulsing).
-            scrim.setClickable(mState != ScrimState.AOD && mState != ScrimState.PULSING);
+            // Eat touch events (unless dozing).
+            scrim.setClickable(mState != ScrimState.AOD);
         }
         updateScrim(scrim, alpha);
     }
@@ -608,9 +588,11 @@
         anim.setStartDelay(mAnimationDelay);
         anim.setDuration(mAnimationDuration);
         anim.addListener(new AnimatorListenerAdapter() {
+            private Callback lastCallback = mCallback;
+
             @Override
             public void onAnimationEnd(Animator animation) {
-                onFinished();
+                onFinished(lastCallback);
 
                 scrim.setTag(TAG_KEY_ANIM, null);
                 dispatchScrimsVisible();
@@ -668,14 +650,23 @@
     }
 
     private void onFinished() {
+        onFinished(mCallback);
+    }
+
+    private void onFinished(Callback callback) {
         if (mWakeLockHeld) {
             mWakeLock.release();
             mWakeLockHeld = false;
         }
-        if (mCallback != null) {
-            mCallback.onFinished();
-            mCallback = null;
+
+        if (callback != null) {
+            callback.onFinished();
+
+            if (callback == mCallback) {
+                mCallback = null;
+            }
         }
+
         // When unlocking with fingerprint, we'll fade the scrims from black to transparent.
         // At the end of the animation we need to remove the tint.
         if (mState == ScrimState.UNLOCKED) {
@@ -894,7 +885,7 @@
         // Backdrop event may arrive after state was already applied,
         // in this case, back-scrim needs to be re-evaluated
         if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
-            float newBehindAlpha = mState.getBehindAlpha(mNotificationDensity);
+            float newBehindAlpha = mState.getBehindAlpha();
             if (mCurrentBehindAlpha != newBehindAlpha) {
                 mCurrentBehindAlpha = newBehindAlpha;
                 updateScrims();
@@ -908,6 +899,10 @@
         }
     }
 
+    public void setPulseReason(int pulseReason) {
+        ScrimState.PULSING.setPulseReason(pulseReason);
+    }
+
     public interface Callback {
         default void onStart() {
         }
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 fb3c4aa..11a2d32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -18,8 +18,8 @@
 
 import android.graphics.Color;
 import android.os.Trace;
-import android.util.MathUtils;
 
+import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
@@ -57,13 +57,6 @@
             mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
             mCurrentInFrontAlpha = 0;
         }
-
-        @Override
-        public float getBehindAlpha(float busynessFactor) {
-            return MathUtils.map(0 /* start */, 1 /* stop */,
-                    mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
-                    busynessFactor);
-        }
     },
 
     /**
@@ -117,7 +110,7 @@
         }
 
         @Override
-        public float getBehindAlpha(float busyness) {
+        public float getBehindAlpha() {
             return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
         }
 
@@ -133,17 +126,17 @@
     PULSING(5) {
         @Override
         public void prepare(ScrimState previousState) {
-            mCurrentInFrontAlpha = 0;
-            mCurrentInFrontTint = Color.BLACK;
-            mCurrentBehindTint = Color.BLACK;
+            mCurrentInFrontAlpha = 0f;
+            if (mPulseReason == DozeLog.PULSE_REASON_NOTIFICATION
+                    || mPulseReason == DozeLog.PULSE_REASON_DOCKING) {
+                mCurrentBehindAlpha = previousState.getBehindAlpha();
+                mCurrentBehindTint = Color.BLACK;
+            } else {
+                mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
+                mCurrentBehindTint = Color.TRANSPARENT;
+            }
             mBlankScreen = mDisplayRequiresBlanking;
         }
-
-        @Override
-        public float getBehindAlpha(float busyness) {
-            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f
-                    : ScrimController.PULSING_WALLPAPER_SCRIM_ALPHA;
-        }
     },
 
     /**
@@ -204,6 +197,7 @@
     int mIndex;
     boolean mHasBackdrop;
     boolean mLaunchingAffordanceWithPreview;
+    int mPulseReason;
 
     ScrimState(int index) {
         mIndex = index;
@@ -219,6 +213,14 @@
     public void prepare(ScrimState previousState) {
     }
 
+    /**
+     * Check if lockscreen wallpaper or music album art exists.
+     * @return true if lockscreen wallpaper or music album art exists.
+     */
+    public boolean hasBackdrop() {
+        return mHasBackdrop;
+    }
+
     public int getIndex() {
         return mIndex;
     }
@@ -227,7 +229,7 @@
         return mCurrentInFrontAlpha;
     }
 
-    public float getBehindAlpha(float busyness) {
+    public float getBehindAlpha() {
         return mCurrentBehindAlpha;
     }
 
@@ -268,6 +270,10 @@
         mAodFrontScrimAlpha = aodFrontScrimAlpha;
     }
 
+    public void setPulseReason(int pulseReason) {
+        mPulseReason = pulseReason;
+    }
+
     public void setScrimBehindAlphaKeyguard(float scrimBehindAlphaKeyguard) {
         mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
     }
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 977e336..1470d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -70,6 +70,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -98,6 +99,7 @@
 import android.service.notification.StatusBarNotification;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
@@ -192,12 +194,12 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationClicker;
-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.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationListController;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -386,6 +388,7 @@
     private NotificationGutsManager mGutsManager;
     protected NotificationLogger mNotificationLogger;
     protected NotificationEntryManager mEntryManager;
+    private NotificationListController mNotificationListController;
     private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private NotificationRowBinder mNotificationRowBinder;
     protected NotificationViewHierarchyManager mViewHierarchyManager;
@@ -460,13 +463,6 @@
     private NotificationMediaManager mMediaManager;
     protected NotificationLockscreenUserManager mLockscreenUserManager;
     protected NotificationRemoteInputManager mRemoteInputManager;
-    protected BubbleController mBubbleController;
-    private final BubbleController.BubbleExpandListener mBubbleExpandListener =
-            (isExpanding, amount) -> {
-                if (amount == 1) {
-                    updateScrimController();
-                }
-            };
 
     private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
         @Override
@@ -479,8 +475,13 @@
             WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
             final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
                     com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+            final boolean aodImageWallpaperEnabled = FeatureFlagUtils.isEnabled(mContext,
+                    FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED);
+            updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled);
+            // If WallpaperInfo is null, it must be ImageWallpaper.
             final boolean supportsAmbientMode = deviceSupportsAodWallpaper
-                    && info != null && info.supportsAmbientMode();
+                    && (info == null && aodImageWallpaperEnabled
+                        || info != null && info.supportsAmbientMode());
 
             mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
             mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -489,7 +490,7 @@
 
     private Runnable mLaunchTransitionEndRunnable;
     protected boolean mLaunchTransitionFadingAway;
-    private NotificationData.Entry mDraggedDownEntry;
+    private NotificationEntry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
@@ -582,12 +583,19 @@
     protected NotificationPresenter mPresenter;
     private NotificationActivityStarter mNotificationActivityStarter;
     private boolean mPulsing;
+    private ContentObserver mFeatureFlagObserver;
+    protected BubbleController mBubbleController;
+    private final BubbleController.BubbleExpandListener mBubbleExpandListener =
+            (isExpanding, key) -> {
+                mEntryManager.updateNotifications();
+                updateScrimController();
+            };
 
     @Override
     public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
         mForegroundServiceController.onAppOpChanged(code, uid, packageName, active);
         Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
-            mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
+            mNotificationListController.updateNotificationsForAppOp(code, uid, packageName, active);
         });
     }
 
@@ -633,7 +641,7 @@
         mBubbleController.setExpandListener(mBubbleExpandListener);
         KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
         if (sliceProvider != null) {
-            sliceProvider.initDependencies();
+            sliceProvider.initDependencies(mMediaManager, mStatusBarStateController);
         } else {
             Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
         }
@@ -698,6 +706,9 @@
         mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
                 wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
         mWallpaperChangedReceiver.onReceive(mContext, null);
+        mFeatureFlagObserver = new FeatureFlagObserver(
+                FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED /* feature */,
+                () -> mWallpaperChangedReceiver.onReceive(mContext, null) /* callback */);
 
         // Set up the initial notification state. This needs to happen before CommandQueue.disable()
         setUpPresenter();
@@ -790,7 +801,7 @@
         mNotificationLogger.setUpWithContainer(notifListContainer);
 
         mNotificationIconAreaController = SystemUIFactory.getInstance()
-                .createNotificationIconAreaController(context, this);
+                .createNotificationIconAreaController(context, this, mStatusBarStateController);
         inflateShelf();
         mNotificationIconAreaController.setupShelf(mNotificationShelf);
 
@@ -1035,6 +1046,13 @@
                 mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager,
                 mNotificationAlertingManager);
 
+        mNotificationListController =
+                new NotificationListController(
+                        mEntryManager,
+                        (NotificationListContainer) mStackScroller,
+                        mForegroundServiceController,
+                        mDeviceProvisionedController);
+
         mAppOpsController.addCallback(APP_OPS, this);
         mNotificationListener.setUpWithPresenter(mPresenter);
         mNotificationShelf.setOnActivatedListener(mPresenter);
@@ -1047,6 +1065,7 @@
                 this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
 
         mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
+        mNotificationListController.bind();
     }
 
     /**
@@ -1510,21 +1529,21 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         dismissVolumeDialog();
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mEntryManager.updateNotificationRanking(null /* rankingMap */);
     }
 
     @Override
-    public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         mEntryManager.updateNotificationRanking(null);
         if (isAmbient) {
             mDozeServiceHost.fireNotificationPulse();
@@ -2551,11 +2570,11 @@
     };
 
     public void resetUserExpandedStates() {
-        ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData()
+        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
         final int notificationCount = activeNotifications.size();
         for (int i = 0; i < notificationCount; i++) {
-            NotificationData.Entry entry = activeNotifications.get(i);
+            NotificationEntry entry = activeNotifications.get(i);
             entry.resetUserExpansion();
         }
     }
@@ -2822,7 +2841,7 @@
         } catch (RemoteException e) {
             // Ignore.
         }
-        mEntryManager.destroy();
+        mNotificationListController.destroy();
         // End old BaseStatusBar.destroy().
         if (mStatusBarWindow != null) {
             mWindowManager.removeViewImmediate(mStatusBarWindow);
@@ -3506,7 +3525,7 @@
 
         int userId = mLockscreenUserManager.getCurrentUserId();
         ExpandableNotificationRow row = null;
-        NotificationData.Entry entry = null;
+        NotificationEntry entry = null;
         if (expandView instanceof ExpandableNotificationRow) {
             entry = ((ExpandableNotificationRow) expandView).getEntry();
             entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
@@ -3574,10 +3593,7 @@
             mVisualStabilityManager.setScreenOn(false);
             updateVisibleToUser();
 
-            // We need to disable touch events because these might
-            // collapse the panel after we expanded it, and thus we would end up with a blank
-            // Keyguard.
-            mNotificationPanel.setTouchAndAnimationDisabled(true);
+            updateNotificationPanelTouchState();
             mStatusBarWindow.cancelCurrentTouch();
             if (mLaunchCameraOnFinishedGoingToSleep) {
                 mLaunchCameraOnFinishedGoingToSleep = false;
@@ -3600,13 +3616,22 @@
             mDeviceInteractive = true;
             mAmbientPulseManager.releaseAllImmediately();
             mVisualStabilityManager.setScreenOn(true);
-            mNotificationPanel.setTouchAndAnimationDisabled(false);
+            updateNotificationPanelTouchState();
             updateVisibleToUser();
             updateIsKeyguard();
             mDozeServiceHost.stopDozing();
         }
     };
 
+    /**
+     * We need to disable touch events because these might
+     * collapse the panel after we expanded it, and thus we would end up with a blank
+     * Keyguard.
+     */
+    private void updateNotificationPanelTouchState() {
+        mNotificationPanel.setTouchAndAnimationDisabled(!mDeviceInteractive && !mPulsing);
+    }
+
     final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
         @Override
         public void onScreenTurningOn() {
@@ -3858,6 +3883,7 @@
 
         @Override
         public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
+            mScrimController.setPulseReason(reason);
             if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
                 mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
                 startAssist(new Bundle());
@@ -3872,17 +3898,15 @@
                 @Override
                 public void onPulseStarted() {
                     callback.onPulseStarted();
-                    if (mAmbientPulseManager.hasNotifications()) {
-                        // Only pulse the stack scroller if there's actually something to show.
-                        // Otherwise just show the always-on screen.
-                        setPulsing(true);
-                    }
+                    updateNotificationPanelTouchState();
+                    setPulsing(true);
                 }
 
                 @Override
                 public void onPulseFinished() {
                     mPulsing = false;
                     callback.onPulseFinished();
+                    updateNotificationPanelTouchState();
                     setPulsing(false);
                 }
 
@@ -3986,7 +4010,7 @@
         }
 
         @Override
-        public void onDoubleTap(float screenX, float screenY) {
+        public void onSlpiTap(float screenX, float screenY) {
             if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
                 && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
                 mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
@@ -4412,4 +4436,33 @@
     public @TransitionMode int getStatusBarMode() {
         return mStatusBarMode;
     }
+
+    private void updateAodMaskVisibility(boolean supportsAodWallpaper) {
+        View mask = mStatusBarWindow.findViewById(R.id.aod_mask);
+        if (mask != null) {
+            mask.setVisibility(supportsAodWallpaper ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    private final class FeatureFlagObserver extends ContentObserver {
+        private final Runnable mCallback;
+
+        FeatureFlagObserver(String feature, Runnable callback) {
+            this(null, feature, callback);
+        }
+
+        private FeatureFlagObserver(Handler handler, String feature, Runnable callback) {
+            super(handler);
+            mCallback = callback;
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(feature), false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (mCallback != null) {
+                mStatusBarWindow.post(mCallback);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index db7589d..f846036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -36,7 +36,6 @@
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.IconLogger;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -61,7 +60,6 @@
 
     private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
     private final ArraySet<String> mIconBlacklist = new ArraySet<>();
-    private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
 
     // Points to light or dark context depending on the... context?
     private Context mContext;
@@ -147,7 +145,6 @@
         int viewIndex = getViewIndex(index, holder.getTag());
         boolean blocked = mIconBlacklist.contains(slot);
 
-        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
         mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
     }
 
@@ -281,8 +278,6 @@
             return;
         }
 
-        mIconLogger.onIconHidden(slotName);
-
         int slotIndex = getSlotIndex(slotName);
         List<StatusBarIconHolder> iconsToRemove = slot.getHolderListInViewOrder();
         for (StatusBarIconHolder holder : iconsToRemove) {
@@ -297,7 +292,6 @@
         if (getIcon(index, tag) == null) {
             return;
         }
-        mIconLogger.onIconHidden(getSlotName(index));
         super.removeIcon(index, tag);
         int viewIndex = getViewIndex(index, 0);
         mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
@@ -305,7 +299,6 @@
 
     private void handleSet(int index, StatusBarIconHolder holder) {
         int viewIndex = getViewIndex(index, holder.getTag());
-        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
         mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
     }
 
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 0f8970f..bb23608 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -187,7 +187,7 @@
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
         } else if (bouncerNeedsScrimming()) {
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
-        } else if (mShowing && !mDozing) {
+        } else if (mShowing) {
             if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
                 mBouncer.setExpansion(expansion);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 8d1b911..4f61009 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -60,10 +60,10 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -131,7 +131,7 @@
 
         mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onPendingEntryAdded(NotificationData.Entry entry) {
+            public void onPendingEntryAdded(NotificationEntry entry) {
                 handleFullScreenIntent(entry);
             }
         });
@@ -267,7 +267,7 @@
             }
         }
         Intent fillInIntent = null;
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         CharSequence remoteInputText = null;
         if (!TextUtils.isEmpty(entry.remoteInputText)) {
             remoteInputText = entry.remoteInputText;
@@ -345,7 +345,7 @@
         }, null, false /* afterKeyguardGone */);
     }
 
-    private void handleFullScreenIntent(NotificationData.Entry entry) {
+    private void handleFullScreenIntent(NotificationEntry entry) {
         boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
         if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
             if (shouldSuppressFullScreenIntent(entry)) {
@@ -413,7 +413,7 @@
                 || !mActivityLaunchAnimator.isAnimationPending();
     }
 
-    private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
+    private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
         if (mPresenter.isDeviceInVrMode()) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index b9372e8..df7f53b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -60,12 +60,12 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -104,6 +104,8 @@
             Dependency.get(NotificationMediaManager.class);
     private final VisualStabilityManager mVisualStabilityManager =
             Dependency.get(VisualStabilityManager.class);
+    private final NotificationGutsManager mGutsManager =
+            Dependency.get(NotificationGutsManager.class);
     protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
 
     private final NotificationPanelView mNotificationPanel;
@@ -183,41 +185,35 @@
         Dependency.get(InitController.class).addPostInitTask(() -> {
             NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
                 @Override
-                public void onNotificationAdded(Entry entry) {
+                public void onNotificationAdded(NotificationEntry entry) {
                     // Recalculate the position of the sliding windows and the titles.
                     mShadeController.updateAreThereNotifications();
                 }
 
                 @Override
-                public void onEntryUpdated(Entry entry) {
+                public void onPostEntryUpdated(NotificationEntry entry) {
                     mShadeController.updateAreThereNotifications();
                 }
 
                 @Override
                 public void onEntryRemoved(
-                        @Nullable Entry entry,
-                        String key,
-                        StatusBarNotification old,
+                        @Nullable NotificationEntry entry,
                         NotificationVisibility visibility,
-                        boolean lifetimeExtended,
                         boolean removedByUser) {
-                    if (!lifetimeExtended) {
-                        StatusBarNotificationPresenter.this.onNotificationRemoved(key, old);
-                    }
+                    StatusBarNotificationPresenter.this.onNotificationRemoved(
+                            entry.key, entry.notification);
                     if (removedByUser) {
                         maybeEndAmbientPulse();
                     }
                 }
             };
 
-            NotificationGutsManager gutsManager = Dependency.get(NotificationGutsManager.class);
-
             mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
             mEntryManager.setUpWithPresenter(this, notifListContainer, mHeadsUpManager);
             mEntryManager.addNotificationEntryListener(notificationEntryListener);
             mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
             mEntryManager.addNotificationLifetimeExtender(mAmbientPulseManager);
-            mEntryManager.addNotificationLifetimeExtender(gutsManager);
+            mEntryManager.addNotificationLifetimeExtender(mGutsManager);
             mEntryManager.addNotificationLifetimeExtenders(
                     remoteInputManager.getLifetimeExtenders());
             mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
@@ -227,7 +223,7 @@
             mLockscreenUserManager.setUpWithPresenter(this);
             mMediaManager.setUpWithPresenter(this);
             mVisualStabilityManager.setUpWithPresenter(this);
-            gutsManager.setUpWithPresenter(this,
+            mGutsManager.setUpWithPresenter(this,
                     notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
             // ForegroundServiceControllerListener adds its listener in its constructor
             // but we need to request it here in order for it to be instantiated.
@@ -246,7 +242,7 @@
         MessagingMessage.dropCache();
         MessagingGroup.dropCache();
         if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
-            mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
+            updateNotificationsOnDensityOrFontScaleChanged();
         } else {
             mReinflateNotificationsOnUserSwitched = true;
         }
@@ -262,10 +258,10 @@
     }
 
     private void updateNotificationOnUiModeChanged() {
-        ArrayList<Entry> userNotifications
+        ArrayList<NotificationEntry> userNotifications
                 = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
-            Entry entry = userNotifications.get(i);
+            NotificationEntry entry = userNotifications.get(i);
             ExpandableNotificationRow row = entry.getRow();
             if (row != null) {
                 row.onUiModeChanged();
@@ -273,6 +269,19 @@
         }
     }
 
+    private void updateNotificationsOnDensityOrFontScaleChanged() {
+        ArrayList<NotificationEntry> userNotifications =
+                mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+        for (int i = 0; i < userNotifications.size(); i++) {
+            NotificationEntry entry = userNotifications.get(i);
+            entry.onDensityOrFontScaleChanged();
+            boolean exposedGuts = entry.areGutsExposed();
+            if (exposedGuts) {
+                mGutsManager.onDensityOrFontScaleChanged(entry);
+            }
+        }
+    }
+
     @Override
     public boolean isCollapsing() {
         return mNotificationPanel.isCollapsing()
@@ -327,7 +336,7 @@
         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
     }
 
-    public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+    public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
         if (mShadeController.isDozing()) {
             return false;
         }
@@ -371,7 +380,7 @@
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
         mCommandQueue.animateCollapsePanels();
         if (mReinflateNotificationsOnUserSwitched) {
-            mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
+            updateNotificationsOnDensityOrFontScaleChanged();
             mReinflateNotificationsOnUserSwitched = false;
         }
         if (mDispatchUiModeChangeOnUserSwitched) {
@@ -385,7 +394,7 @@
     }
 
     @Override
-    public void onBindRow(Entry entry, PackageManager pmUser,
+    public void onBindRow(NotificationEntry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
         row.setAboveShelfChangedListener(mAboveShelfObserver);
         row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer);
@@ -443,7 +452,7 @@
     }
 
     @Override
-    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
+    public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) {
         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
             mShadeController.goToLockedShade(clickedEntry.getRow());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 3ddfc0c..8d58410 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -53,7 +53,8 @@
 /**
  */
 @Singleton
-public class StatusBarRemoteInputCallback implements Callback, Callbacks {
+public class StatusBarRemoteInputCallback implements Callback, Callbacks,
+        StatusBarStateController.StateListener {
 
     private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
     private final StatusBarStateController mStatusBarStateController
@@ -63,7 +64,6 @@
     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;
@@ -78,13 +78,14 @@
         mContext = context;
         mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
                 new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
-        mStatusBarStateController.addCallback(mStateListener);
+        mStatusBarStateController.addCallback(this);
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
         mCommandQueue = getComponent(context, CommandQueue.class);
         mCommandQueue.addCallback(this);
     }
 
-    private void setStatusBarState(int state) {
+    @Override
+    public void onStateChanged(int state) {
         if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) {
             if (!mStatusBarStateController.isKeyguardRequested()) {
                 if (mPendingRemoteInputView != null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 53e461d..8b25c34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -339,7 +339,7 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
-        if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) {
+        if (mService.isDozing() && !mService.isPulsing()) {
             // Capture all touch events in always-on.
             return true;
         }
@@ -347,8 +347,7 @@
         if (mNotificationPanel.isFullyExpanded()
                 && stackScrollLayout.getVisibility() == View.VISIBLE
                 && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
-                && !mService.isBouncerShowing()
-                && !mService.isDozing()) {
+                && !mService.isBouncerShowing()) {
             intercept = mDragDownHelper.onInterceptTouchEvent(ev);
         }
         if (!intercept) {
@@ -369,7 +368,7 @@
         boolean handled = false;
         if (mService.isDozing()) {
             mDoubleTapHelper.onTouchEvent(ev);
-            handled = true;
+            handled = !mService.isPulsing();
         }
         if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled)
                 || mDragDownHelper.isDraggingDown()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index f65f826..5e94152 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -48,18 +48,36 @@
     }
 
     /**
-     * A listener that will be notified whenever a change in battery level or power save mode
-     * has occurred.
+     * A listener that will be notified whenever a change in battery level or power save mode has
+     * occurred.
      */
     interface BatteryStateChangeCallback {
-        default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {}
-        default void onPowerSaveChanged(boolean isPowerSave) {}
+
+        default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        }
+
+        default void onPowerSaveChanged(boolean isPowerSave) {
+        }
     }
 
     /**
-     * If available, get the estimated battery time remaining as a string
+     * If available, get the estimated battery time remaining as a string.
+     *
+     * @param completion A lambda that will be called with the result of fetching the estimate. The
+     * first time this method is called may need to be dispatched to a background thread. The
+     * completion is called on the main thread
      */
-    default String getEstimatedTimeRemainingString() {
-        return null;
+    default void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {}
+
+    /**
+     * Callback called when the estimated time remaining text is fetched.
+     */
+    public interface EstimateFetchCompletion {
+
+        /**
+         * The callback
+         * @param estimate the estimate
+         */
+        void onBatteryRemainingEstimateRetrieved(String estimate);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 6190c8f..af3c96f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -27,9 +27,12 @@
 import android.os.PowerSaveState;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.settingslib.utils.PowerUtil;
+import com.android.systemui.Dependency;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.Estimate;
 
@@ -56,6 +59,7 @@
 
     private final EnhancedEstimates mEstimates;
     private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+    private final ArrayList<EstimateFetchCompletion> mFetchCallbacks = new ArrayList<>();
     private final PowerManager mPowerManager;
     private final Handler mHandler;
     private final Context mContext;
@@ -70,6 +74,7 @@
     private boolean mHasReceivedBattery = false;
     private Estimate mEstimate;
     private long mLastEstimateTimestamp = -1;
+    private boolean mFetchingEstimate = false;
 
     @Inject
     public BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates) {
@@ -197,20 +202,61 @@
     }
 
     @Override
-    public String getEstimatedTimeRemainingString() {
-        if (mEstimate == null
-                || System.currentTimeMillis() > mLastEstimateTimestamp + UPDATE_GRANULARITY_MSEC) {
-            updateEstimate();
+    public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
+        if (mEstimate != null
+                && mLastEstimateTimestamp > System.currentTimeMillis() - UPDATE_GRANULARITY_MSEC) {
+            String percentage = generateTimeRemainingString();
+            completion.onBatteryRemainingEstimateRetrieved(percentage);
+            return;
         }
-        // Estimates may not exist yet even if we've checked
+
+        // Need to fetch or refresh the estimate, but it may involve binder calls so offload the
+        // work
+        synchronized (mFetchCallbacks) {
+            mFetchCallbacks.add(completion);
+        }
+        updateEstimateInBackground();
+    }
+
+    @Nullable
+    private String generateTimeRemainingString() {
         if (mEstimate == null) {
             return null;
         }
-        final String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
+
+        String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
         return PowerUtil.getBatteryRemainingShortStringFormatted(
                 mContext, mEstimate.estimateMillis);
     }
 
+    private void updateEstimateInBackground() {
+        if (mFetchingEstimate) {
+            // Already dispatched a fetch. It will notify all listeners when finished
+            return;
+        }
+
+        mFetchingEstimate = true;
+        Dependency.get(Dependency.BG_HANDLER).post(() -> {
+            mEstimate = mEstimates.getEstimate();
+            mLastEstimateTimestamp = System.currentTimeMillis();
+            mFetchingEstimate = false;
+
+            Dependency.get(Dependency.MAIN_HANDLER).post(this::notifyEstimateFetchCallbacks);
+        });
+    }
+
+    private void notifyEstimateFetchCallbacks() {
+        String estimate = generateTimeRemainingString();
+
+        synchronized (mFetchCallbacks) {
+            for (EstimateFetchCompletion completion : mFetchCallbacks) {
+                completion.onBatteryRemainingEstimateRetrieved(estimate);
+            }
+
+            mFetchCallbacks.clear();
+        }
+    }
+
     private void updateEstimate() {
         mEstimate = mEstimates.getEstimate();
         mLastEstimateTimestamp = System.currentTimeMillis();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index de7ef3b..4299af7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -274,7 +274,6 @@
 
     private void updateClockVisibility() {
         boolean visible = shouldBeVisible();
-        Dependency.get(IconLogger.class).onIconVisibility("clock", visible);
         int visibility = visible ? View.VISIBLE : View.GONE;
         super.setVisibility(visibility);
     }
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 a02c9d5..fd3f680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -30,7 +30,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.io.FileDescriptor;
@@ -108,11 +108,11 @@
         }
     }
 
-    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
+    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
         return hasFullScreenIntent(entry);
     }
 
-    protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
+    protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
         return entry.notification.getNotification().fullScreenIntent != null;
     }
 
@@ -121,7 +121,7 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setEntryPinned: " + isPinned);
         }
-        NotificationData.Entry entry = headsUpEntry.mEntry;
+        NotificationEntry entry = headsUpEntry.mEntry;
         if (entry.isRowPinned() != isPinned) {
             entry.setRowPinned(isPinned);
             updatePinnedMode();
@@ -141,7 +141,7 @@
 
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setHeadsUp(true);
         setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
         for (OnHeadsUpChangedListener listener : mListeners) {
@@ -151,7 +151,7 @@
 
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setHeadsUp(false);
         setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
@@ -222,7 +222,7 @@
      * Returns the top Heads Up Notification, which appears to show at first.
      */
     @Nullable
-    public NotificationData.Entry getTopEntry() {
+    public NotificationEntry getTopEntry() {
         HeadsUpEntry topEntry = getTopHeadsUpEntry();
         return (topEntry != null) ? topEntry.mEntry : null;
     }
@@ -323,7 +323,7 @@
      * @return -1 if the first argument should be ranked higher than the second, 1 if the second
      * one should be ranked higher and 0 if they are equal.
      */
-    public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) {
+    public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) {
         AlertEntry aEntry = getHeadsUpEntry(a.key);
         AlertEntry bEntry = getHeadsUpEntry(b.key);
         if (aEntry == null || bEntry == null) {
@@ -336,7 +336,7 @@
      * Set an entry to be expanded and therefore stick in the heads up area if it's pinned
      * until it's collapsed again.
      */
-    public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
+    public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
         if (headsUpEntry != null && entry.isRowPinned()) {
             headsUpEntry.setExpanded(expanded);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLogger.java
deleted file mode 100644
index 710e1df..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLogger.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-public interface IconLogger {
-
-    void onIconShown(String tag);
-    void onIconHidden(String tag);
-
-    default void onIconVisibility(String tag, boolean visible) {
-        if (visible) {
-            onIconShown(tag);
-        } else {
-            onIconHidden(tag);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
deleted file mode 100644
index ba6369e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_NUM_STATUS_ICONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_ICONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.STATUS_BAR_ICONS_CHANGED;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
-import static com.android.systemui.Dependency.BG_LOOPER_NAME;
-
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.ArraySet;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.logging.MetricsLogger;
-
-import java.util.Arrays;
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-public class IconLoggerImpl implements IconLogger {
-
-    // Minimum ms between log statements.
-    // NonFinalForTesting
-    @VisibleForTesting
-    protected static long MIN_LOG_INTERVAL = 1000;
-
-    private final Context mContext;
-    private final Handler mHandler;
-    private final MetricsLogger mLogger;
-    private final ArraySet<String> mIcons = new ArraySet<>();
-    private final List<String> mIconIndex;
-    private long mLastLog = System.currentTimeMillis();
-
-    @Inject
-    public IconLoggerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
-            MetricsLogger logger) {
-        mContext = context;
-        mHandler = new Handler(bgLooper);
-        mLogger = logger;
-        String[] icons = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_statusBarIcons);
-        mIconIndex = Arrays.asList(icons);
-        doLog();
-    }
-
-    @Override
-    public void onIconShown(String tag) {
-        synchronized (mIcons) {
-            if (mIcons.contains(tag)) return;
-            mIcons.add(tag);
-        }
-        if (!mHandler.hasCallbacks(mLog)) {
-            mHandler.postDelayed(mLog, MIN_LOG_INTERVAL);
-        }
-    }
-
-    @Override
-    public void onIconHidden(String tag) {
-        synchronized (mIcons) {
-            if (!mIcons.contains(tag)) return;
-            mIcons.remove(tag);
-        }
-        if (!mHandler.hasCallbacks(mLog)) {
-            mHandler.postDelayed(mLog, MIN_LOG_INTERVAL);
-        }
-    }
-
-    private void doLog() {
-        long time = System.currentTimeMillis();
-        long timeSinceLastLog = time - mLastLog;
-        mLastLog = time;
-
-        ArraySet<String> icons;
-        synchronized (mIcons) {
-            icons = new ArraySet<>(mIcons);
-        }
-        mLogger.write(new LogMaker(STATUS_BAR_ICONS_CHANGED)
-                .setType(TYPE_ACTION)
-                .setLatency(timeSinceLastLog)
-                .addTaggedData(FIELD_NUM_STATUS_ICONS, icons.size())
-                .addTaggedData(FIELD_STATUS_ICONS, getBitField(icons)));
-    }
-
-    private int getBitField(ArraySet<String> icons) {
-        int iconsVisible = 0;
-        for (String icon : icons) {
-            int index = mIconIndex.indexOf(icon);
-            if (index >= 0) {
-                iconsVisible |= (1 << index);
-            }
-        }
-        return iconsVisible;
-    }
-
-    private final Runnable mLog = this::doLog;
-}
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 9422101..f79ad71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -541,7 +541,8 @@
 
     @VisibleForTesting
     void doUpdateMobileControllers() {
-        List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
+        List<SubscriptionInfo> subscriptions = mSubscriptionManager
+                .getActiveSubscriptionInfoList(true);
         if (subscriptions == null) {
             subscriptions = Collections.emptyList();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index 7ad547a..438226a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * A listener to heads up changes
@@ -33,12 +32,12 @@
     /**
      * A notification was just pinned to the top.
      */
-    default void onHeadsUpPinned(NotificationData.Entry entry) {}
+    default void onHeadsUpPinned(NotificationEntry entry) {}
 
     /**
      * A notification was just unpinned from the top.
      */
-    default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
+    default void onHeadsUpUnPinned(NotificationEntry entry) {}
 
     /**
      * A notification just became a heads up or turned back to its normal state.
@@ -46,5 +45,5 @@
      * @param entry     the entry of the changed notification
      * @param isHeadsUp whether the notification is now a headsUp notification
      */
-    default void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {}
+    default void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 866015e..dfb02cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -56,7 +56,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
@@ -83,7 +83,7 @@
     private RemoteInputController mController;
     private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
 
     private boolean mRemoved;
 
@@ -180,7 +180,7 @@
     }
 
     public static RemoteInputView inflate(Context context, ViewGroup root,
-            NotificationData.Entry entry,
+            NotificationEntry entry,
             RemoteInputController controller) {
         RemoteInputView v = (RemoteInputView)
                 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 6193159..3bd0d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
 
+import android.app.RemoteInput;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -42,14 +43,21 @@
     private static final String KEY_REQUIRES_TARGETING_P = "requires_targeting_p";
     private static final String KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS =
             "max_squeeze_remeasure_attempts";
+    private static final String KEY_EDIT_CHOICES_BEFORE_SENDING =
+            "edit_choices_before_sending";
+    private static final String KEY_SHOW_IN_HEADS_UP = "show_in_heads_up";
 
     private final boolean mDefaultEnabled;
     private final boolean mDefaultRequiresP;
     private final int mDefaultMaxSqueezeRemeasureAttempts;
+    private final boolean mDefaultEditChoicesBeforeSending;
+    private final boolean mDefaultShowInHeadsUp;
 
     private boolean mEnabled;
     private boolean mRequiresTargetingP;
     private int mMaxSqueezeRemeasureAttempts;
+    private boolean mEditChoicesBeforeSending;
+    private boolean mShowInHeadsUp;
 
     private final Context mContext;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -66,6 +74,10 @@
                 R.bool.config_smart_replies_in_notifications_requires_targeting_p);
         mDefaultMaxSqueezeRemeasureAttempts = resources.getInteger(
                 R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts);
+        mDefaultEditChoicesBeforeSending = resources.getBoolean(
+                R.bool.config_smart_replies_in_notifications_edit_choices_before_sending);
+        mDefaultShowInHeadsUp = resources.getBoolean(
+                R.bool.config_smart_replies_in_notifications_show_in_heads_up);
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS),
@@ -90,6 +102,9 @@
             mRequiresTargetingP = mParser.getBoolean(KEY_REQUIRES_TARGETING_P, mDefaultRequiresP);
             mMaxSqueezeRemeasureAttempts = mParser.getInt(
                     KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS, mDefaultMaxSqueezeRemeasureAttempts);
+            mEditChoicesBeforeSending = mParser.getBoolean(
+                    KEY_EDIT_CHOICES_BEFORE_SENDING, mDefaultEditChoicesBeforeSending);
+            mShowInHeadsUp = mParser.getBoolean(KEY_SHOW_IN_HEADS_UP, mDefaultShowInHeadsUp);
         }
     }
 
@@ -113,4 +128,31 @@
     public int getMaxSqueezeRemeasureAttempts() {
         return mMaxSqueezeRemeasureAttempts;
     }
+
+    /**
+     * Returns whether by tapping on a choice should let the user edit the input before it
+     * is sent to the app.
+     *
+     * @param remoteInputEditChoicesBeforeSending The value from
+     *         {@link RemoteInput#getEditChoicesBeforeSending()}
+     */
+    public boolean getEffectiveEditChoicesBeforeSending(
+            @RemoteInput.EditChoicesBeforeSending int remoteInputEditChoicesBeforeSending) {
+        switch (remoteInputEditChoicesBeforeSending) {
+            case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED:
+                return false;
+            case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED:
+                return true;
+            case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO:
+            default:
+                return mEditChoicesBeforeSending;
+        }
+    }
+
+    /**
+     * Returns whether smart suggestions should be enabled in heads-up notifications.
+     */
+    public boolean getShowInHeadsUp() {
+        return mShowInHeadsUp;
+    }
 }
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 f85d803..a76cf16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -36,8 +36,8 @@
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
@@ -194,7 +194,7 @@
      */
     public void addRepliesFromRemoteInput(
             SmartReplies smartReplies,
-            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+            SmartReplyController smartReplyController, NotificationEntry entry) {
         if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
             if (smartReplies.choices != null) {
                 for (int i = 0; i < smartReplies.choices.length; ++i) {
@@ -212,7 +212,7 @@
      * notification are shown.
      */
     public void addSmartActions(SmartActions smartActions,
-            SmartReplyController smartReplyController, NotificationData.Entry entry,
+            SmartReplyController smartReplyController, NotificationEntry entry,
             HeadsUpManager headsUpManager) {
         int numSmartActions = smartActions.actions.size();
         for (int n = 0; n < numSmartActions; n++) {
@@ -235,16 +235,15 @@
     @VisibleForTesting
     Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
             SmartReplies smartReplies, SmartReplyController smartReplyController,
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
         CharSequence choice = smartReplies.choices[replyIndex];
         b.setText(choice);
 
         OnDismissAction action = () -> {
-            // TODO(b/111437455): Also for EDIT_CHOICES_BEFORE_SENDING_AUTO, depending on flags.
-            if (smartReplies.remoteInput.getEditChoicesBeforeSending()
-                    == RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED) {
+            if (mConstants.getEffectiveEditChoicesBeforeSending(
+                    smartReplies.remoteInput.getEditChoicesBeforeSending())) {
                 entry.remoteInputText = choice;
                 mRemoteInputManager.activateRemoteInput(b,
                         new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput,
@@ -289,7 +288,7 @@
     @VisibleForTesting
     Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
             SmartActions smartActions, SmartReplyController smartReplyController,
-            NotificationData.Entry entry, HeadsUpManager headsUpManager) {
+            NotificationEntry entry, HeadsUpManager headsUpManager) {
         Notification.Action action = smartActions.actions.get(actionIndex);
         Button button = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_action_button, root, false);
@@ -428,9 +427,9 @@
                     markButtonsWithPendingSqueezeStatusAs(
                             LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);
 
-                    // The current button doesn't fit, so there's no point in measuring further
-                    // buttons.
-                    break;
+                    // The current button doesn't fit, keep on adding lower-priority buttons in case
+                    // any of those fit.
+                    continue;
                 }
 
                 // The current button fits, so mark all squeezed buttons as "successfully squeezed"
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
new file mode 100644
index 0000000..52cabe2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
@@ -0,0 +1,222 @@
+/*
+ * 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.wallpaper;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.widget.ImageView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.ScrimState;
+
+/**
+ * A view that draws mask upon either image wallpaper or music album art in AOD.
+ */
+public class AodMaskView extends ImageView implements StatusBarStateController.StateListener,
+        ImageWallpaperTransformer.TransformationListener {
+    private static final String TAG = AodMaskView.class.getSimpleName();
+    private static final int TRANSITION_DURATION = 1000;
+
+    private static final AnimatableProperty TRANSITION_PROGRESS = AnimatableProperty.from(
+            "transition_progress",
+            AodMaskView::setTransitionAmount,
+            AodMaskView::getTransitionAmount,
+            R.id.aod_mask_transition_progress_tag,
+            R.id.aod_mask_transition_progress_start_tag,
+            R.id.aod_mask_transition_progress_end_tag
+    );
+
+    private final AnimationProperties mTransitionProperties = new AnimationProperties();
+    private final ImageWallpaperTransformer mTransformer;
+    private final RectF mBounds = new RectF();
+    private boolean mChangingStates;
+    private boolean mNeedMask;
+    private float mTransitionAmount;
+    private final WallpaperManager mWallpaperManager;
+    private final DisplayManager mDisplayManager;
+    private DisplayListener mDisplayListener = new DisplayListener() {
+        @Override
+        public void onDisplayAdded(int displayId) {
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            // We just support DEFAULT_DISPLAY currently.
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                mTransformer.updateDisplayInfo(getDisplayInfo(displayId));
+            }
+        }
+    };
+
+    public AodMaskView(Context context) {
+        this(context, null);
+    }
+
+    public AodMaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, null);
+    }
+
+    @VisibleForTesting
+    public AodMaskView(Context context, AttributeSet attrs, ImageWallpaperTransformer transformer) {
+        super(context, attrs);
+        setClickable(false);
+
+        StatusBarStateController controller = Dependency.get(StatusBarStateController.class);
+        if (controller != null) {
+            controller.addCallback(this);
+        } else {
+            Log.d(TAG, "Can not get StatusBarStateController!");
+        }
+
+        mDisplayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
+        mDisplayManager.registerDisplayListener(mDisplayListener, null);
+        mWallpaperManager =
+                (WallpaperManager) getContext().getSystemService(Context.WALLPAPER_SERVICE);
+
+        if (transformer == null) {
+            mTransformer = new ImageWallpaperTransformer(this);
+            mTransformer.addFilter(new ScrimFilter());
+            mTransformer.addFilter(new VignetteFilter());
+            mTransformer.updateOffsets();
+            mTransformer.updateDisplayInfo(getDisplayInfo(Display.DEFAULT_DISPLAY));
+
+            mTransitionProperties.setDuration(TRANSITION_DURATION);
+            mTransitionProperties.setAnimationFinishListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mTransformer.setIsTransiting(false);
+                }
+
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mTransformer.setIsTransiting(true);
+                }
+            });
+        } else {
+            // This part should only be hit by test cases.
+            mTransformer = transformer;
+        }
+    }
+
+    private DisplayInfo getDisplayInfo(int displayId) {
+        DisplayInfo displayInfo = new DisplayInfo();
+        mDisplayManager.getDisplay(displayId).getDisplayInfo(displayInfo);
+        return displayInfo;
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        mBounds.set(0, 0, w, h);
+        mTransformer.updateOffsets();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mNeedMask) {
+            mTransformer.drawTransformedImage(canvas, null /* target */, null /* src */, mBounds);
+        }
+    }
+
+    private boolean checkIfNeedMask() {
+        // We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art).
+        return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop();
+    }
+
+    @Override
+    public void onStatePreChange(int oldState, int newState) {
+        mChangingStates = oldState != newState;
+        mNeedMask = checkIfNeedMask();
+    }
+
+    @Override
+    public void onStatePostChange() {
+        mChangingStates = false;
+    }
+
+    @Override
+    public void onStateChanged(int newState) {
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        if (!mNeedMask) {
+            return;
+        }
+
+        boolean enabled = checkFeatureIsEnabled();
+        mTransformer.updateAmbientModeState(enabled && isDozing);
+
+        if (enabled && !mChangingStates) {
+            setAnimatorProperty(isDozing);
+        } else {
+            invalidate();
+        }
+    }
+
+    private boolean checkFeatureIsEnabled() {
+        return FeatureFlagUtils.isEnabled(
+                getContext(), FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED);
+    }
+
+    @VisibleForTesting
+    void setAnimatorProperty(boolean isDozing) {
+        PropertyAnimator.setProperty(
+                this,
+                TRANSITION_PROGRESS,
+                isDozing ? 1f : 0f /* newEndValue */,
+                mTransitionProperties,
+                true /* animated */);
+    }
+
+    @Override
+    public void onTransformationUpdated() {
+        invalidate();
+    }
+
+    private void setTransitionAmount(float amount) {
+        mTransitionAmount = amount;
+        mTransformer.updateTransitionAmount(amount);
+    }
+
+    private float getTransitionAmount() {
+        return mTransitionAmount;
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java
new file mode 100644
index 0000000..d457dac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java
@@ -0,0 +1,81 @@
+/*
+ * 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.wallpaper;
+
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+/**
+ * Abstract filter used by static image wallpaper.
+ */
+abstract class ImageWallpaperFilter {
+    protected static final boolean DEBUG = false;
+
+    private ImageWallpaperTransformer mTransformer;
+
+    /**
+     * Apply this filter to the bitmap before drawing on canvas.
+     * @param c      The canvas that will draw to.
+     * @param bitmap The bitmap to apply this filter.
+     * @param src    The subset of the bitmap to be drawn.
+     * @param dest   The rectangle that the bitmap will be scaled/translated to fit into.
+     */
+    public abstract void apply(@NonNull Canvas c, @Nullable Bitmap bitmap,
+            @Nullable Rect src, @NonNull RectF dest);
+
+    /**
+     * Notifies the occurrence of built-in transition of the animation.
+     * @param animator The animator which was animated.
+     */
+    public abstract void onAnimatorUpdate(ValueAnimator animator);
+
+    /**
+     * Notifies the occurrence of another transition of the animation.
+     * @param amount The transition amount.
+     */
+    public abstract void onTransitionAmountUpdate(float amount);
+
+    /**
+     * To set the associated transformer.
+     * @param transformer The transformer that is associated with this filter.
+     */
+    public void setTransformer(ImageWallpaperTransformer transformer) {
+        if (transformer != null) {
+            mTransformer = transformer;
+        }
+    }
+
+    protected ImageWallpaperTransformer getTransformer() {
+        return mTransformer;
+    }
+
+    /**
+     * Notifies the changing of the offset value of the ImageWallpaper.
+     * @param force True to force re-evaluate offsets.
+     * @param xOffset X offset of the ImageWallpaper in percentage.
+     * @param yOffset Y offset of the ImageWallpaper in percentage.
+     */
+    public void onOffsetsUpdate(boolean force, float xOffset, float yOffset) {
+        // No-op
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java
new file mode 100644
index 0000000..25b0b0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java
@@ -0,0 +1,173 @@
+/*
+ * 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.wallpaper;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.DisplayInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to manage the filters that will be applied.
+ */
+public class ImageWallpaperTransformer {
+    private static final String TAG = ImageWallpaperTransformer.class.getSimpleName();
+
+    private DisplayInfo mDisplayInfo;
+    private final List<ImageWallpaperFilter> mFilters;
+    private final TransformationListener mListener;
+    private boolean mIsInAmbientMode;
+    private boolean mIsTransiting;
+
+    /**
+     * Constructor.
+     * @param listener A listener to inform you the transformation has updated.
+     */
+    public ImageWallpaperTransformer(TransformationListener listener) {
+        mFilters = new ArrayList<>();
+        mListener = listener;
+    }
+
+    /**
+     * Claim that we want to use the specified filter.
+     * @param filter The filter will be used.
+     */
+    public void addFilter(ImageWallpaperFilter filter) {
+        if (filter != null) {
+            filter.setTransformer(this);
+            mFilters.add(filter);
+        }
+    }
+
+    /**
+     * Check if any transition is running.
+     * @return True if the transition is running, false otherwise.
+     */
+    boolean isTransiting() {
+        return mIsTransiting;
+    }
+
+    /**
+     * Indicate if any transition is running. <br/>
+     * @param isTransiting True if the transition is running.
+     */
+    void setIsTransiting(boolean isTransiting) {
+        mIsTransiting = isTransiting;
+    }
+
+    /**
+     * Check if the device is in ambient mode.
+     * @return True if the device is in ambient mode, false otherwise.
+     */
+    public boolean isInAmbientMode() {
+        return mIsInAmbientMode;
+    }
+
+    /**
+     * Update current state of ambient mode.
+     * @param isInAmbientMode Current ambient mode state.
+     */
+    public void updateAmbientModeState(boolean isInAmbientMode) {
+        mIsInAmbientMode = isInAmbientMode;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        int idx = 0;
+        for (ImageWallpaperFilter filter : mFilters) {
+            sb.append(idx++).append(": ").append(filter.getClass().getSimpleName()).append("\n");
+        }
+        if (sb.length() == 0) {
+            sb.append("No filters applied");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Set a new display info.
+     * @param displayInfo New display info.
+     */
+    public void updateDisplayInfo(DisplayInfo displayInfo) {
+        mDisplayInfo = displayInfo;
+    }
+
+    /**
+     * To get current display info.
+     * @return Current display info.
+     */
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayInfo;
+    }
+
+    /**
+     * Update the offsets with default value.
+     */
+    public void updateOffsets() {
+        this.updateOffsets(true, 0f, .5f);
+    }
+
+    /**
+     * To notify the filters that the offset of the ImageWallpaper changes.
+     * @param force True to force re-evaluate offsets.
+     * @param offsetX X offset of the ImageWallpaper in percentage.
+     * @param offsetY Y offset of the ImageWallpaper in percentage.
+     */
+    public void updateOffsets(boolean force, float offsetX, float offsetY) {
+        mFilters.forEach(filter -> filter.onOffsetsUpdate(force, offsetX, offsetY));
+    }
+
+    /**
+     * Apply all specified filters to the bitmap then draw to the canvas.
+     * @param c      The canvas that will draw to.
+     * @param target The bitmap to apply filters.
+     * @param src    The subset of the bitmap to be drawn
+     * @param dest   The rectangle that the bitmap will be scaled/translated to fit into.
+     */
+    void drawTransformedImage(@NonNull Canvas c, @Nullable Bitmap target,
+            @Nullable Rect src, @NonNull RectF dest) {
+        mFilters.forEach(filter -> filter.apply(c, target, src, dest));
+    }
+
+    /**
+     * Update the transition amount. <br/>
+     * Must invoke this to update transition amount if not running built-in transition.
+     * @param amount The transition amount.
+     */
+    void updateTransitionAmount(float amount) {
+        mFilters.forEach(filter -> filter.onTransitionAmountUpdate(amount));
+        if (mListener != null) {
+            mListener.onTransformationUpdated();
+        }
+    }
+
+    /**
+     * An interface that informs the transformation status.
+     */
+    public interface TransformationListener {
+        /**
+         * Notifies the update of the transformation.
+         */
+        void onTransformationUpdated();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java
new file mode 100644
index 0000000..637e48e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java
@@ -0,0 +1,67 @@
+/*
+ * 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.wallpaper;
+
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+/**
+ * A filter that implements 70% black scrim effect.
+ */
+public class ScrimFilter extends ImageWallpaperFilter {
+    private static final int MAX_ALPHA = (int) (255 * .7f);
+    private static final int MIN_ALPHA = 0;
+
+    private final Paint mPaint;
+
+    public ScrimFilter() {
+        mPaint = new Paint();
+        mPaint.setColor(Color.BLACK);
+        mPaint.setAlpha(MAX_ALPHA);
+    }
+
+    @Override
+    public void apply(Canvas c, Bitmap bitmap, Rect src, RectF dest) {
+        ImageWallpaperTransformer transformer = getTransformer();
+
+        // If it is not in the transition, we need to set the property according to aod state.
+        if (!transformer.isTransiting()) {
+            mPaint.setAlpha(transformer.isInAmbientMode() ? MAX_ALPHA : MIN_ALPHA);
+        }
+
+        c.drawRect(dest, mPaint);
+    }
+
+    @Override
+    public void onAnimatorUpdate(ValueAnimator animator) {
+        ImageWallpaperTransformer transformer = getTransformer();
+        float fraction = animator.getAnimatedFraction();
+        float factor = transformer.isInAmbientMode() ? fraction : 1f - fraction;
+        mPaint.setAlpha((int) (factor * MAX_ALPHA));
+    }
+
+    @Override
+    public void onTransitionAmountUpdate(float amount) {
+        mPaint.setAlpha((int) (amount * MAX_ALPHA));
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java
new file mode 100644
index 0000000..ad0b98b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java
@@ -0,0 +1,124 @@
+/*
+ * 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.wallpaper;
+
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.util.Log;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A filter that implements vignette effect.
+ */
+public class VignetteFilter extends ImageWallpaperFilter {
+    private static final String TAG = VignetteFilter.class.getSimpleName();
+    private static final int MAX_ALPHA = 255;
+    private static final int MIN_ALPHA = 0;
+
+    private final Paint mPaint;
+    private final Matrix mMatrix;
+    private final Shader mShader;
+
+    private float mXOffset;
+    private float mYOffset;
+    private float mCenterX;
+    private float mCenterY;
+    private float mStretchX;
+    private float mStretchY;
+    private boolean mCalculateOffsetNeeded;
+
+    public VignetteFilter() {
+        mPaint = new Paint();
+        mMatrix = new Matrix();
+        mShader = new RadialGradient(0, 0, 1,
+                Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
+    }
+
+    @Override
+    public void apply(Canvas c, Bitmap bitmap, Rect src, RectF dest) {
+        DisplayInfo info = getTransformer().getDisplayInfo();
+
+        if (mCalculateOffsetNeeded) {
+            int lw = info.logicalWidth;
+            int lh = info.logicalHeight;
+            mCenterX = lw / 2 + (dest.width() - lw) * mXOffset;
+            mCenterY = lh / 2 + (dest.height() - lh) * mYOffset;
+            mStretchX = info.logicalWidth / 2;
+            mStretchY = info.logicalHeight / 2;
+            mCalculateOffsetNeeded = false;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "apply: lw=" + info.logicalWidth + ", lh=" + info.logicalHeight
+                    + ", center=(" + mCenterX + "," + mCenterY + ")"
+                    + ", stretch=(" + mStretchX + "," + mStretchY + ")");
+        }
+
+        mMatrix.reset();
+        mMatrix.postTranslate(mCenterX, mCenterY);
+        mMatrix.postScale(mStretchX, mStretchY, mCenterX, mCenterY);
+        mShader.setLocalMatrix(mMatrix);
+        mPaint.setShader(mShader);
+
+        ImageWallpaperTransformer transformer = getTransformer();
+
+        // If it is not in the transition, we need to set the property according to aod state.
+        if (!transformer.isTransiting()) {
+            mPaint.setAlpha(transformer.isInAmbientMode() ? MAX_ALPHA : MIN_ALPHA);
+        }
+
+        c.drawRect(dest, mPaint);
+    }
+
+    @Override
+    public void onAnimatorUpdate(ValueAnimator animator) {
+        ImageWallpaperTransformer transformer = getTransformer();
+        float fraction = animator.getAnimatedFraction();
+        float factor = transformer.isInAmbientMode() ? fraction : 1f - fraction;
+        mPaint.setAlpha((int) (factor * MAX_ALPHA));
+    }
+
+    @Override
+    public void onTransitionAmountUpdate(float amount) {
+        mPaint.setAlpha((int) (amount * MAX_ALPHA));
+    }
+
+    @Override
+    public void onOffsetsUpdate(boolean force, float xOffset, float yOffset) {
+        if (force || mXOffset != xOffset || mYOffset != yOffset) {
+            mXOffset = xOffset;
+            mYOffset = yOffset;
+            mCalculateOffsetNeeded = true;
+        }
+    }
+
+    @VisibleForTesting
+    public PointF getCenterPoint() {
+        return new PointF(mCenterX, mCenterY);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 4150602..fbc1c20 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -21,7 +21,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -35,22 +34,25 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.text.TextPaint;
 import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.TextClock;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.function.Consumer;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 // Need to run on the main thread because KeyguardSliceView$Row init checks for
@@ -58,8 +60,8 @@
 // the keyguard_clcok_switch layout is inflated.
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardClockSwitchTest extends SysuiTestCase {
-    private PluginManager mPluginManager;
     private FrameLayout mClockContainer;
+    private StatusBarStateController.StateListener mStateListener;
 
     @Mock
     TextClock mClockView;
@@ -68,29 +70,13 @@
 
     @Before
     public void setUp() {
-        mPluginManager = mDependency.injectMockDependency(PluginManager.class);
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         mKeyguardClockSwitch =
                 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
         mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view);
         MockitoAnnotations.initMocks(this);
         when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
-    }
-
-    @Test
-    public void onAttachToWindow_addPluginListener() {
-        mKeyguardClockSwitch.onAttachedToWindow();
-
-        ArgumentCaptor<PluginListener> listener = ArgumentCaptor.forClass(PluginListener.class);
-        verify(mPluginManager).addPluginListener(listener.capture(), eq(ClockPlugin.class));
-    }
-
-    @Test
-    public void onDetachToWindow_removePluginListener() {
-        mKeyguardClockSwitch.onDetachedFromWindow();
-
-        ArgumentCaptor<PluginListener> listener = ArgumentCaptor.forClass(PluginListener.class);
-        verify(mPluginManager).removePluginListener(listener.capture());
+        mStateListener = mKeyguardClockSwitch.getStateListener();
     }
 
     @Test
@@ -98,9 +84,8 @@
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
 
-        listener.onPluginConnected(plugin, null);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
 
         verify(mClockView).setVisibility(GONE);
         assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer);
@@ -116,9 +101,8 @@
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getBigClockView()).thenReturn(pluginView);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
         // WHEN the plugin is connected
-        listener.onPluginConnected(plugin, null);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
         // THEN the big clock container is visible and it is the parent of the
         // big clock view.
         assertThat(bigClockContainer.getVisibility()).isEqualTo(VISIBLE);
@@ -128,8 +112,7 @@
     @Test
     public void onPluginConnected_nullView() {
         ClockPlugin plugin = mock(ClockPlugin.class);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin, null);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
         verify(mClockView, never()).setVisibility(GONE);
     }
 
@@ -138,27 +121,36 @@
         // GIVEN a plugin has already connected
         ClockPlugin plugin1 = mock(ClockPlugin.class);
         when(plugin1.getView()).thenReturn(new TextClock(getContext()));
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin1, null);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin1);
         // WHEN a second plugin is connected
         ClockPlugin plugin2 = mock(ClockPlugin.class);
         when(plugin2.getView()).thenReturn(new TextClock(getContext()));
-        listener.onPluginConnected(plugin2, null);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin2);
         // THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
         assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
         assertThat(plugin1.getView().getParent()).isNull();
     }
 
     @Test
+    public void onPluginConnected_darkAmountInitialized() {
+        // GIVEN that the dark amount has already been set
+        mKeyguardClockSwitch.setDarkAmount(0.5f);
+        // WHEN a plugin is connected
+        ClockPlugin plugin = mock(ClockPlugin.class);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+        // THEN dark amount should be initalized on the plugin.
+        verify(plugin).setDarkAmount(0.5f);
+    }
+
+    @Test
     public void onPluginDisconnected_showDefaultClock() {
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
         mClockView.setVisibility(GONE);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
 
-        listener.onPluginConnected(plugin, null);
-        listener.onPluginDisconnected(plugin);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(null);
 
         verify(mClockView).setVisibility(VISIBLE);
         assertThat(plugin.getView().getParent()).isNull();
@@ -174,10 +166,9 @@
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getBigClockView()).thenReturn(pluginView);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin, null);
-        // WHEN the plugin is disconnected
-        listener.onPluginDisconnected(plugin);
+        // WHEN the plugin is connected and then disconnected
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(null);
         // THEN the big lock container is GONE and the big clock view doesn't have
         // a parent.
         assertThat(bigClockContainer.getVisibility()).isEqualTo(GONE);
@@ -187,41 +178,23 @@
     @Test
     public void onPluginDisconnected_nullView() {
         ClockPlugin plugin = mock(ClockPlugin.class);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin, null);
-        listener.onPluginDisconnected(plugin);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(null);
         verify(mClockView, never()).setVisibility(GONE);
     }
 
     @Test
-    public void onPluginDisconnected_firstOfTwoDisconnected() {
-        // GIVEN two plugins are connected
-        ClockPlugin plugin1 = mock(ClockPlugin.class);
-        when(plugin1.getView()).thenReturn(new TextClock(getContext()));
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin1, null);
-        ClockPlugin plugin2 = mock(ClockPlugin.class);
-        when(plugin2.getView()).thenReturn(new TextClock(getContext()));
-        listener.onPluginConnected(plugin2, null);
-        // WHEN the first plugin is disconnected
-        listener.onPluginDisconnected(plugin1);
-        // THEN the view from the second plugin is still a child of KeyguardClockSwitch.
-        assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
-        assertThat(plugin1.getView().getParent()).isNull();
-    }
-
-    @Test
     public void onPluginDisconnected_secondOfTwoDisconnected() {
         // GIVEN two plugins are connected
         ClockPlugin plugin1 = mock(ClockPlugin.class);
         when(plugin1.getView()).thenReturn(new TextClock(getContext()));
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin1, null);
+        Consumer<ClockPlugin> consumer = mKeyguardClockSwitch.getClockPluginConsumer();
+        consumer.accept(plugin1);
         ClockPlugin plugin2 = mock(ClockPlugin.class);
         when(plugin2.getView()).thenReturn(new TextClock(getContext()));
-        listener.onPluginConnected(plugin2, null);
+        consumer.accept(plugin2);
         // WHEN the second plugin is disconnected
-        listener.onPluginDisconnected(plugin2);
+        consumer.accept(null);
         // THEN the default clock should be shown.
         verify(mClockView).setVisibility(VISIBLE);
         assertThat(plugin1.getView().getParent()).isNull();
@@ -240,8 +213,7 @@
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin, null);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
 
         mKeyguardClockSwitch.setTextColor(Color.WHITE);
 
@@ -265,11 +237,34 @@
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
         Style style = mock(Style.class);
-        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
-        listener.onPluginConnected(plugin, null);
+        mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
 
         mKeyguardClockSwitch.setStyle(style);
 
         verify(plugin).setStyle(style);
     }
+
+    @Test
+    public void onStateChanged_InvisibleInShade() {
+        // GIVEN that the big clock container is visible
+        ViewGroup container = mock(ViewGroup.class);
+        when(container.getVisibility()).thenReturn(View.VISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(container);
+        // WHEN transitioned to SHADE state
+        mStateListener.onStateChanged(StatusBarState.SHADE);
+        // THEN the container is invisible.
+        verify(container).setVisibility(View.INVISIBLE);
+    }
+
+    @Test
+    public void onStateChanged_VisibleInKeyguard() {
+        // GIVEN that the big clock container is invisible
+        ViewGroup container = mock(ViewGroup.class);
+        when(container.getVisibility()).thenReturn(View.INVISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(container);
+        // WHEN transitioned to KEYGUARD state
+        mStateListener.onStateChanged(StatusBarState.KEYGUARD);
+        // THEN the container is visible.
+        verify(container).setVisibility(View.VISIBLE);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
new file mode 100644
index 0000000..9e946fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class BubbleClockControllerTest extends SysuiTestCase {
+
+    private BubbleClockController mClockController;
+
+    @Before
+    public void setUp() {
+        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+        mClockController = BubbleClockController.build(layoutInflater);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        ViewGroup smallClockFrame = (ViewGroup) mClockController.getView();
+        View smallClock = smallClockFrame.getChildAt(0);
+        // WHEN dark amount is set to AOD
+        mClockController.setDarkAmount(1f);
+        // THEN small clock should not be shown.
+        assertThat(smallClock.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setTextColor_setDigitalClock() {
+        ViewGroup smallClock = (ViewGroup) mClockController.getView();
+        // WHEN text color is set
+        mClockController.setTextColor(42);
+        // THEN child of small clock should have text color set.
+        TextView digitalClock = (TextView) smallClock.getChildAt(0);
+        assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java
new file mode 100644
index 0000000..fd7657f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class CrossFadeDarkControllerTest extends SysuiTestCase {
+
+    private View mViewFadeIn;
+    private View mViewFadeOut;
+    private CrossFadeDarkController mDarkController;
+
+    @Before
+    public void setUp() {
+        mViewFadeIn = new TextView(getContext());
+        mViewFadeIn.setVisibility(View.VISIBLE);
+        mViewFadeIn.setAlpha(1f);
+        mViewFadeOut = new TextView(getContext());
+        mViewFadeOut.setVisibility(View.VISIBLE);
+        mViewFadeOut.setAlpha(1f);
+
+        mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        // WHEN dark amount corresponds to AOD
+        mDarkController.setDarkAmount(1f);
+        // THEN fade in view should be faded in and fade out view faded out.
+        assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setDarkAmount_fadeOut() {
+        // WHEN dark amount corresponds to lock screen
+        mDarkController.setDarkAmount(0f);
+        // THEN fade out view should bed faded out and fade in view faded in.
+        assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void setDarkAmount_partialFadeIn() {
+        // WHEN dark amount corresponds to a partial transition
+        mDarkController.setDarkAmount(0.9f);
+        // THEN views should have intermediate alpha value.
+        assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f);
+        assertThat(mViewFadeIn.getAlpha()).isLessThan(1f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void setDarkAmount_partialFadeOut() {
+        // WHEN dark amount corresponds to a partial transition
+        mDarkController.setDarkAmount(0.1f);
+        // THEN views should have intermediate alpha value.
+        assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f);
+        assertThat(mViewFadeOut.getAlpha()).isLessThan(1f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
new file mode 100644
index 0000000..8de8f3d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class StretchAnalogClockControllerTest extends SysuiTestCase {
+
+    private StretchAnalogClockController mClockController;
+
+    @Before
+    public void setUp() {
+        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+        mClockController = StretchAnalogClockController.build(layoutInflater);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        ViewGroup smallClockFrame = (ViewGroup) mClockController.getView();
+        View smallClock = smallClockFrame.getChildAt(0);
+        // WHEN dark amount is set to AOD
+        mClockController.setDarkAmount(1f);
+        // THEN small clock should not be shown.
+        assertThat(smallClock.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setTextColor_setDigitalClock() {
+        ViewGroup smallClock = (ViewGroup) mClockController.getView();
+        // WHEN text color is set
+        mClockController.setTextColor(42);
+        // THEN child of small clock should have text color set.
+        TextView digitalClock = (TextView) smallClock.getChildAt(0);
+        assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 199a3a6..31e8071 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -36,9 +36,9 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -391,19 +391,19 @@
     }
 
     private void entryRemoved(StatusBarNotification notification) {
-        mEntryListener.onEntryRemoved(new NotificationData.Entry(notification),
-                null, null, null, false, false);
+        mEntryListener.onEntryRemoved(new NotificationEntry(notification),
+                null, false);
     }
 
     private void entryAdded(StatusBarNotification notification, int importance) {
-        NotificationData.Entry entry = new NotificationData.Entry(notification);
+        NotificationEntry entry = new NotificationEntry(notification);
         entry.importance = importance;
         mEntryListener.onPendingEntryAdded(entry);
     }
 
     private void entryUpdated(StatusBarNotification notification, int importance) {
-        NotificationData.Entry entry = new NotificationData.Entry(notification);
+        NotificationEntry entry = new NotificationEntry(notification);
         entry.importance = importance;
-        mEntryListener.onEntryUpdated(entry);
+        mEntryListener.onPostEntryUpdated(entry);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 521d5d1..53ad0b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -16,17 +16,12 @@
 
 package com.android.systemui;
 
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.graphics.Bitmap;
@@ -58,13 +53,15 @@
     @Mock private SurfaceHolder mSurfaceHolder;
     @Mock private DisplayInfo mDisplayInfo;
 
-    CountDownLatch mEventCountdown;
+    private CountDownLatch mEventCountdown;
+    private CountDownLatch mAmbientEventCountdown;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         mEventCountdown = new CountDownLatch(1);
+        mAmbientEventCountdown = new CountDownLatch(2);
 
         mImageWallpaper = new ImageWallpaper() {
             @Override
@@ -86,6 +83,11 @@
                         assertTrue("mFixedSizeAllowed should be true", allowed);
                         mEventCountdown.countDown();
                     }
+
+                    @Override
+                    public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
+                        mAmbientEventCountdown.countDown();
+                    }
                 };
             }
         };
@@ -132,4 +134,23 @@
         verify(mSurfaceHolder, times(1)).setFixedSize(ImageWallpaper.DrawableEngine.MIN_BACKGROUND_WIDTH, ImageWallpaper.DrawableEngine.MIN_BACKGROUND_HEIGHT);
     }
 
+    @Test
+    public void testDeliversAmbientModeChanged() {
+        ImageWallpaper.DrawableEngine wallpaperEngine =
+                (ImageWallpaper.DrawableEngine) mImageWallpaper.onCreateEngine();
+
+        assertEquals("setFixedSizeAllowed should have been called.",
+                0, mEventCountdown.getCount());
+
+        wallpaperEngine.setCreated(true);
+        wallpaperEngine.doAmbientModeChanged(false, 1000);
+        assertFalse("ambient mode should be false", wallpaperEngine.isInAmbientMode());
+        assertEquals("onAmbientModeChanged should have been called.",
+                1, mAmbientEventCountdown.getCount());
+
+        wallpaperEngine.doAmbientModeChanged(true, 1000);
+        assertTrue("ambient mode should be true", wallpaperEngine.isInAmbientMode());
+        assertEquals("onAmbientModeChanged should have been called.",
+                0, mAmbientEventCountdown.getCount());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 8f2b2d0..fa5cf04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -18,6 +18,9 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.IActivityManager;
 import android.content.Context;
@@ -29,6 +32,9 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
@@ -36,6 +42,8 @@
 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;
 
@@ -45,6 +53,8 @@
 public class BubbleControllerTest extends SysuiTestCase {
 
     @Mock
+    private NotificationEntryManager mNotificationEntryManager;
+    @Mock
     private WindowManager mWindowManager;
     @Mock
     private IActivityManager mActivityManager;
@@ -52,17 +62,24 @@
     private DozeParameters mDozeParameters;
     @Mock
     private FrameLayout mStatusBarView;
+    @Captor
+    private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
 
     private TestableBubbleController mBubbleController;
     private StatusBarWindowController mStatusBarWindowController;
+    private NotificationEntryListener mEntryListener;
 
     private NotificationTestHelper mNotificationTestHelper;
     private ExpandableNotificationRow mRow;
     private ExpandableNotificationRow mRow2;
 
+    @Mock
+    private NotificationData mNotificationData;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
 
         // Bubbles get added to status bar window view
         mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
@@ -74,72 +91,91 @@
         mRow = mNotificationTestHelper.createBubble();
         mRow2 = mNotificationTestHelper.createBubble();
 
-        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
-    }
+        // Return non-null notification data from the NEM
+        when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
+        when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
 
-    @Test
-    public void testIsBubble() {
-        assertTrue(mRow.getEntry().isBubble());
+        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+
+        // Get a reference to the BubbleController's entry listener
+        verify(mNotificationEntryManager, atLeastOnce())
+                .addNotificationEntryListener(mEntryListenerCaptor.capture());
+        mEntryListener = mEntryListenerCaptor.getValue();
     }
 
     @Test
     public void testAddBubble() {
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
     }
 
     @Test
     public void testHasBubbles() {
         assertFalse(mBubbleController.hasBubbles());
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
     }
 
     @Test
     public void testRemoveBubble() {
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
 
         mBubbleController.removeBubble(mRow.getEntry().key);
         assertFalse(mStatusBarWindowController.getBubblesShowing());
+        assertTrue(mRow.getEntry().isBubbleDismissed());
+        verify(mNotificationEntryManager).updateNotifications();
     }
 
     @Test
     public void testDismissStack() {
-        mBubbleController.addBubble(mRow.getEntry());
-        mBubbleController.addBubble(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+        mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
 
         mBubbleController.dismissStack();
         assertFalse(mStatusBarWindowController.getBubblesShowing());
+        verify(mNotificationEntryManager).updateNotifications();
     }
 
     @Test
     public void testIsStackExpanded() {
         assertFalse(mBubbleController.isStackExpanded());
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
 
         BubbleStackView stackView = mBubbleController.getStackView();
-        stackView.animateExpansion(true /* expanded */);
+        stackView.expandStack();
         assertTrue(mBubbleController.isStackExpanded());
 
-        stackView.animateExpansion(false /* expanded */);
+        stackView.collapseStack();
         assertFalse(mBubbleController.isStackExpanded());
     }
 
     @Test
     public void testCollapseStack() {
-        mBubbleController.addBubble(mRow.getEntry());
-        mBubbleController.addBubble(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+        mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
 
         BubbleStackView stackView = mBubbleController.getStackView();
-        stackView.animateExpansion(true /* expanded */);
+        stackView.expandStack();
         assertTrue(mBubbleController.isStackExpanded());
 
         mBubbleController.collapseStack();
         assertFalse(mBubbleController.isStackExpanded());
     }
 
+    @Test
+    public void testMarkNewNotificationAsBubble() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        assertTrue(mRow.getEntry().isBubble());
+    }
+
+    @Test
+    public void testMarkNewNotificationAsShowInShade() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+    }
+
     static class TestableBubbleController extends BubbleController {
 
         TestableBubbleController(Context context,
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 ce28b50..dc42872 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -104,7 +104,7 @@
     }
 
     @Override
-    public void onDoubleTap(float x, float y) {
+    public void onSlpiTap(float x, float y) {
         doubleTapX = y;
         doubleTapY = y;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index a8ff0b2..cfc19ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 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.verify;
 
@@ -44,6 +45,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -67,6 +69,8 @@
     private AlarmManager mAlarmManager;
     @Mock
     private NotificationMediaManager mNotificationMediaManager;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
     private TestableKeyguardSliceProvider mProvider;
     private boolean mIsZenMode;
 
@@ -76,7 +80,7 @@
         mIsZenMode = false;
         mProvider = new TestableKeyguardSliceProvider();
         mProvider.attachInfo(getContext(), null);
-        mProvider.initDependencies();
+        mProvider.initDependencies(mNotificationMediaManager, mStatusBarStateController);
         SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
     }
 
@@ -98,6 +102,7 @@
     @Test
     public void onBindSlice_readsMedia() {
         MediaMetadata metadata = mock(MediaMetadata.class);
+        mProvider.onDozingChanged(true);
         mProvider.onMetadataChanged(metadata);
         mProvider.onBindSlice(mProvider.getUri());
         verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
@@ -162,7 +167,31 @@
     @Test
     public void onMetadataChanged_updatesSlice() {
         mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onDozingChanged(true);
         verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // Hides after waking up
+        reset(mContentResolver);
+        mProvider.onDozingChanged(false);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // And won't update slice if device is awake
+        reset(mContentResolver);
+        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
+    }
+
+    @Test
+    public void onDozingChanged_updatesSliceIfMedia() {
+        // Show media when dozing
+        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onDozingChanged(true);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // Do not notify again if nothing changed
+        reset(mContentResolver);
+        mProvider.onDozingChanged(true);
+        verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
     }
 
     private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
@@ -201,11 +230,6 @@
         protected String getFormattedDateLocked() {
             return super.getFormattedDateLocked() + mCounter++;
         }
-
-        @Override
-        public void initDependencies() {
-            mMediaManager = mNotificationMediaManager;
-        }
     }
 
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 4583770..3415c72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -91,7 +91,7 @@
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
         mMockManager = mock(PluginManagerImpl.class);
-        when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader());
+        when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader());
         mMockEnabler = mock(PluginEnabler.class);
         when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
         mMockVersionInfo = mock(VersionInfo.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 76e68f1..536c043 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -25,6 +25,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -135,9 +136,13 @@
         });
         resetExceptionHandler();
 
+        String sourceDir = "myPlugin";
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.sourceDir = sourceDir;
+        applicationInfo.packageName = WHITELISTED_PACKAGE;
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNull(mPluginManager.getOneShotPlugin("myPlugin", TestPlugin.class));
-        assertNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
+        assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class));
+        assertNull(mPluginManager.getClassLoader(applicationInfo));
     }
 
     @Test
@@ -152,9 +157,16 @@
         });
         resetExceptionHandler();
 
+        String sourceDir = "myPlugin";
+        ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo();
+        whiteListedApplicationInfo.sourceDir = sourceDir;
+        whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE;
+        ApplicationInfo invalidApplicationInfo = new ApplicationInfo();
+        invalidApplicationInfo.sourceDir = sourceDir;
+        invalidApplicationInfo.packageName = "com.android.invalidpackage";
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNotNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
-        assertNull(mPluginManager.getClassLoader("myPlugin", "com.android.invalidpackage"));
+        assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo));
+        assertNull(mPluginManager.getClassLoader(invalidApplicationInfo));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 9d0556f..f8957b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -36,7 +36,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 
@@ -67,7 +67,7 @@
 
     private AlertingNotificationManager mAlertingNotificationManager;
 
-    protected NotificationData.Entry mEntry;
+    protected NotificationEntry mEntry;
     protected Handler mTestHandler;
     private StatusBarNotification mSbn;
     protected boolean mTimedOut = false;
@@ -119,7 +119,7 @@
     public void setUp() {
         mTestHandler = Handler.createAsync(Looper.myLooper());
         mSbn = createNewNotification(0 /* id */);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.setRow(mRow);
 
         mAlertingNotificationManager = createAlertingNotificationManager();
@@ -170,7 +170,7 @@
     public void testReleaseAllImmediately() {
         for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = createNewNotification(i);
-            NotificationData.Entry entry = new NotificationData.Entry(sbn);
+            NotificationEntry entry = new NotificationEntry(sbn);
             entry.setRow(mRow);
             mAlertingNotificationManager.showNotification(entry);
         }
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 65c04fe..c880172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -31,8 +31,9 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -85,7 +86,7 @@
 
     @Test
     public void testNotificationUpdateCallsUpdateNotification() {
-        when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn));
+        when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationEntry(mSbn));
         mListener.onNotificationPosted(mSbn, mRanking);
         TestableLooper.get(this).processAllMessages();
         verify(mEntryManager).updateNotification(mSbn, mRanking);
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 f168a49..d7ca5b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
 import static android.content.Intent.ACTION_USER_SWITCHED;
 
 import static junit.framework.Assert.assertFalse;
@@ -42,8 +41,8 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
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 150dcb9..c159516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -24,8 +24,8 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.ShadeController;
 
@@ -60,7 +60,7 @@
 
     private TestableNotificationRemoteInputManager mRemoteInputManager;
     private StatusBarNotification mSbn;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private RemoteInputHistoryExtender mRemoteInputHistoryExtender;
     private SmartReplyHistoryExtender mSmartReplyHistoryExtender;
     private RemoteInputActiveExtender mRemoteInputActiveExtender;
@@ -75,7 +75,7 @@
                 Handler.createAsync(Looper.myLooper()));
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.setRow(mRow);
 
         mRemoteInputManager.setUpWithPresenterForTest(mCallback,
@@ -170,7 +170,7 @@
         // Setup a notification entry with 1 remote input.
         StatusBarNotification newSbn =
                 mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
-        NotificationData.Entry entry = new NotificationData.Entry(newSbn);
+        NotificationEntry entry = new NotificationEntry(newSbn);
 
         // Try rebuilding to add another reply.
         newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
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 fb5e875..8952594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -17,13 +17,17 @@
 package com.android.systemui.statusbar;
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
@@ -32,7 +36,7 @@
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -86,8 +90,7 @@
      * @throws Exception
      */
     public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
-        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */,
-                false /* isBubble */);
+        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
     }
 
     /**
@@ -98,8 +101,7 @@
      * @throws Exception
      */
     public ExpandableNotificationRow createRow(Notification notification) throws Exception {
-        return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */,
-                false /* isBubble */);
+        return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */);
     }
 
     /**
@@ -112,8 +114,7 @@
      */
     public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags)
             throws Exception {
-        return generateRow(createNotification(), PKG, UID, extraInflationFlags,
-                false /* isBubble */);
+        return generateRow(createNotification(), PKG, UID, extraInflationFlags);
     }
 
     /**
@@ -134,20 +135,21 @@
         return createGroup(2);
     }
 
-    /**
-     * Retursn an {@link ExpandableNotificationRow} that should be a bubble.
-     */
-    public ExpandableNotificationRow createBubble() throws Exception {
-        return createRow(PKG, UID, false /* isGroupSummary */, null /* groupKey */,
-                true /* isBubble */);
-    }
-
     private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception {
-        return createRow(PKG, UID, true /* isGroupSummary */, groupkey, false);
+        return createRow(PKG, UID, true /* isGroupSummary */, groupkey);
     }
 
     private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception {
-        return createRow(PKG, UID, false /* isGroupSummary */, groupkey, false);
+        return createRow(PKG, UID, false /* isGroupSummary */, groupkey);
+    }
+
+    /**
+     * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+     */
+    public ExpandableNotificationRow createBubble() throws Exception {
+        Notification n = createNotification(false /* isGroupSummary */,
+                null /* groupKey */, true /* isBubble */);
+        return generateRow(n, PKG, UID, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
     }
 
     /**
@@ -157,7 +159,6 @@
      * @param uid uid used for creating a {@link StatusBarNotification}
      * @param isGroupSummary whether the notification row is a group summary
      * @param groupKey the group key for the notification group used across notifications
-     * @param isBubble
      * @return a row with that's either a standalone notification or a group notification if the
      *         groupKey is non-null
      * @throws Exception
@@ -166,10 +167,10 @@
             String pkg,
             int uid,
             boolean isGroupSummary,
-            @Nullable String groupKey, boolean isBubble)
+            @Nullable String groupKey)
             throws Exception {
         Notification notif = createNotification(isGroupSummary, groupKey);
-        return generateRow(notif, pkg, uid, 0 /* inflationFlags */, isBubble);
+        return generateRow(notif, pkg, uid, 0 /* inflationFlags */);
     }
 
     /**
@@ -188,8 +189,20 @@
      * @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) {
+        return createNotification(isGroupSummary, groupKey, false /* isBubble */);
+    }
+
+    /**
+     * 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
+     * @param isBubble whether this notification should bubble
+     * @return a notification that is in the group specified or standalone if unspecified
+     */
     private Notification createNotification(boolean isGroupSummary,
-            @Nullable String groupKey) {
+            @Nullable String groupKey, boolean isBubble) {
         Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -207,6 +220,9 @@
         if (!TextUtils.isEmpty(groupKey)) {
             notificationBuilder.setGroup(groupKey);
         }
+        if (isBubble) {
+            notificationBuilder.setBubbleMetadata(makeBubbleMetadata());
+        }
         return notificationBuilder.build();
     }
 
@@ -214,7 +230,17 @@
             Notification notification,
             String pkg,
             int uid,
-            @InflationFlag int extraInflationFlags, boolean isBubble)
+            @InflationFlag int extraInflationFlags)
+            throws Exception {
+        return generateRow(notification, pkg, uid, extraInflationFlags, IMPORTANCE_DEFAULT);
+    }
+
+    private ExpandableNotificationRow generateRow(
+            Notification notification,
+            String pkg,
+            int uid,
+            @InflationFlag int extraInflationFlags,
+            int importance)
             throws Exception {
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 mContext.LAYOUT_INFLATER_SERVICE);
@@ -238,13 +264,12 @@
                 mUser,
                 null /* overrideGroupKey */,
                 System.currentTimeMillis());
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.setRow(row);
         entry.createIcons(mContext, sbn);
         entry.channel = new NotificationChannel(
-                notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
+                notification.getChannelId(), notification.getChannelId(), importance);
         entry.channel.setBlockableSystem(true);
-        entry.setIsBubble(isBubble);
         row.setEntry(entry);
         row.getNotificationInflater().addInflationFlags(extraInflationFlags);
         NotificationInflaterTest.runThenWaitForInflation(
@@ -257,4 +282,14 @@
         mGroupManager.onEntryAdded(entry);
         return row;
     }
+
+    private Notification.BubbleMetadata makeBubbleMetadata() {
+        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        return new Notification.BubbleMetadata.Builder()
+                .setIntent(bubbleIntent)
+                .setTitle("bubble title")
+                .setIcon(Icon.createWithResource(mContext, 1))
+                .setDesiredHeight(314)
+                .build();
+    }
 }
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 8cf4b05..56e1fc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -36,12 +36,11 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-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.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -77,7 +76,7 @@
     @Mock private ShadeController mShadeController;
 
     private NotificationViewHierarchyManager mViewHierarchyManager;
-    private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);
+    private NotificationTestHelper mHelper;
 
     @Before
     public void setUp() {
@@ -90,19 +89,21 @@
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
+        mHelper = new NotificationTestHelper(mContext);
+
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
         mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
                 mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
-                mock(StatusBarStateController.class), mEntryManager, mock(BubbleController.class),
+                mock(StatusBarStateController.class), mEntryManager,
                 () -> mShadeController);
         Dependency.get(InitController.class).executePostInitTasks();
         mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
     }
 
-    private NotificationData.Entry createEntry() throws Exception {
+    private NotificationEntry createEntry() throws Exception {
         ExpandableNotificationRow row = mHelper.createRow();
-        NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
+        NotificationEntry entry = new NotificationEntry(row.getStatusBarNotification());
         entry.setRow(row);
         return entry;
     }
@@ -111,9 +112,9 @@
     public void testNotificationsBecomingBundled() throws Exception {
         // Tests 3 top level notifications becoming a single bundled notification with |entry0| as
         // the summary.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
-        NotificationData.Entry entry2 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
+        NotificationEntry entry2 = createEntry();
 
         // Set up the prior state to look like three top level notifications.
         mListContainer.addContainerView(entry0.getRow());
@@ -140,9 +141,9 @@
     @Test
     public void testNotificationsBecomingUnbundled() throws Exception {
         // Tests a bundled notification becoming three top level notifications.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
-        NotificationData.Entry entry2 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
+        NotificationEntry entry2 = createEntry();
         entry0.getRow().addChildNotification(entry1.getRow());
         entry0.getRow().addChildNotification(entry2.getRow());
 
@@ -171,8 +172,8 @@
     @Test
     public void testNotificationsBecomingSuppressed() throws Exception {
         // Tests two top level notifications becoming a suppressed summary and a child.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
         entry0.getRow().addChildNotification(entry1.getRow());
 
         // Set up the prior state to look like a top level notification.
@@ -197,7 +198,7 @@
 
     @Test
     public void testUpdateNotificationViews_appOps() throws Exception {
-        NotificationData.Entry entry0 = createEntry();
+        NotificationEntry entry0 = createEntry();
         entry0.setRow(spy(entry0.getRow()));
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0));
@@ -262,10 +263,7 @@
         public void setMaxDisplayedNotifications(int maxNotifications) {}
 
         @Override
-        public void snapViewIfNeeded(Entry entry) {}
-
-        @Override
-        public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+        public ViewGroup getViewParentForNotification(NotificationEntry entry) {
             return null;
         }
 
@@ -281,10 +279,10 @@
         }
 
         @Override
-        public void cleanUpViewStateForEntry(Entry entry) { }
+        public void cleanUpViewStateForEntry(NotificationEntry entry) { }
 
         @Override
-        public boolean isInVisibleLocation(Entry entry) {
+        public boolean isInVisibleLocation(NotificationEntry entry) {
             return true;
         }
 
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 e7a1f05..6cca434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -36,8 +36,8 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.Before;
@@ -58,7 +58,7 @@
     private static final int TEST_ACTION_COUNT = 3;
 
     private Notification mNotification;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private SmartReplyController mSmartReplyController;
     private NotificationRemoteInputManager mRemoteInputManager;
 
@@ -92,7 +92,7 @@
 
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
     }
 
     @Test
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 57c9d29..9ce6ae1 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
@@ -22,18 +22,15 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
-import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -48,7 +45,6 @@
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.util.ArraySet;
 import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
@@ -65,7 +61,9 @@
 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.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
@@ -76,12 +74,11 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
-import junit.framework.Assert;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -121,8 +118,9 @@
     @Mock private MetricsLogger mMetricsLogger;
     @Mock private SmartReplyController mSmartReplyController;
     @Mock private RowInflaterTask mAsyncInflationTask;
+    @Mock private NotificationRowBinder mMockedRowBinder;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private TestableNotificationEntryManager mEntryManager;
     private CountDownLatch mCountDownLatch;
@@ -135,8 +133,12 @@
             mCountDownLatch = new CountDownLatch(1);
         }
 
+        public void setNotificationData(NotificationData data) {
+            mNotificationData = data;
+        }
+
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry,
+        public void onAsyncInflationFinished(NotificationEntry entry,
                 @NotificationInflater.InflationFlag int inflatedFlags) {
             super.onAsyncInflationFinished(entry, inflatedFlags);
 
@@ -221,7 +223,7 @@
                 .setContentText("Text");
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
         mEntryManager = new TestableNotificationEntryManager(mContext);
@@ -258,10 +260,10 @@
         verify(mEntryListener, never()).onInflationError(any(), any());
 
         // Row inflation:
-        ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass(
-                NotificationData.Entry.class);
+        ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
+                NotificationEntry.class);
         verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
-        NotificationData.Entry entry = entryCaptor.getValue();
+        NotificationEntry entry = entryCaptor.getValue();
         verify(mRemoteInputManager).bindRow(entry.getRow());
 
         // Row content inflation:
@@ -290,14 +292,43 @@
 
         verify(mEntryListener, never()).onInflationError(any(), any());
 
+        verify(mEntryListener).onPreEntryUpdated(mEntry);
         verify(mPresenter).updateNotificationViews();
-        verify(mEntryListener).onEntryUpdated(mEntry);
+        verify(mEntryListener).onPostEntryUpdated(mEntry);
+
         assertNotNull(mEntry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
     }
 
     @Test
+    public void testUpdateNotification_prePostEntryOrder() throws Exception {
+        com.android.systemui.util.Assert.isNotMainThread();
+        TestableLooper.get(this).processAllMessages();
+
+        NotificationData notifData = mock(NotificationData.class);
+        when(notifData.get(mEntry.key)).thenReturn(mEntry);
+
+        mEntryManager.setNotificationData(notifData);
+
+        mEntryManager.updateNotification(mSbn, mRankingMap);
+        TestableLooper.get(this).processMessages(1);
+        // Wait for content update.
+        assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
+
+        verify(mEntryListener, never()).onInflationError(any(), any());
+
+        // Ensure that update callbacks happen in correct order
+        InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener);
+        order.verify(mEntryListener).onPreEntryUpdated(mEntry);
+        order.verify(notifData).filterAndSort();
+        order.verify(mPresenter).updateNotificationViews();
+        order.verify(mEntryListener).onPostEntryUpdated(mEntry);
+
+        assertNotNull(mEntry.getRow());
+    }
+
+    @Test
     public void testRemoveNotification() throws Exception {
         com.android.systemui.util.Assert.isNotMainThread();
 
@@ -308,10 +339,9 @@
 
         verify(mEntryListener, never()).onInflationError(any(), any());
 
-        verify(mListContainer).cleanUpViewStateForEntry(mEntry);
         verify(mPresenter).updateNotificationViews();
-        verify(mEntryListener).onEntryRemoved(mEntry, mSbn.getKey(), mSbn,
-                null, false /* lifetimeExtended */, false /* removedByUser */);
+        verify(mEntryListener).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
         verify(mRow).setRemoved();
 
         assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
@@ -335,92 +365,31 @@
 
         assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
         verify(extender).setShouldManageLifetime(mEntry, true /* shouldManage */);
-        verify(mEntryListener).onEntryRemoved(mEntry, mSbn.getKey(), null,
-                null, true /* lifetimeExtended */, false /* removedByUser */);
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
     }
 
     @Test
-    public void testUpdateAppOps_foregroundNoti() {
+    public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
         com.android.systemui.util.Assert.isNotMainThread();
 
-        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
-                .thenReturn(mEntry.key);
-        mEntry.setRow(mRow);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.removeNotification("not_a_real_key", mRankingMap);
 
-        mEntryManager.updateNotificationsForAppOp(
-                AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
-                mEntry.notification.getPackageName(), true);
-
-        verify(mPresenter, times(1)).updateNotificationViews();
-        assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains(
-                AppOpsManager.OP_CAMERA));
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
     }
 
     @Test
-    public void testUpdateAppOps_otherNoti() {
+    public void testRemoveNotification_whilePending() throws InterruptedException {
         com.android.systemui.util.Assert.isNotMainThread();
 
-        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
-                .thenReturn(null);
-        mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
+        mEntryManager.setRowBinder(mMockedRowBinder);
 
-        verify(mPresenter, never()).updateNotificationViews();
-    }
+        mEntryManager.addNotification(mSbn, mRankingMap);
+        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
 
-    @Test
-    public void testAddNotificationExistingAppOps() {
-        mEntry.setRow(mRow);
-        mEntryManager.getNotificationData().add(mEntry);
-        ArraySet<Integer> expected = new ArraySet<>();
-        expected.add(3);
-        expected.add(235);
-        expected.add(1);
-
-        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(expected);
-        when(mForegroundServiceController.getStandardLayoutKey(
-                mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
-
-        mEntryManager.tagForeground(mEntry.notification);
-
-        Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size());
-        for (int op : expected) {
-            assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op));
-        }
-    }
-
-    @Test
-    public void testAdd_noExistingAppOps() {
-        mEntry.setRow(mRow);
-        mEntryManager.getNotificationData().add(mEntry);
-        when(mForegroundServiceController.getStandardLayoutKey(
-                mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
-        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(null);
-
-        mEntryManager.tagForeground(mEntry.notification);
-        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
-    }
-
-    @Test
-    public void testAdd_existingAppOpsNotForegroundNoti() {
-        mEntry.setRow(mRow);
-        mEntryManager.getNotificationData().add(mEntry);
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(3);
-        ops.add(235);
-        ops.add(1);
-        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(ops);
-        when(mForegroundServiceController.getStandardLayoutKey(
-                mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn("something else");
-
-        mEntryManager.tagForeground(mEntry.notification);
-        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index da8bc01d..387475f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -40,6 +40,8 @@
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -191,12 +193,12 @@
 
         // test should filter out hidden notifications:
         // hidden
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.suspended = true;
         assertTrue(mNotificationFilter.shouldFilterOut(entry));
 
         // not hidden
-        entry = new NotificationData.Entry(mMockStatusBarNotification);
+        entry = new NotificationEntry(mMockStatusBarNotification);
         entry.suspended = false;
         assertFalse(mNotificationFilter.shouldFilterOut(entry));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
new file mode 100644
index 0000000..4b5037b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2019 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.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArraySet;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationListControllerTest extends SysuiTestCase {
+    private NotificationListController mController;
+
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationListContainer mListContainer;
+    @Mock private ForegroundServiceController mForegroundServiceController;
+    @Mock private DeviceProvisionedController mDeviceProvisionedController;
+
+    @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
+    @Captor private ArgumentCaptor<DeviceProvisionedListener> mProvisionedCaptor;
+
+    private NotificationEntryListener mEntryListener;
+    private DeviceProvisionedListener mProvisionedListener;
+
+    // TODO: Remove this once EntryManager no longer needs to be mocked
+    private NotificationData mNotificationData = new NotificationData();
+
+    private int mNextNotifId = 0;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
+
+        mController = new NotificationListController(
+                mEntryManager,
+                mListContainer,
+                mForegroundServiceController,
+                mDeviceProvisionedController);
+        mController.bind();
+
+        // Capture callbacks passed to mocks
+        verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
+        mEntryListener = mEntryListenerCaptor.getValue();
+        verify(mDeviceProvisionedController).addCallback(mProvisionedCaptor.capture());
+        mProvisionedListener = mProvisionedCaptor.getValue();
+    }
+
+    @Test
+    public void testCleanUpViewStateOnEntryRemoved() {
+        final NotificationEntry entry = buildEntry();
+        mEntryListener.onEntryRemoved(
+                entry,
+                NotificationVisibility.obtain(entry.key, 0, 0, true),
+                false);
+        verify(mListContainer).cleanUpViewStateForEntry(entry);
+    }
+
+    @Test
+    public void testCallUpdateNotificationsOnDeviceProvisionedChange() {
+        mProvisionedListener.onDeviceProvisionedChanged();
+        verify(mEntryManager).updateNotifications();
+    }
+
+    @Test
+    public void testAppOps_appOpAddedToForegroundNotif() {
+        // GIVEN a notification associated with a foreground service
+        final NotificationEntry entry = buildEntry();
+        mNotificationData.add(entry);
+        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+                .thenReturn(entry.key);
+
+        // WHEN we are notified of a new app op
+        mController.updateNotificationsForAppOp(
+                AppOpsManager.OP_CAMERA,
+                entry.notification.getUid(),
+                entry.notification.getPackageName(),
+                true);
+
+        // THEN the app op is added to the entry
+        assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+        // THEN updateNotifications() is called
+        verify(mEntryManager, times(1)).updateNotifications();
+    }
+
+    @Test
+    public void testAppOps_appOpAddedToUnrelatedNotif() {
+        // GIVEN No current foreground notifs
+        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+                .thenReturn(null);
+
+        // WHEN An unrelated notification gets a new app op
+        mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
+
+        // THEN We never call updateNotifications()
+        verify(mEntryManager, never()).updateNotifications();
+    }
+
+    @Test
+    public void testAppOps_addNotificationWithExistingAppOps() {
+        // GIVEN a notification with three associated app ops that is associated with a foreground
+        // service
+        final NotificationEntry entry = buildEntry();
+        mNotificationData.add(entry);
+        ArraySet<Integer> expected = new ArraySet<>();
+        expected.add(3);
+        expected.add(235);
+        expected.add(1);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                entry.notification.getUserId(),
+                entry.notification.getPackageName())).thenReturn(entry.key);
+        when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
+                entry.notification.getPackageName())).thenReturn(expected);
+
+        // WHEN the notification is added
+        mEntryListener.onBeforeNotificationAdded(entry);
+
+        // THEN the entry is tagged with all three app ops
+        assertEquals(expected.size(), entry.mActiveAppOps.size());
+        for (int op : expected) {
+            assertTrue("Entry missing op " + op, entry.mActiveAppOps.contains(op));
+        }
+    }
+
+    @Test
+    public void testAdd_addNotificationWithNoExistingAppOps() {
+        // GIVEN a notification with NO associated app ops
+        final NotificationEntry entry = buildEntry();
+
+        mNotificationData.add(entry);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                entry.notification.getUserId(),
+                entry.notification.getPackageName())).thenReturn(entry.key);
+        when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
+                entry.notification.getPackageName())).thenReturn(null);
+
+        // WHEN the notification is added
+        mEntryListener.onBeforeNotificationAdded(entry);
+
+        // THEN the entry doesn't have any app ops associated with it
+        assertEquals(0, entry.mActiveAppOps.size());
+    }
+
+    @Test
+    public void testAdd_addNonForegroundNotificationWithExistingAppOps() {
+        // GIVEN a notification with app ops that isn't associated with a foreground service
+        final NotificationEntry entry = buildEntry();
+        mNotificationData.add(entry);
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(3);
+        ops.add(235);
+        ops.add(1);
+        when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
+                entry.notification.getPackageName())).thenReturn(ops);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                entry.notification.getUserId(),
+                entry.notification.getPackageName())).thenReturn("something else");
+
+        // WHEN the notification is added
+        mEntryListener.onBeforeNotificationAdded(entry);
+
+        // THEN the entry doesn't have any app ops associated with it
+        assertEquals(0, entry.mActiveAppOps.size());
+    }
+
+    private NotificationEntry buildEntry() {
+        mNextNotifId++;
+
+        Notification.Builder n = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text");
+
+        StatusBarNotification notification =
+                new StatusBarNotification(
+                        TEST_PACKAGE_NAME,
+                        TEST_PACKAGE_NAME,
+                        mNextNotifId,
+                        null,
+                        TEST_UID,
+                        0,
+                        n.build(),
+                        new UserHandle(ActivityManager.getCurrentUser()),
+                        null,
+                        0);
+        return new NotificationEntry(notification);
+    }
+
+    private static final String TEST_PACKAGE_NAME = "test";
+    private static final int TEST_UID = 0;
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index 6fbd8c7..813fc9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -29,6 +29,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Before;
@@ -44,13 +45,13 @@
     private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class);
     private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class);
     private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
 
     @Before
     public void setUp() {
         mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class));
         mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
-        mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
+        mEntry = new NotificationEntry(mock(StatusBarNotification.class));
         mEntry.setRow(mRow);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 871ff89..193f483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.collection;
 
 import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
 import static android.app.AppOpsManager.OP_CAMERA;
@@ -59,7 +59,9 @@
 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.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -203,7 +205,7 @@
                 mRow.getEntry().notification)).thenReturn(false);
         when(mEnvironment.isNotificationForCurrentProfiles(
                 row2.getEntry().notification)).thenReturn(true);
-        ArrayList<NotificationData.Entry> result =
+        ArrayList<NotificationEntry> result =
                 mNotificationData.getNotificationsForCurrentUser();
 
         assertEquals(result.size(), 1);
@@ -217,7 +219,7 @@
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
         Notification n = mMockStatusBarNotification.getNotification();
         n.flags = Notification.FLAG_FOREGROUND_SERVICE;
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         mNotificationData.add(entry);
 
         assertTrue(entry.isExemptFromDndVisualSuppression());
@@ -234,7 +236,7 @@
         nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
         n = nb.build();
         when(mMockStatusBarNotification.getNotification()).thenReturn(n);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         mNotificationData.add(entry);
 
         assertTrue(entry.isExemptFromDndVisualSuppression());
@@ -246,7 +248,7 @@
         initStatusBarNotification(false);
         when(mMockStatusBarNotification.getKey()).thenReturn(
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
         mNotificationData.add(entry);
 
@@ -259,7 +261,7 @@
         initStatusBarNotification(false);
         when(mMockStatusBarNotification.getKey()).thenReturn(
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
         mNotificationData.add(entry);
 
@@ -313,8 +315,8 @@
         snoozeCriterions.add(snoozeCriterion);
         when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions);
 
-        NotificationData.Entry entry =
-                new NotificationData.Entry(mMockStatusBarNotification, ranking);
+        NotificationEntry entry =
+                new NotificationEntry(mMockStatusBarNotification, ranking);
 
         assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
         assertEquals(NOTIFICATION_CHANNEL, entry.channel);
@@ -343,7 +345,7 @@
         StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
                 notification, mContext.getUser(), "", 0);
 
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.setHasSentReply();
 
         assertTrue(entry.isLastMessageFromReply());
@@ -400,8 +402,7 @@
                 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
                 title,
                 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
-                        .setSemanticAction(
-                                Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+                        .setContextual(true)
                         .build();
     }
 
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 983ca83..6472589 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
@@ -41,9 +41,10 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -77,7 +78,7 @@
     @Mock private NotificationListener mListener;
     @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private TestableNotificationLogger mLogger;
     private NotificationEntryListener mNotificationEntryListener;
     private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
@@ -93,7 +94,7 @@
         StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
                 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
-        mEntry = new NotificationData.Entry(sbn);
+        mEntry = new NotificationEntry(sbn);
         mEntry.setRow(mRow);
 
         mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class),
@@ -159,11 +160,6 @@
         verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
     }
 
-    @Test
-    public void testHandleNullEntryOnEntryRemoved() {
-        mNotificationEntryListener.onEntryRemoved(null, "foobar", null, null, false, false);
-    }
-
     private class TestableNotificationLogger extends NotificationLogger {
 
         TestableNotificationLogger(NotificationListener notificationListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 82b42c5..d94bf84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -47,7 +47,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 
 import org.junit.Before;
@@ -73,7 +73,7 @@
     StatusBarNotification mStatusBarNotification;
     @Mock
     Notification mNotification;
-    NotificationData.Entry mEntry;
+    NotificationEntry mEntry;
     @Mock
     RemoteInput mRemoteInput;
     @Mock
@@ -107,7 +107,7 @@
         // Smart replies and actions
         when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
         when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
-        mEntry = new NotificationData.Entry(mStatusBarNotification);
+        mEntry = new NotificationEntry(mStatusBarNotification);
         when(mSmartReplyConstants.isEnabled()).thenReturn(true);
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
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 ad43bea..0899c73 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
@@ -60,7 +60,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -183,8 +183,8 @@
         when(row.getGuts()).thenReturn(guts);
         doNothing().when(row).inflateGuts();
 
-        NotificationData.Entry realEntry = realRow.getEntry();
-        NotificationData.Entry entry = spy(realEntry);
+        NotificationEntry realEntry = realRow.getEntry();
+        NotificationEntry entry = spy(realEntry);
 
         when(entry.getRow()).thenReturn(row);
         when(entry.getGuts()).thenReturn(guts);
@@ -443,7 +443,7 @@
         NotificationGuts guts = new NotificationGuts(mContext);
         ExpandableNotificationRow row = spy(createTestNotificationRow());
         doReturn(guts).when(row).getGuts();
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         entry.setRow(row);
         mGutsManager.setExposedGuts(guts);
 
@@ -452,7 +452,7 @@
 
     @Test
     public void testSetShouldManageLifetime_setShouldManage() {
-        NotificationData.Entry entry = createTestNotificationRow().getEntry();
+        NotificationEntry entry = createTestNotificationRow().getEntry();
         mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */);
 
         assertTrue(entry.key.equals(mGutsManager.mKeyToRemoveOnGutsClosed));
@@ -460,7 +460,7 @@
 
     @Test
     public void testSetShouldManageLifetime_setShouldNotManage() {
-        NotificationData.Entry entry = createTestNotificationRow().getEntry();
+        NotificationEntry entry = createTestNotificationRow().getEntry();
         mGutsManager.mKeyToRemoveOnGutsClosed = entry.key;
         mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */);
 
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 d6b706d..648df3c 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
@@ -48,7 +48,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -88,7 +88,7 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry,
+            public void onAsyncInflationFinished(NotificationEntry entry,
                     @NotificationInflater.InflationFlag int inflatedFlags) {
             }
         });
@@ -174,7 +174,7 @@
                     }
 
                     @Override
-                    public void onAsyncInflationFinished(NotificationData.Entry entry,
+                    public void onAsyncInflationFinished(NotificationEntry entry,
                             @NotificationInflater.InflationFlag int inflatedFlags) {
                         countDownLatch.countDown();
                     }
@@ -256,7 +256,7 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry,
+            public void onAsyncInflationFinished(NotificationEntry entry,
                     @NotificationInflater.InflationFlag int inflatedFlags) {
                 if (expectingException) {
                     exceptionHolder.setException(new RuntimeException(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index ecb0cf8..f6791dd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -45,7 +45,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
@@ -73,6 +72,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -498,12 +498,15 @@
     }
 
     @Test
-    public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
+    public void testLogBlockingHelperCounter_logGutsViewDisplayed() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
                 IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
-        verifyZeroInteractions(mMetricsLogger);
+        verify(mMetricsLogger).write(argThat(logMaker ->
+                logMaker.getType() == MetricsEvent.NOTIFICATION_BLOCKING_HELPER
+                        && logMaker.getSubtype() == MetricsEvent.BLOCKING_HELPER_DISPLAY
+        ));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index e4d0196..04d7509 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -35,7 +35,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -54,7 +54,7 @@
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mRow = mock(ExpandableNotificationRow.class);
-        NotificationData.Entry entry = new NotificationData.Entry(
+        NotificationEntry entry = new NotificationEntry(
                 mock(StatusBarNotification.class));
         entry.channel = mock(NotificationChannel.class);
         when(mRow.getEntry()).thenReturn(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 8ae7d52..ff30a4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -29,7 +29,10 @@
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
@@ -37,6 +40,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.HashSet;
 
@@ -45,14 +50,18 @@
 @RunWithLooper
 public class NotificationRoundnessManagerTest extends SysuiTestCase {
 
-    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private NotificationRoundnessManager mRoundnessManager;
     private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>();
     private Runnable mRoundnessCallback = mock(Runnable.class);
     private ExpandableNotificationRow mFirst;
     private ExpandableNotificationRow mSecond;
+    @Mock
+    private AmbientPulseManager mAmbientPulseManager;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mRoundnessManager = new NotificationRoundnessManager(mAmbientPulseManager);
         com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
         mFirst = testHelper.createRow();
@@ -127,6 +136,27 @@
     }
 
     @Test
+    public void testRoundnessPulsing() throws Exception {
+        // Let's create a notification that's neither the first or last item of the stack,
+        // this way we'll ensure that it won't have any rounded corners by default.
+        mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
+                createSection(mFirst, mSecond),
+                createSection(null, null)
+        });
+        ExpandableNotificationRow row = new NotificationTestHelper(getContext()).createRow();
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.getRow()).thenReturn(row);
+
+        mRoundnessManager.onAmbientStateChanged(entry, true);
+        Assert.assertEquals(1f, row.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1f, row.getCurrentTopRoundness(), 0.0f);
+
+        mRoundnessManager.onAmbientStateChanged(entry, false);
+        Assert.assertEquals(0f, row.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0f, row.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
     public void testRoundnessSetOnSecondSectionLast() {
         mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
                 createSection(mFirst, mFirst),
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 06be5f6..736f384 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
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.atLeastOnce;
@@ -35,22 +36,21 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.Looper;
-import android.os.PowerManager;
+import android.metrics.LogMaker;
 import android.provider.Settings;
-import android.service.dreams.IDreamManager;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
 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.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -58,9 +58,9 @@
 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.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -78,6 +78,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -91,7 +92,8 @@
 @RunWith(AndroidJUnit4.class)
 public class NotificationStackScrollLayoutTest extends SysuiTestCase {
 
-    private NotificationStackScrollLayout mStackScroller;
+    private NotificationStackScrollLayout mStackScroller;  // Normally test this
+    private NotificationStackScrollLayout mStackScrollerInternal;  // See explanation below
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
     @Mock private StatusBar mBar;
@@ -104,11 +106,12 @@
     @Mock private NotificationData mNotificationData;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
-    @Mock private IDreamManager mDreamManager;
-    private PowerManager mPowerManager;
+    @Mock private MetricsLogger mMetricsLogger;
+    @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
     private TestableNotificationEntryManager mEntryManager;
     private int mOriginalInterruptionModelSetting;
 
+
     @Before
     @UiThreadTest
     public void setUp() throws Exception {
@@ -117,17 +120,13 @@
                 NotificationBlockingHelperManager.class,
                 mBlockingHelperManager);
         mDependency.injectTestDependency(StatusBarStateController.class, mBarState);
+        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mDependency.injectTestDependency(NotificationRemoteInputManager.class,
                 mRemoteInputManager);
         mDependency.injectMockDependency(ShadeController.class);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
-        IPowerManager powerManagerService = mock(IPowerManager.class);
-        mPowerManager = new PowerManager(mContext, powerManagerService,
-                Handler.createAsync(Looper.myLooper()));
-
-        mEntryManager = new TestableNotificationEntryManager(mPowerManager,
-                mContext);
+        mEntryManager = new TestableNotificationEntryManager(mContext);
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager,
@@ -135,8 +134,15 @@
 
 
         NotificationShelf notificationShelf = mock(NotificationShelf.class);
-        mStackScroller = spy(new NotificationStackScrollLayout(getContext(), null,
-                true /* allowLongPress */));
+
+        // The actual class under test.  You may need to work with this class directly when
+        // testing anonymous class members of mStackScroller, like mMenuEventListener,
+        // which refer to members of NotificationStackScrollLayout. The spy
+        // holds a copy of the CUT's instances of these classes, so they still refer to the CUT's
+        // member variables, not the spy's member variables.
+        mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null,
+                true /* allowLongPress */, mNotificationRoundnessManager);
+        mStackScroller = spy(mStackScrollerInternal);
         mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setScrimController(mock(ScrimController.class));
@@ -154,6 +160,7 @@
         doNothing().when(mGroupManager).collapseAllGroups();
         doNothing().when(mExpandHelper).cancelImmediately();
         doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+        doNothing().when(notificationShelf).fadeInTranslating();
 
         mOriginalInterruptionModelSetting = Settings.Secure.getInt(mContext.getContentResolver(),
                 NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
@@ -277,8 +284,8 @@
     @Test
     public void testUpdateFooter_remoteInput() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mNotificationData.getActiveNotifications()).thenReturn(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -294,8 +301,8 @@
     @Test
     public void testUpdateFooter_oneClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mNotificationData.getActiveNotifications()).thenReturn(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -310,8 +317,8 @@
     @Test
     public void testUpdateFooter_oneNonClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
         assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
 
@@ -326,7 +333,7 @@
 
         // add notification
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        NotificationData.Entry entry = mock(NotificationData.Entry.class);
+        NotificationEntry entry = mock(NotificationEntry.class);
         when(row.getEntry()).thenReturn(entry);
         when(entry.isClearable()).thenReturn(true);
         mStackScroller.addContainerView(row);
@@ -433,6 +440,63 @@
         assertNull(swipeActionHelper.getExposedMenuView());
     }
 
+    class LogMatcher implements ArgumentMatcher<LogMaker> {
+        private int mCategory, mType;
+
+        LogMatcher(int category, int type) {
+            mCategory = category;
+            mType = type;
+        }
+        public boolean matches(LogMaker l) {
+            return (l.getCategory() == mCategory)
+                    && (l.getType() == mType);
+        }
+
+        public String toString() {
+            return String.format("LogMaker(%d, %d)", mCategory, mType);
+        }
+    }
+
+    private LogMaker logMatcher(int category, int type) {
+        return argThat(new LogMatcher(category, type));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testOnMenuClickedLogging() {
+        // Set up the object under test to have a valid mLongPressListener.  We're testing an
+        // anonymous-class member, mMenuEventListener, so we need to modify the state of the
+        // class itself, not the Mockito spy copied from it.  See notes in setup.
+        mStackScrollerInternal.setLongPressListener(
+                mock(ExpandableNotificationRow.LongPressListener.class));
+
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
+        when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker(
+                MetricsProto.MetricsEvent.VIEW_UNKNOWN));
+
+        mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock(
+                NotificationMenuRowPlugin.MenuItem.class));
+        verify(row.getStatusBarNotification()).getLogMaker();  // This writes most of the log data
+        verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR,
+                MetricsProto.MetricsEvent.TYPE_ACTION));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testOnMenuShownLogging() {
+        // Set up the object under test to have a valid mHeadsUpManager. See notes in setup.
+        mStackScrollerInternal.setHeadsUpManager(mHeadsUpManager);
+
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
+        when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker(
+                MetricsProto.MetricsEvent.VIEW_UNKNOWN));
+
+        mStackScroller.mMenuEventListener.onMenuShown(row);
+        verify(row.getStatusBarNotification()).getLogMaker();  // This writes most of the log data
+        verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR,
+                MetricsProto.MetricsEvent.TYPE_ACTION));
+    }
+
     private void setBarStateForTest(int 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.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index add8838..a72be7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -57,13 +57,15 @@
     @Before
     public void setup() {
         mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+        StatusBar statusBar = mock(StatusBar.class);
+        mSysuiContext.putComponent(StatusBar.class, statusBar);
         mSysuiContext.putComponent(TunerService.class, mock(TunerService.class));
         mStatusBarStateController = mDependency
                 .injectMockDependency(StatusBarStateController.class);
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
         mNotificationAreaInner = mock(View.class);
+        when(statusBar.getPanel()).thenReturn(mock(NotificationPanelView.class));
         when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class));
         when(mMockNotificiationAreaController.getNotificationInnerAreaView()).thenReturn(
                 mNotificationAreaInner);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 44deb10..9ceb325 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -16,15 +16,20 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.view.View;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.View;
 
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -34,11 +39,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.mockito.Mockito.when;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -96,7 +96,7 @@
 
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
-        NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
+        NotificationEntry laterEntry = new NotificationEntry(createNewNotification(1));
         laterEntry.setRow(mRow);
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.showNotification(laterEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 27ed9c5..cd52e87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -47,7 +47,6 @@
     private float mPanelExpansion;
     private int mKeyguardStatusHeight;
     private float mDark;
-    private boolean mPulsing;
 
     @Before
     public void setUp() {
@@ -208,20 +207,6 @@
     }
 
     @Test
-    public void notifPositionWhilePulsingOnAOD() {
-        // GIVEN on AOD and pulsing
-        givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
-        mKeyguardStatusHeight = EMPTY_HEIGHT;
-        mPulsing = true;
-        mClockPositionAlgorithm.setPulsingPadding(200);
-        // WHEN the clock position algorithm is run
-        positionClock();
-        // THEN the notif padding doesn't adjust for pulsing.
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
-    }
-
-    @Test
     public void notifPositionMiddleOfScreenOnLockScreen() {
         // GIVEN on lock screen and both stack scroll and clock have 0 height
         givenLockScreen();
@@ -307,20 +292,6 @@
         assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
     }
 
-    @Test
-    public void notifPositionWhilePulsingOnLockScreen() {
-        // GIVEN on lock screen and pulsing
-        givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
-        mKeyguardStatusHeight = EMPTY_HEIGHT;
-        mPulsing = true;
-        mClockPositionAlgorithm.setPulsingPadding(200);
-        // WHEN the clock position algorithm is run
-        positionClock();
-        // THEN the notif padding adjusts for pulsing.
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1200);
-    }
-
     private void givenAOD() {
         mPanelExpansion = 1.f;
         mDark = 1.f;
@@ -334,7 +305,7 @@
     private void positionClock() {
         mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
                 mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED,
-                mPulsing, ZERO_DRAG);
+                ZERO_DRAG);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 56af400..cefd4ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -32,10 +32,9 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -64,7 +63,7 @@
     @Captor
     private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
     private NotificationEntryListener mNotificationEntryListener;
-    private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
+    private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
     private final NotificationGroupTestHelper mGroupTestHelper =
             new NotificationGroupTestHelper(mContext);
 
@@ -94,9 +93,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransfersToChild() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
 
         // Summary will be suppressed because there is only one child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -109,11 +108,11 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -133,11 +132,11 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -158,9 +157,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
 
@@ -176,9 +175,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
 
@@ -196,13 +195,13 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -226,9 +225,9 @@
 
     @Test
     public void testCleanUpPendingAlertInfo() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
@@ -237,17 +236,16 @@
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        mNotificationEntryListener.onEntryRemoved(childEntry, childEntry.key, null, null,
-                false, false);
+        mNotificationEntryListener.onEntryRemoved(childEntry, null, false);
 
         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
     }
 
     @Test
     public void testUpdateGroupChangeDoesNotTransfer() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
@@ -268,9 +266,9 @@
 
     @Test
     public void testUpdateChildToSummaryDoesNotTransfer() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index b0bd1fb..ecb3e4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -29,7 +29,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -69,8 +69,8 @@
 
     @Test
     public void testIsOnlyChildInGroup() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -80,8 +80,8 @@
 
     @Test
     public void testIsChildInGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -92,8 +92,8 @@
 
     @Test
     public void testIsSummaryOfGroupWithChildren() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -105,8 +105,8 @@
 
     @Test
     public void testRemoveChildFromGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -118,8 +118,8 @@
 
     @Test
     public void testRemoveSummaryFromGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -132,8 +132,8 @@
 
     @Test
     public void testHeadsUpEntryIsIsolated() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -149,8 +149,8 @@
 
     @Test
     public void testAmbientPulseEntryIsIsolated() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 7ad68eb..e6b9778 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -27,7 +27,7 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -44,23 +44,23 @@
         mContext = context;
     }
 
-    public NotificationData.Entry createSummaryNotification() {
+    public NotificationEntry createSummaryNotification() {
         return createSummaryNotification(Notification.GROUP_ALERT_ALL);
     }
 
-    public NotificationData.Entry createSummaryNotification(int groupAlertBehavior) {
+    public NotificationEntry createSummaryNotification(int groupAlertBehavior) {
         return createEntry(true, groupAlertBehavior);
     }
 
-    public NotificationData.Entry createChildNotification() {
+    public NotificationEntry createChildNotification() {
         return createChildNotification(Notification.GROUP_ALERT_ALL);
     }
 
-    public NotificationData.Entry createChildNotification(int groupAlertBehavior) {
+    public NotificationEntry createChildNotification(int groupAlertBehavior) {
         return createEntry(false, groupAlertBehavior);
     }
 
-    public NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) {
+    public NotificationEntry createEntry(boolean isSummary, int groupAlertBehavior) {
         Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("Title")
                 .setSmallIcon(R.drawable.ic_person)
@@ -79,7 +79,7 @@
                 new UserHandle(ActivityManager.getCurrentUser()),
                 null /* overrideGroupKey */,
                 0 /* postTime */);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
new file mode 100644
index 0000000..b7b95ef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationPanelViewTest extends SysuiTestCase {
+
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private NotificationStackScrollLayout mNotificationStackScrollLayout;
+    @Mock
+    private KeyguardStatusView mKeyguardStatusView;
+    private NotificationPanelView mNotificationPanelView;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mDependency.injectTestDependency(StatusBarStateController.class,
+                mStatusBarStateController);
+        mDependency.injectMockDependency(ShadeController.class);
+        mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
+        mDependency.injectMockDependency(ConfigurationController.class);
+        mDependency.injectMockDependency(ZenModeController.class);
+        mNotificationPanelView = new TestableNotificationPanelView();
+    }
+
+    @Test
+    public void testSetDozing_notifiesNsslAndStateController() {
+        mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+        InOrder inOrder = inOrder(mNotificationStackScrollLayout, mStatusBarStateController);
+        inOrder.verify(mNotificationStackScrollLayout).setDark(eq(true), eq(true), eq(null));
+        inOrder.verify(mNotificationStackScrollLayout).setShowDarkShelf(eq(true));
+        inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true));
+    }
+
+    @Test
+    public void testSetDozing_showsDarkShelfWithDefaultClock() {
+        when(mKeyguardStatusView.hasCustomClock()).thenReturn(false);
+        mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+        verify(mNotificationStackScrollLayout).setShowDarkShelf(eq(true));
+    }
+
+    @Test
+    public void testSetDozing_hidesDarkShelfWhenCustomClock() {
+        when(mKeyguardStatusView.hasCustomClock()).thenReturn(true);
+        mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+        verify(mNotificationStackScrollLayout).setShowDarkShelf(eq(false));
+    }
+
+    private class TestableNotificationPanelView extends NotificationPanelView {
+        TestableNotificationPanelView() {
+            super(NotificationPanelViewTest.this.mContext, null);
+            mNotificationStackScroller = mNotificationStackScrollLayout;
+            mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index 2805908..382dde9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -97,6 +97,7 @@
         doReturn(HIT_TARGET_NONE).when(mNavigationBarView).getDownHitTarget();
         doReturn(backButton).when(mNavigationBarView).getBackButton();
         doReturn(mResources).when(mNavigationBarView).getResources();
+        doReturn(mContext).when(mNavigationBarView).getContext();
 
         mController = new QuickStepController(mContext);
         mController.setComponents(mNavigationBarView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 146c5d6..c20d37f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -45,6 +45,7 @@
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.util.function.TriConsumer;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.utils.os.FakeHandler;
@@ -54,7 +55,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.function.Consumer;
 
@@ -94,6 +95,7 @@
                 },
                 visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager);
         mScrimController.setHasBackdrop(false);
+        mScrimController.setWallpaperSupportsAmbientMode(false);
     }
 
     @Test
@@ -132,7 +134,8 @@
         // Back scrim should be transparent
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
 
-        // Move on to PULSING and check if the back scrim is still transparent
+        // Pulsing notification should conserve AOD wallpaper.
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
@@ -217,13 +220,14 @@
         mScrimController.finishAnimationsImmediately();
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
 
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN);
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be semi-transparent so the user can see the wallpaper
         // Pulse callback should have been invoked
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
-        assertScrimTint(mScrimBehind, true /* tinted */);
+        assertScrimTint(mScrimBehind, false /* tinted */);
     }
 
     @Test
@@ -474,6 +478,21 @@
     }
 
     @Test
+    public void testHoldsPulsingWallpaperAnimationLock() {
+        // Pre-conditions
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
+        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.finishAnimationsImmediately();
+        reset(mWakeLock);
+
+        mScrimController.onHideWallpaperTimeout();
+        verify(mWakeLock).acquire();
+        verify(mWakeLock, never()).release();
+        mScrimController.finishAnimationsImmediately();
+        verify(mWakeLock).release();
+    }
+
+    @Test
     public void testWillHideAodWallpaper() {
         mScrimController.setWallpaperSupportsAmbientMode(true);
         mScrimController.transitionTo(ScrimState.AOD);
@@ -483,6 +502,30 @@
     }
 
     @Test
+    public void testWillHidePulsingWallpaper_whenNotification() {
+        mScrimController.setWallpaperSupportsAmbientMode(false);
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
+        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true);
+    }
+
+    @Test
+    public void testWillHidePulsingWallpaper_whenDocking() {
+        mScrimController.setWallpaperSupportsAmbientMode(false);
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_DOCKING);
+        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true);
+    }
+
+    @Test
     public void testConservesExpansionOpacityAfterTransition() {
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.setPanelExpansion(0.5f);
@@ -511,34 +554,6 @@
         Assert.assertTrue(mScrimController.wasAnimationJustCancelled());
     }
 
-    /**
-     * Number of visible notifications affects scrim opacity.
-     */
-    @Test
-    public void testNotificationDensity() {
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
-        mScrimController.finishAnimationsImmediately();
-
-        mScrimController.setNotificationCount(0);
-        mScrimController.finishAnimationsImmediately();
-        Assert.assertEquals("lower density when no notifications",
-                ScrimController.GRADIENT_SCRIM_ALPHA,  mScrimBehind.getViewAlpha(), 0.01f);
-
-        mScrimController.setNotificationCount(3);
-        mScrimController.finishAnimationsImmediately();
-        Assert.assertEquals("stronger density when notifications are visible",
-                ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,  mScrimBehind.getViewAlpha(), 0.01f);
-    }
-
-    /**
-     * Moving from/to states conserves old notification density.
-     */
-    @Test
-    public void testConservesNotificationDensity() {
-        testConservesNotificationDensity(0 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA);
-        testConservesNotificationDensity(3 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY);
-    }
-
     @Test
     public void testScrimFocus() {
         mScrimController.transitionTo(ScrimState.AOD);
@@ -578,7 +593,7 @@
     @Test
     public void testEatsTouchEvent() {
         HashSet<ScrimState> eatsTouches =
-                new HashSet<>(Arrays.asList(ScrimState.AOD, ScrimState.PULSING));
+                new HashSet<>(Collections.singletonList(ScrimState.AOD));
         for (ScrimState state : ScrimState.values()) {
             if (state == ScrimState.UNINITIALIZED) {
                 continue;
@@ -613,24 +628,6 @@
                 mScrimBehind.getDefaultFocusHighlightEnabled());
     }
 
-    /**
-     * Conserves old notification density after leaving state and coming back.
-     *
-     * @param count How many notification.
-     * @param expectedAlpha Expected alpha.
-     */
-    private void testConservesNotificationDensity(int count, float expectedAlpha) {
-        mScrimController.setNotificationCount(count);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
-        mScrimController.finishAnimationsImmediately();
-
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Doesn't respect notification busyness after transition",
-                expectedAlpha,  mScrimBehind.getViewAlpha(), 0.01f);
-    }
-
     private void assertScrimTint(ScrimView scrimView, boolean tinted) {
         final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
         final String name = scrimView == mScrimInFront ? "front" : "back";
@@ -738,5 +735,4 @@
             callback.run();
         }
     }
-
 }
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
index 2bbfd7d..12cb9957 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -40,7 +40,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -82,7 +82,7 @@
         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);
+        NotificationEntry entry = new NotificationEntry(sbn);
         mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
                 false /* animate */);
         TestableLooper.get(this).processAllMessages();
@@ -96,7 +96,7 @@
         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);
+        NotificationEntry entry = new NotificationEntry(sbn);
         mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
                 false /* animate */);
         TestableLooper.get(this).processAllMessages();
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 d57d565..0d4046b 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
@@ -95,13 +95,13 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -135,7 +135,7 @@
     @Mock private IDreamManager mDreamManager;
     @Mock private ScrimController mScrimController;
     @Mock private DozeScrimController mDozeScrimController;
-    @Mock private ArrayList<Entry> mNotificationList;
+    @Mock private ArrayList<NotificationEntry> mNotificationList;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private NotificationData mNotificationData;
     @Mock
@@ -205,7 +205,7 @@
 
         mMetricsLogger = new FakeMetricsLogger();
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mEntryManager = new TestableNotificationEntryManager(mPowerManager, mContext);
+        mEntryManager = new TestableNotificationEntryManager(mContext);
         mNotificationLogger = new NotificationLogger(mNotificationListener,
                 Dependency.get(UiOffloadThread.class), mEntryManager, mStatusBarStateController);
         mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
@@ -409,7 +409,7 @@
                 .build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.importance = IMPORTANCE_HIGH;
 
         assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -430,7 +430,7 @@
                 .build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.importance = IMPORTANCE_HIGH;
 
         assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -447,7 +447,7 @@
         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);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
         entry.importance = IMPORTANCE_HIGH;
 
@@ -465,7 +465,7 @@
         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);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.importance = IMPORTANCE_HIGH;
 
         assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -761,9 +761,8 @@
 
     public static class TestableNotificationEntryManager extends NotificationEntryManager {
 
-        public TestableNotificationEntryManager(PowerManager powerManager, Context context) {
+        public TestableNotificationEntryManager(Context context) {
             super(context);
-            mPowerManager = powerManager;
         }
 
         public void setUpForTest(NotificationPresenter presenter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/IconLoggerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/IconLoggerImplTest.java
deleted file mode 100644
index 5c34730..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/IconLoggerImplTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_NUM_STATUS_ICONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_ICONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
-        .RESERVED_FOR_LOGBUILDER_LATENCY_MILLIS;
-
-import static org.junit.Assert.*;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static java.lang.Thread.sleep;
-
-import android.metrics.LogMaker;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.MessageHandler;
-import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class IconLoggerImplTest extends SysuiTestCase {
-
-    private MetricsLogger mMetricsLogger;
-    private IconLoggerImpl mIconLogger;
-    private TestableLooper mTestableLooper;
-    private MessageHandler mMessageHandler;
-
-    @Before
-    public void setup() {
-        IconLoggerImpl.MIN_LOG_INTERVAL = 5; // Low interval for testing
-        mMetricsLogger = mock(MetricsLogger.class);
-        mTestableLooper = TestableLooper.get(this);
-        mMessageHandler = mock(MessageHandler.class);
-        mTestableLooper.setMessageHandler(mMessageHandler);
-        String[] iconArray = new String[] {
-                "test_icon_1",
-                "test_icon_2",
-        };
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.array.config_statusBarIcons, iconArray);
-        mIconLogger = new IconLoggerImpl(mContext, mTestableLooper.getLooper(), mMetricsLogger);
-        when(mMessageHandler.onMessageHandled(any())).thenReturn(true);
-        clearInvocations(mMetricsLogger);
-    }
-
-    @Test
-    public void testIconShown() throws InterruptedException {
-        // Should only get one message, for the same icon shown twice.
-        mIconLogger.onIconShown("test_icon_2");
-        mIconLogger.onIconShown("test_icon_2");
-
-        // There should be some delay before execute.
-        mTestableLooper.processAllMessages();
-        verify(mMessageHandler, never()).onMessageHandled(any());
-
-        sleep(10);
-        mTestableLooper.processAllMessages();
-        verify(mMessageHandler, times(1)).onMessageHandled(any());
-    }
-
-    @Test
-    public void testIconHidden() throws InterruptedException {
-        // Add the icon so that it can be removed.
-        mIconLogger.onIconShown("test_icon_2");
-        sleep(10);
-        mTestableLooper.processAllMessages();
-        clearInvocations(mMessageHandler);
-
-        // Should only get one message, for the same icon shown twice.
-        mIconLogger.onIconHidden("test_icon_2");
-        mIconLogger.onIconHidden("test_icon_2");
-
-        // There should be some delay before execute.
-        mTestableLooper.processAllMessages();
-        verify(mMessageHandler, never()).onMessageHandled(any());
-
-        sleep(10);
-        mTestableLooper.processAllMessages();
-        verify(mMessageHandler, times(1)).onMessageHandled(any());
-    }
-
-    @Test
-    public void testLog() throws InterruptedException {
-        mIconLogger.onIconShown("test_icon_2");
-        sleep(10);
-        mTestableLooper.processAllMessages();
-
-        verify(mMetricsLogger).write(argThat(maker -> {
-            if (IconLoggerImpl.MIN_LOG_INTERVAL >
-                    (long) maker.getTaggedData(RESERVED_FOR_LOGBUILDER_LATENCY_MILLIS)) {
-                Log.e("IconLoggerImplTest", "Invalid latency "
-                        + maker.getTaggedData(RESERVED_FOR_LOGBUILDER_LATENCY_MILLIS));
-                return false;
-            }
-            if (1 != (int) maker.getTaggedData(FIELD_NUM_STATUS_ICONS)) {
-                Log.e("IconLoggerImplTest", "Invalid icon count "
-                        + maker.getTaggedData(FIELD_NUM_STATUS_ICONS));
-                return false;
-            }
-            return true;
-        }));
-    }
-
-    @Test
-    public void testBitField() throws InterruptedException {
-        mIconLogger.onIconShown("test_icon_2");
-        sleep(10);
-        mTestableLooper.processAllMessages();
-
-        verify(mMetricsLogger).write(argThat(maker -> {
-            if ((1 << 1) != (int) maker.getTaggedData(FIELD_STATUS_ICONS)) {
-                Log.e("IconLoggerImplTest", "Invalid bitfield " + Integer.toHexString(
-                        (Integer) maker.getTaggedData(FIELD_NUM_STATUS_ICONS)));
-                return false;
-            }
-            return true;
-        }));
-
-        mIconLogger.onIconShown("test_icon_1");
-        sleep(10);
-        mTestableLooper.processAllMessages();
-
-        verify(mMetricsLogger).write(argThat(maker -> {
-            if ((1 << 1 | 1 << 0) != (int) maker.getTaggedData(FIELD_STATUS_ICONS)) {
-                Log.e("IconLoggerImplTest", "Invalid bitfield " + Integer.toHexString(
-                        (Integer) maker.getTaggedData(FIELD_NUM_STATUS_ICONS)));
-                return false;
-            }
-            return true;
-        }));
-
-        mIconLogger.onIconHidden("test_icon_2");
-        sleep(10);
-        mTestableLooper.processAllMessages();
-
-        verify(mMetricsLogger).write(argThat(maker -> {
-            if ((1 << 0) != (int) maker.getTaggedData(FIELD_STATUS_ICONS)) {
-                Log.e("IconLoggerImplTest", "Invalid bitfield " + Integer.toHexString(
-                        (Integer) maker.getTaggedData(FIELD_STATUS_ICONS)));
-                return false;
-            }
-            return true;
-        }));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index fdbf090..c1f8885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -182,6 +182,7 @@
             subs.add(subscription);
         }
         when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs);
+        when(mMockSm.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subs);
         mNetworkController.doUpdateMobileControllers();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index 2266b47..3cbf902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -20,6 +20,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import android.app.RemoteInput;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
@@ -51,6 +52,9 @@
         resources.addOverride(R.bool.config_smart_replies_in_notifications_enabled, true);
         resources.addOverride(
                 R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts, 7);
+        resources.addOverride(
+                R.bool.config_smart_replies_in_notifications_edit_choices_before_sending, false);
+        resources.addOverride(R.bool.config_smart_replies_in_notifications_show_in_heads_up, true);
         mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext);
     }
 
@@ -104,6 +108,71 @@
         assertEquals(5, mConstants.getMaxSqueezeRemeasureAttempts());
     }
 
+    @Test
+    public void testGetEffectiveEditChoicesBeforeSendingWithNoConfig() {
+        overrideSetting("enabled=true");
+        triggerConstantsOnChange();
+        assertFalse(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+        assertTrue(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+        assertFalse(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+    }
+
+    @Test
+    public void testGetEffectiveEditChoicesBeforeSendingWithEnabledConfig() {
+        overrideSetting("enabled=true,edit_choices_before_sending=true");
+        triggerConstantsOnChange();
+        assertTrue(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+        assertTrue(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+        assertFalse(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+    }
+
+    @Test
+    public void testGetEffectiveEditChoicesBeforeSendingWithDisabledConfig() {
+        overrideSetting("enabled=true,edit_choices_before_sending=false");
+        triggerConstantsOnChange();
+        assertFalse(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+        assertTrue(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+        assertFalse(
+                mConstants.getEffectiveEditChoicesBeforeSending(
+                        RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+    }
+
+    @Test
+    public void testShowInHeadsUpWithNoConfig() {
+        assertTrue(mConstants.isEnabled());
+        assertTrue(mConstants.getShowInHeadsUp());
+    }
+
+    @Test
+    public void testShowInHeadsUpEnabled() {
+        overrideSetting("enabled=true,show_in_heads_up=true");
+        triggerConstantsOnChange();
+        assertTrue(mConstants.getShowInHeadsUp());
+    }
+
+    @Test
+    public void testShowInHeadsUpDisabled() {
+        overrideSetting("enabled=true,show_in_heads_up=false");
+        triggerConstantsOnChange();
+        assertFalse(mConstants.getShowInHeadsUp());
+    }
+
     private void overrideSetting(String flags) {
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, flags);
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 c5bac92..1066bc1 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
@@ -50,7 +50,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.ShadeController;
 
@@ -94,7 +94,7 @@
     private int mSpacing;
 
     @Mock private SmartReplyController mLogger;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private Notification mNotification;
 
     @Mock ActivityStarter mActivityStarter;
@@ -127,7 +127,7 @@
         StatusBarNotification sbn = mock(StatusBarNotification.class);
         when(sbn.getNotification()).thenReturn(mNotification);
         when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
-        mEntry = new NotificationData.Entry(sbn);
+        mEntry = new NotificationEntry(sbn);
 
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
@@ -855,4 +855,92 @@
         assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
         assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
     }
+
+    /**
+     * Test to ensure that we try to add all possible actions - if we find one action that's too
+     * long we just skip that one rather than quitting altogether.
+     */
+    @Test
+    public void testMeasure_skipTooLongActions() {
+        String[] choices = new String[] {};
+        String[] actions = new String[] {
+                "a1", "a2", "this action is soooooooo long it's ridiculous", "a4"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
+                createActions(new String[] {"a1", "a2", "a4"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonHidden(mView.getChildAt(2));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+    }
+
+    /**
+     * Test to ensure that we try to add all possible replies - if we find one reply that's too
+     * long we just skip that one rather than quitting altogether.
+     */
+    @Test
+    public void testMeasure_skipTooLongReplies() {
+        String[] choices = new String[] {
+                "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
+        String[] actions = new String[] {};
+
+        // All replies should be displayed as single-line smart reply buttons.
+        ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
+                1 /* lineCount */, Collections.emptyList());
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonHidden(mView.getChildAt(2));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+    }
+
+    /**
+     * Test to ensure that we try to add all possible replies and actions - if we find a reply or
+     * action that's too long we just skip that one rather than quitting altogether.
+     */
+    @Test
+    public void testMeasure_skipTooLongRepliesAndActions() {
+        String[] choices = new String[] {
+                "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
+        String[] actions = new String[] {
+                "a1", "ThisActionIsSooooooooLongItsRidiculousIPromise"};
+
+        // All replies should be displayed as single-line smart reply buttons.
+        ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
+                1 /* lineCount */, createActions(new String[] {"a1"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(
+                expectedView.getChildAt(0), mView.getChildAt(0)); // r1
+        assertReplyButtonShownWithEqualMeasures(
+                expectedView.getChildAt(1), mView.getChildAt(1)); // r2
+        assertReplyButtonHidden(mView.getChildAt(2)); // long reply
+        assertReplyButtonShownWithEqualMeasures(
+                expectedView.getChildAt(2), mView.getChildAt(3)); // r4
+        assertReplyButtonShownWithEqualMeasures(
+                expectedView.getChildAt(3), mView.getChildAt(4)); // a1
+        assertReplyButtonHidden(mView.getChildAt(5)); // long action
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java
new file mode 100644
index 0000000..c44a366
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.wallpaper;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.FeatureFlagUtils;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AodMaskViewTest extends SysuiTestCase {
+    private AodMaskView mMaskView;
+    private DisplayInfo mDisplayInfo;
+    private ImageWallpaperTransformer mTransformer;
+
+    @Before
+    public void setUp() throws Exception {
+        DisplayManager displayManager =
+                spy((DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE));
+        doNothing().when(displayManager).registerDisplayListener(any(), any());
+        mContext.addMockSystemService(DisplayManager.class, displayManager);
+
+        WallpaperManager wallpaperManager =
+                spy((WallpaperManager) mContext.getSystemService(Context.WALLPAPER_SERVICE));
+        doReturn(null).when(wallpaperManager).getWallpaperInfo();
+        mContext.addMockSystemService(WallpaperManager.class, wallpaperManager);
+
+        mTransformer = spy(new ImageWallpaperTransformer(null /* listener */));
+        mMaskView = spy(new AodMaskView(getContext(), null /* attrs */, mTransformer));
+        mDisplayInfo = new DisplayInfo();
+
+        ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
+                .getDefaultDisplay().getDisplayInfo(mDisplayInfo);
+
+        FeatureFlagUtils.setEnabled(
+                mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED, true);
+    }
+
+    @After
+    public void tearDown() {
+        FeatureFlagUtils.setEnabled(
+                mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED, false);
+    }
+
+    @Test
+    public void testCreateMaskView_TransformerIsNotNull() {
+        assertNotNull("mTransformer should not be null", mTransformer);
+    }
+
+    @Test
+    public void testAodMaskView_ShouldNotClickable() {
+        assertFalse("MaskView should not be clickable", mMaskView.isClickable());
+    }
+
+    @Test
+    public void testAodMaskView_OnSizeChange_ShouldUpdateTransformerOffsets() {
+        mMaskView.onSizeChanged(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, 0, 0);
+        verify(mTransformer, times(1)).updateOffsets();
+    }
+
+    @Test
+    public void testAodMaskView_OnDraw_ShouldDrawTransformedImage() {
+        Canvas c = new Canvas();
+        RectF bounds = new RectF(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        mMaskView.onSizeChanged(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, 0, 0);
+        mMaskView.onStatePreChange(0, 1);
+        mMaskView.onDraw(c);
+        verify(mTransformer, times(1)).drawTransformedImage(c, null, null, bounds);
+    }
+
+    @Test
+    public void testAodMaskView_IsDozing_ShouldUpdateAmbientModeState() {
+        doNothing().when(mMaskView).setAnimatorProperty(anyBoolean());
+        mMaskView.onStatePreChange(0, 1);
+        mMaskView.onDozingChanged(true);
+        verify(mTransformer, times(1)).updateAmbientModeState(true);
+    }
+
+    @Test
+    public void testAodMaskView_IsDozing_ShouldDoTransitionOrDrawFinalFrame() {
+        doNothing().when(mMaskView).setAnimatorProperty(anyBoolean());
+        mMaskView.onStatePreChange(0, 1);
+        mMaskView.onDozingChanged(true);
+        mMaskView.onStatePostChange();
+        mMaskView.onDozingChanged(false);
+        verify(mMaskView, times(1)).invalidate();
+        verify(mMaskView, times(1)).setAnimatorProperty(false);
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java
new file mode 100644
index 0000000..55b0aae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 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.wallpaper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ImageWallpaperTransformerTest extends SysuiTestCase {
+    private DisplayInfo mDisplayInfo;
+    private Bitmap mBitmap;
+    private Canvas mCanvas;
+    private RectF mDestination;
+
+    @Before
+    public void setUp() throws Exception {
+        mDisplayInfo = new DisplayInfo();
+        ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
+                .getDefaultDisplay().getDisplayInfo(mDisplayInfo);
+        int dimension = Math.max(mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
+        mBitmap = Bitmap.createBitmap(dimension, dimension, Bitmap.Config.ARGB_8888);
+        mCanvas = new Canvas(mBitmap);
+        mCanvas.drawColor(Color.RED);
+        mDestination = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+    }
+
+    @Test
+    public void testVignetteFilter() {
+        VignetteFilter vignette = new VignetteFilter();
+
+        ImageWallpaperTransformer transformer = getTransformer(vignette);
+        transformer.drawTransformedImage(mCanvas, mBitmap, null, mDestination);
+
+        PointF center = vignette.getCenterPoint();
+        int p1 = mBitmap.getPixel((int) center.x, (int) center.y);
+        int p2 = mBitmap.getPixel(0, 0);
+        int p3 = mBitmap.getPixel(mBitmap.getWidth() - 1, mBitmap.getHeight() - 1);
+
+        assertThat(p1).isEqualTo(Color.RED);
+        assertThat(p2 | p3).isEqualTo(Color.BLACK);
+    }
+
+    @Test
+    public void testScrimFilter() {
+        getTransformer(new ScrimFilter())
+                .drawTransformedImage(mCanvas, mBitmap, null, mDestination);
+
+        int pixel = mBitmap.getPixel(0, 0);
+
+        // 0xff4d0000 is the result of 70% alpha pre-multiplied which is 0.7*(0,0,0)+0.3*(255,0,0).
+        assertThat(pixel).isEqualTo(0xff4d0000);
+    }
+
+    private ImageWallpaperTransformer getTransformer(ImageWallpaperFilter filter) {
+        ImageWallpaperTransformer transformer = new ImageWallpaperTransformer(null);
+        transformer.addFilter(filter);
+        transformer.updateDisplayInfo(mDisplayInfo);
+        transformer.updateOffsets();
+        transformer.updateAmbientModeState(true);
+        return transformer;
+    }
+}
diff --git a/packages/VpnDialogs/res/values-as/strings.xml b/packages/VpnDialogs/res/values-as/strings.xml
index 25f16e3..45d8458 100644
--- a/packages/VpnDialogs/res/values-as/strings.xml
+++ b/packages/VpnDialogs/res/values-as/strings.xml
@@ -21,7 +21,7 @@
     <string name="legacy_title" msgid="192936250066580964">"ভিপিএন সংযোগ হৈ আছে"</string>
     <string name="session" msgid="6470628549473641030">"ছেশ্বন:"</string>
     <string name="duration" msgid="3584782459928719435">"সময়সীমা:"</string>
-    <string name="data_transmitted" msgid="7988167672982199061">"পঠিওৱা হ\'ল:"</string>
+    <string name="data_transmitted" msgid="7988167672982199061">"পঠিওৱা হ’ল:"</string>
     <string name="data_received" msgid="4062776929376067820">"পোৱা গ\'ল:"</string>
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_1">%2$s</xliff:g> পেকেট / <xliff:g id="NUMBER_0">%1$s</xliff:g> বাইট"</string>
     <string name="always_on_disconnected_title" msgid="1906740176262776166">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ কৰিবপৰা নাই"</string>
diff --git a/packages/VpnDialogs/res/values-ne/strings.xml b/packages/VpnDialogs/res/values-ne/strings.xml
index 5019a06..b716c35 100644
--- a/packages/VpnDialogs/res/values-ne/strings.xml
+++ b/packages/VpnDialogs/res/values-ne/strings.xml
@@ -25,7 +25,7 @@
     <string name="data_received" msgid="4062776929376067820">"प्राप्त भयो:"</string>
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> बाइटहरू / <xliff:g id="NUMBER_1">%2$s</xliff:g> प्याकेटहरू"</string>
     <string name="always_on_disconnected_title" msgid="1906740176262776166">"सधैँ-सक्रिय रहने VPN सेवामा जडान गर्न सकिँदैन"</string>
-    <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> लाई सधैँ जडान भइरहनेगरि सेटअप गरिएको छ तर यसलाई अहिले नै जडान गर्न मिल्दैन। तपाईंको फोन <xliff:g id="VPN_APP_1">%1$s</xliff:g> मा पुन: जडान नहुँदासम्म यसले कुनै सार्वजनिक नेटवर्क प्रयोग गर्नेछ।"</string>
+    <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> लाई सधैँ जडान भइरहनेगरि सेटअप गरिएको छ तर यसलाई अहिले नै जडान गर्न मिल्दैन। तपाईंको फोन <xliff:g id="VPN_APP_1">%1$s</xliff:g> मा पुन: जडान नहुँदासम्म यसले कुनै सार्वजनिक नेटवर्क प्रयोग गर्ने छ।"</string>
     <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> लाई सधैँ पनि जडान भइरहनेगरि सेटअप गरिएको छ तर यसलाई अहिले नै जडान गर्न मिल्दैन। VPN पुन: जडान नहुँदासम्म तपाईंसँग कुनै पनि इन्टरनेट रहनेछैन।"</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सम्बन्धी सेटिङहरू परिवर्तन गर्नुहोस्"</string>
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index 848f2bd..2fa1dde 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_PACKAGE_NAME := WallpaperCropper
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/packages/WallpaperCropper/CleanSpec.mk b/packages/WallpaperCropper/CleanSpec.mk
new file mode 100644
index 0000000..e6d8d5a
--- /dev/null
+++ b/packages/WallpaperCropper/CleanSpec.mk
@@ -0,0 +1,50 @@
+# Copyright (C) 2019 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/WallpaperCropper)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
index baf09b1..da10361 100644
--- a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Black accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_black_overlay" translatable="false">Black Accent Color</string>
+    <string name="accent_color_black_overlay" translatable="false">Black</string>
 </resources>
 
diff --git a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
index 4de344c..623a1da 100644
--- a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Green accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_green_overlay" translatable="false">Green Accent Color</string>
+    <string name="accent_color_green_overlay" translatable="false">Green</string>
 </resources>
 
diff --git a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
index d1eb95a..d1c7168 100644
--- a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Purple accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_purple_overlay" translatable="false">Purple Accent Color</string>
+    <string name="accent_color_purple_overlay" translatable="false">Purple</string>
 </resources>
 
diff --git a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
index 165aab2..a6aa64c 100644
--- a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
+++ b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
@@ -19,7 +19,7 @@
      <!-- Name of a font family to use for body text. -->
     <string name="config_bodyFontFamily" translatable="false">source-sans-pro</string>
     <!-- Name of a font family to use for medium body text. -->
-    <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-medium</string>
+    <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-semi-bold</string>
     <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
     <string name="config_headlineFontFamily" translatable="false">arbutus-slab</string>
     <!-- Name of the font family used for system surfaces where the font should use medium weight -->
diff --git a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
index 229c578..4e70d72 100644
--- a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
+++ b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
@@ -19,10 +19,10 @@
      <!-- Name of a font family to use for body text. -->
     <string name="config_bodyFontFamily" translatable="false">lato</string>
     <!-- Name of a font family to use for medium body text. -->
-    <string name="config_bodyFontFamilyMedium" translatable="false">lato-medium</string>
+    <string name="config_bodyFontFamilyMedium" translatable="false">lato-bold</string>
     <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
     <string name="config_headlineFontFamily" translatable="false">arvo</string>
     <!-- Name of the font family used for system surfaces where the font should use medium weight -->
-    <string name="config_headlineFontFamilyMedium" translatable="false">arvo-medium</string>
+    <string name="config_headlineFontFamilyMedium" translatable="false">arvo-bold</string>
 </resources>
 
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
index dc5c196..3c4c24d 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Rounded corner rectangle overlay -->
-    <string name="icon_shape_roundedrect_overlay" translatable="false">RoundedRectangle Icons</string>
+    <string name="icon_shape_roundedrect_overlay" translatable="false">Rounded Rectangle</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
index 4fd39ff26..5772165 100644
--- a/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Square icon overlay -->
-    <string name="icon_shape_square_overlay" translatable="false">Square Icons</string>
+    <string name="icon_shape_square_overlay" translatable="false">Square</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
index b7c001c..028eccb 100644
--- a/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Squircle icon shape overlay -->
-    <string name="icon_shape_squircle_overlay" translatable="false">Square Icons</string>
+    <string name="icon_shape_squircle_overlay" translatable="false">Squircle</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
index d946ee8..db9fa98 100644
--- a/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Teardrop icon overlay -->
-    <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop Icons</string>
+    <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop</string>
 
 </resources>
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
index 295b3d8b..be9ba43 100644
--- a/packages/services/PacProcessor/Android.mk
+++ b/packages/services/PacProcessor/Android.mk
@@ -26,6 +26,6 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
 
-LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor libpac
+LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor
 
 include $(BUILD_PACKAGE)
diff --git a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
index 846ff25..d969c69 100644
--- a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
+++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
@@ -16,6 +16,9 @@
 
 #define LOG_TAG "PacProcessor"
 
+#include <stdlib.h>
+#include <string>
+
 #include <utils/Log.h>
 #include <utils/Mutex.h>
 #include "android_runtime/AndroidRuntime.h"
@@ -23,40 +26,24 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
-#include "proxy_resolver_v8.h"
+#include "proxy_resolver_v8_wrapper.h"
 
 namespace android {
 
-class ProxyErrorLogger : public net::ProxyErrorListener {
-public:
-    ~ProxyErrorLogger() {
-
-    }
-    void AlertMessage(String16 message) {
-        String8 str(message);
-        ALOGD("Alert: %s", str.string());
-    }
-    void ErrorMessage(String16 message) {
-        String8 str(message);
-        ALOGE("Error: %s", str.string());
-    }
-};
-
-net::ProxyResolverV8* proxyResolver = NULL;
-ProxyErrorLogger* logger = NULL;
+ProxyResolverV8Handle* proxyResolver = NULL;
 bool pacSet = false;
 
-String16 jstringToString16(JNIEnv* env, jstring jstr) {
+std::u16string jstringToString16(JNIEnv* env, jstring jstr) {
     const jchar* str = env->GetStringCritical(jstr, 0);
-    String16 str16(reinterpret_cast<const char16_t*>(str),
+    std::u16string str16(reinterpret_cast<const char16_t*>(str),
                    env->GetStringLength(jstr));
     env->ReleaseStringCritical(jstr, str);
     return str16;
 }
 
-jstring string16ToJstring(JNIEnv* env, String16 string) {
-    const char16_t* str = string.string();
-    size_t len = string.size();
+jstring string16ToJstring(JNIEnv* env, std::u16string string) {
+    const char16_t* str = string.data();
+    size_t len = string.length();
 
     return env->NewString(reinterpret_cast<const jchar*>(str), len);
 }
@@ -64,9 +51,7 @@
 static jboolean com_android_pacprocessor_PacNative_createV8ParserNativeLocked(JNIEnv* /* env */,
         jobject) {
     if (proxyResolver == NULL) {
-        logger = new ProxyErrorLogger();
-        proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault(),
-                logger);
+        proxyResolver = ProxyResolverV8Handle_new();
         pacSet = false;
         return JNI_FALSE;
     }
@@ -76,9 +61,7 @@
 static jboolean com_android_pacprocessor_PacNative_destroyV8ParserNativeLocked(JNIEnv* /* env */,
         jobject) {
     if (proxyResolver != NULL) {
-        delete logger;
-        delete proxyResolver;
-        logger = NULL;
+        ProxyResolverV8Handle_delete(proxyResolver);
         proxyResolver = NULL;
         return JNI_FALSE;
     }
@@ -87,14 +70,14 @@
 
 static jboolean com_android_pacprocessor_PacNative_setProxyScriptNativeLocked(JNIEnv* env, jobject,
         jstring script) {
-    String16 script16 = jstringToString16(env, script);
+    std::u16string script16 = jstringToString16(env, script);
 
     if (proxyResolver == NULL) {
         ALOGE("V8 Parser not started when setting PAC script");
         return JNI_TRUE;
     }
 
-    if (proxyResolver->SetPacScript(script16) != OK) {
+    if (ProxyResolverV8Handle_SetPacScript(proxyResolver, script16.data()) != OK) {
         ALOGE("Unable to set PAC script");
         return JNI_TRUE;
     }
@@ -105,9 +88,8 @@
 
 static jstring com_android_pacprocessor_PacNative_makeProxyRequestNativeLocked(JNIEnv* env, jobject,
         jstring url, jstring host) {
-    String16 url16 = jstringToString16(env, url);
-    String16 host16 = jstringToString16(env, host);
-    String16 ret;
+    std::u16string url16 = jstringToString16(env, url);
+    std::u16string host16 = jstringToString16(env, host);
 
     if (proxyResolver == NULL) {
         ALOGE("V8 Parser not initialized when running PAC script");
@@ -119,12 +101,14 @@
         return NULL;
     }
 
-    if (proxyResolver->GetProxyForURL(url16, host16, &ret) != OK) {
-        String8 ret8(ret);
-        ALOGE("Error Running PAC: %s", ret8.string());
+    std::unique_ptr<char16_t, decltype(&free)> result = std::unique_ptr<char16_t, decltype(&free)>(
+        ProxyResolverV8Handle_GetProxyForURL(proxyResolver, url16.data(), host16.data()), &free);
+    if (result.get() == NULL) {
+        ALOGE("Error Running PAC");
         return NULL;
     }
 
+    std::u16string ret(result.get());
     jstring jret = string16ToJstring(env, ret);
 
     return jret;
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3180b47..8261fe8 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -213,6 +213,25 @@
         IOOP_SYNC = 4;
     }
 
+  // Subtypes of notifications blocking helper view
+  // (NOTIFICATION_BLOCKING_HELPER).
+  enum NotificationBlockingHelper {
+    BLOCKING_HELPER_UNKNOWN = 0;
+    BLOCKING_HELPER_DISPLAY = 1;
+    BLOCKING_HELPER_DISMISS = 2;
+    // When the view of the notification blocking helper was triggered by
+    // system.
+    BLOCKING_HELPER_TRIGGERED_BY_SYSTEM = 3;
+    // "block" was clicked.
+    BLOCKING_HELPER_CLICK_BLOCKED = 4;
+    // "stay silent" was clicked.
+    BLOCKING_HELPER_CLICK_STAY_SILENT = 5;
+    // "alert me" was clicked.
+    BLOCKING_HELPER_CLICK_ALERT_ME = 6;
+    // "undo" was clicked (enables the user to undo "stop notification" action).
+    BLOCKING_HELPER_CLICK_UNDO = 7;
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
@@ -3684,7 +3703,7 @@
     ACTION_SETTINGS_TILE_CLICK = 830;
 
     // OPEN: Notification unsnoozed. CLOSE: Notification snoozed. UPDATE: snoozed notification
-    // updated
+    // updated. TYPE_DISMISS: snoozing canceled due to data being cleared on package
     // CATEGORY: NOTIFICATION
     // OS: O
     NOTIFICATION_SNOOZED = 831;
@@ -5325,7 +5344,7 @@
     // OS: P
     ACCESSIBILITY_VIBRATION = 1292;
 
-    // OPEN: Settings > Accessibility > Vibration > Ring & notification vibration
+    // OPEN: Settings > Accessibility > Vibration > Notification vibration
     // CATEGORY: SETTINGS
     // OS: P
     ACCESSIBILITY_VIBRATION_NOTIFICATION = 1293;
@@ -6730,8 +6749,78 @@
     // OS: Q
     ZEN_CUSTOM_SETTINGS_DIALOG = 1612;
 
-    // ---- End Q Constants, all Q constants go above this line ----
+    // OPEN: Settings > Developer Options > Game Update Packages
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_GUP_DASHBOARD = 1613;
 
+    // CATEGORY: The category for all actions relating to language detection logging.
+    // OS: Q
+    LANGUAGE_DETECTION = 1614;
+
+    // CATEGORY: The category for all actions relating to conversation actions logging.
+    // OS: Q
+    CONVERSATION_ACTIONS = 1615;
+
+    // ACTION: Actions from a text classifier are shown to user.
+    // CATEGORY: CONVERSATION_ACTIONS
+    // OS: Q
+    ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN = 1616;
+
+    // ACTION: Event time of a text classifier event in unix timestamp.
+    // CATEGORY: CONVERSATION_ACTIONS, LANGUAGE_DETECTION
+    // OS: Q
+    FIELD_TEXT_CLASSIFIER_EVENT_TIME = 1617;
+
+    // ACTION: Users compose their own replies instead of using suggested ones.
+    // CATEGORY: CONVERSATION_ACTIONS
+    // OS: Q
+    ACTION_TEXT_CLASSIFIER_MANUAL_REPLY = 1618;
+
+    // ACTION: Text classifier generates an action.
+    // CATEGORY: CONVERSATION_ACTIONS
+    // OS: Q
+    ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED = 1619;
+
+    // OPEN: Settings > Accessibility > Vibration > Ring vibration
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACCESSIBILITY_VIBRATION_RING = 1620;
+
+    // ACTION: Notification blocking helper view, which helps the user to block
+    // application or channel from showing notifications.
+    // SUBTYPE: NotificationBlockingHelper enum.
+    // CATEGORY: NOTIFICATION
+    // OS: Q
+    NOTIFICATION_BLOCKING_HELPER = 1621;
+
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Forground
+    // OS: Q
+    ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622;
+
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Default
+    // OS: Q
+    ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
+
+    // OPEN: Settings > System > Input & Gesture > Skip song gesture
+    // OS: Q
+    SETTINGS_GESTURE_SKIP_SONG = 1624;
+
+    // OPEN: Settings > System > Input & Gesture > Silence gesture
+    // OS: Q
+    SETTINGS_GESTURE_SILENCE = 1625;
+
+    // OPEN: Settings > System > Input & Gesture > Tap screen gesture
+    // OS: Q
+    SETTINGS_GESTURE_TAP_SCREEN = 1626;
+
+    // OPEN: Settings > Network & internet > Click Mobile network to land on a page with a list of
+    // SIM/eSIM subscriptions.
+    // CATEGORY: SETTINGS
+    // OS: Q
+    MOBILE_NETWORK_LIST = 1627;
+
+    // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/Android.bp b/services/Android.bp
index 58a0997..31385ed 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -17,17 +17,21 @@
     static_libs: [
         "services.core",
         "services.accessibility",
+        "services.appprediction",
         "services.appwidget",
         "services.autofill",
         "services.backup",
         "services.companion",
         "services.contentcapture",
+        "services.contentsuggestions",
         "services.coverage",
         "services.devicepolicy",
+        "services.ipmemorystore",
         "services.midi",
         "services.net",
         "services.print",
         "services.restrictions",
+        "services.startop",
         "services.usage",
         "services.usb",
         "services.voiceinteraction",
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 36ca52a..763c16f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,11 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
@@ -23,13 +28,9 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -121,6 +122,8 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import libcore.util.EmptyArray;
+
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.FileDescriptor;
@@ -139,8 +142,6 @@
 import java.util.function.Consumer;
 import java.util.function.IntSupplier;
 
-import libcore.util.EmptyArray;
-
 /**
  * This class is instantiated by the system as a system level service and can be
  * accessed only by the system. The task of this service is to be a centralized
@@ -1827,8 +1828,6 @@
         updateFilterKeyEventsLocked(userState);
         updateTouchExplorationLocked(userState);
         updatePerformGesturesLocked(userState);
-        updateDisplayDaltonizerLocked(userState);
-        updateDisplayInversionLocked(userState);
         updateMagnificationLocked(userState);
         scheduleUpdateFingerprintGestureHandling(userState);
         scheduleUpdateInputFilter(userState);
@@ -2187,14 +2186,6 @@
         return false;
     }
 
-    private void updateDisplayDaltonizerLocked(UserState userState) {
-        DisplayAdjustmentUtils.applyDaltonizerSetting(mContext, userState.mUserId);
-    }
-
-    private void updateDisplayInversionLocked(UserState userState) {
-        DisplayAdjustmentUtils.applyInversionSetting(mContext, userState.mUserId);
-    }
-
     private void updateMagnificationLocked(UserState userState) {
         if (userState.mUserId != mCurrentUserId) {
             return;
@@ -4104,15 +4095,6 @@
         private final Uri mTouchExplorationGrantedAccessibilityServicesUri = Settings.Secure
                 .getUriFor(Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
 
-        private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
-
-        private final Uri mDisplayDaltonizerEnabledUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
-
-        private final Uri mDisplayDaltonizerUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER);
-
         private final Uri mHighTextContrastUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
 
@@ -4153,12 +4135,6 @@
                     mTouchExplorationGrantedAccessibilityServicesUri,
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
-                    mDisplayInversionEnabledUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
-                    mDisplayDaltonizerEnabledUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
-                    mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
                     mHighTextContrastUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
@@ -4202,11 +4178,6 @@
                     if (readTouchExplorationGrantedAccessibilityServicesLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
-                } else if (mDisplayDaltonizerEnabledUri.equals(uri)
-                        || mDisplayDaltonizerUri.equals(uri)) {
-                    updateDisplayDaltonizerLocked(userState);
-                } else if (mDisplayInversionEnabledUri.equals(uri)) {
-                    updateDisplayInversionLocked(userState);
                 } else if (mHighTextContrastUri.equals(uri)) {
                     if (readHighTextContrastEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
deleted file mode 100644
index c81a876..0000000
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2013 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.accessibility;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.Binder;
-import android.provider.Settings.Secure;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.server.LocalServices;
-import com.android.server.display.DisplayTransformManager;
-
-/**
- * Utility methods for performing accessibility display adjustments.
- */
-class DisplayAdjustmentUtils {
-
-    /** Default inversion mode for display color correction. */
-    private static final int DEFAULT_DISPLAY_DALTONIZER =
-            AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;
-
-    /** Matrix and offset used for converting color to gray-scale. */
-    private static final float[] MATRIX_GRAYSCALE = new float[] {
-        .2126f, .2126f, .2126f, 0,
-        .7152f, .7152f, .7152f, 0,
-        .0722f, .0722f, .0722f, 0,
-             0,      0,      0, 1
-    };
-
-    /**
-     * Matrix and offset used for luminance inversion. Represents a transform
-     * from RGB to YIQ color space, rotation around the Y axis by 180 degrees,
-     * transform back to RGB color space, and subtraction from 1. The last row
-     * represents a non-multiplied addition, see surfaceflinger's ProgramCache
-     * for full implementation details.
-     */
-    private static final float[] MATRIX_INVERT_COLOR = new float[] {
-        0.402f, -0.598f, -0.599f, 0,
-       -1.174f, -0.174f, -1.175f, 0,
-       -0.228f, -0.228f,  0.772f, 0,
-             1,       1,       1, 1
-    };
-
-    public static void applyDaltonizerSetting(Context context, int userId) {
-        final ContentResolver cr = context.getContentResolver();
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-
-        int daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED;
-        long identity = Binder.clearCallingIdentity();
-        try {
-            if (Secure.getIntForUser(cr,
-                    Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
-                daltonizerMode = Secure.getIntForUser(cr,
-                        Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER, userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
-        float[] grayscaleMatrix = null;
-        if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
-            // Monochromacy isn't supported by the native Daltonizer.
-            grayscaleMatrix = MATRIX_GRAYSCALE;
-            daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED;
-        }
-        dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, grayscaleMatrix);
-        dtm.setDaltonizerMode(daltonizerMode);
-    }
-
-    /**
-     * Applies the specified user's display color adjustments.
-     */
-    public static void applyInversionSetting(Context context, int userId) {
-        final ContentResolver cr = context.getContentResolver();
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            final boolean invertColors = Secure.getIntForUser(cr,
-                    Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0;
-            dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
-                    invertColors ? MATRIX_INVERT_COLOR : null);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index 17bf570..6a97fbb 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -32,7 +32,6 @@
 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;
@@ -40,8 +39,6 @@
 
 import com.android.internal.R;
 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;
@@ -63,7 +60,7 @@
     private static final String LOG_TAG = "MagnificationController";
 
     public static final float MIN_SCALE = 1.0f;
-    public static final float MAX_SCALE = 5.0f;
+    public static final float MAX_SCALE = 8.0f;
 
     private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
 
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 12e7376..80049e8 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -115,8 +115,11 @@
     private static final boolean DEBUG_PANNING_SCALING = false || DEBUG_ALL;
     private static final boolean DEBUG_EVENT_STREAM = false || DEBUG_ALL;
 
+    // The MIN_SCALE is different from MagnificationController.MIN_SCALE due
+    // to AccessibilityService.MagnificationController#setScale() has
+    // different scale range
     private static final float MIN_SCALE = 2.0f;
-    private static final float MAX_SCALE = 5.0f;
+    private static final float MAX_SCALE = MagnificationController.MAX_SCALE;
 
     @VisibleForTesting final MagnificationController mMagnificationController;
 
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index cf9f233..8ffadde 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -462,7 +462,7 @@
             return false;
         }
 
-        endGestureDetection();
+        endGestureDetection(true);
 
         mAms.onGesture(gestureId);
 
@@ -472,7 +472,7 @@
     @Override
     public boolean onGestureCancelled(MotionEvent event, int policyFlags) {
         if (mCurrentState == STATE_GESTURE_DETECTING) {
-            endGestureDetection();
+            endGestureDetection(event.getActionMasked() == MotionEvent.ACTION_UP);
             return true;
         } else if (mCurrentState == STATE_TOUCH_EXPLORING) {
             // If the finger is still moving, pass the event on.
@@ -804,13 +804,19 @@
         }
     }
 
-    private void endGestureDetection() {
+    private void endGestureDetection(boolean interactionEnd) {
         mAms.onTouchInteractionEnd();
 
         // Announce the end of the gesture recognition.
         sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
-        // Announce the end of a the touch interaction.
-        sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+        if (interactionEnd) {
+            // Announce the end of a the touch interaction.
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+        } else {
+            // If gesture detection is end, but user doesn't release the finger, announce the
+            // transition to exploration state.
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
+        }
 
         mExitGestureDetectionModeDelayed.cancel();
         mCurrentState = STATE_TOUCH_EXPLORING;
@@ -889,7 +895,6 @@
         MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
         if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
             final int pointerIdBits = event.getPointerIdBits();
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
             sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
         }
     }
@@ -1148,8 +1153,8 @@
             sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
             // Clearing puts is in touch exploration state with a finger already
             // down, so announce the transition to exploration state.
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
             clear();
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
         }
     }
 
diff --git a/services/appprediction/Android.bp b/services/appprediction/Android.bp
new file mode 100644
index 0000000..a7be587
--- /dev/null
+++ b/services/appprediction/Android.bp
@@ -0,0 +1,5 @@
+java_library_static {
+    name: "services.appprediction",
+    srcs: ["java/**/*.java"],
+    libs: ["services.core"],
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
new file mode 100644
index 0000000..833eaa0
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -0,0 +1,156 @@
+/*
+ * 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.appprediction;
+
+import static android.Manifest.permission.MANAGE_APP_PREDICTIONS;
+import static android.content.Context.APP_PREDICTION_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.app.prediction.IPredictionManager;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+import java.io.FileDescriptor;
+import java.util.function.Consumer;
+
+/**
+ * A service used to predict app and shortcut usage.
+ *
+ * <p>The data collected by this service can be analyzed and combined with other sources to provide
+ * predictions in different areas of the system such as Launcher and Share sheet.
+ */
+public class AppPredictionManagerService extends
+        AbstractMasterSystemService<AppPredictionManagerService, AppPredictionPerUserService> {
+
+    private static final String TAG = AppPredictionManagerService.class.getSimpleName();
+
+    private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+    public AppPredictionManagerService(Context context) {
+        super(context, new FrameworkResourcesServiceNameResolver(context,
+                com.android.internal.R.string.config_defaultAppPredictionService), null);
+    }
+
+    @Override
+    protected AppPredictionPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
+        return new AppPredictionPerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(APP_PREDICTION_SERVICE, new PredictionManagerServiceStub());
+    }
+
+    @Override
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_APP_PREDICTIONS, TAG);
+    }
+
+    @Override
+    protected int getMaximumTemporaryServiceDurationMs() {
+        return MAX_TEMP_SERVICE_DURATION_MS;
+    }
+
+    private class PredictionManagerServiceStub extends IPredictionManager.Stub {
+
+        @Override
+        public void createPredictionSession(@NonNull AppPredictionContext context,
+                @NonNull AppPredictionSessionId sessionId) {
+            runForUserLocked((service) ->
+                    service.onCreatePredictionSessionLocked(context, sessionId));
+        }
+
+        @Override
+        public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
+                @NonNull AppTargetEvent event) {
+            runForUserLocked((service) -> service.notifyAppTargetEventLocked(sessionId, event));
+        }
+
+        @Override
+        public void notifyLocationShown(@NonNull AppPredictionSessionId sessionId,
+                @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
+            runForUserLocked((service) ->
+                    service.notifyLocationShownLocked(sessionId, launchLocation, targetIds));
+        }
+
+        @Override
+        public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
+                @NonNull ParceledListSlice targets,
+                IPredictionCallback callback) {
+            runForUserLocked((service) ->
+                    service.sortAppTargetsLocked(sessionId, targets, callback));
+        }
+
+        @Override
+        public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+                @NonNull IPredictionCallback callback) {
+            runForUserLocked((service) ->
+                    service.registerPredictionUpdatesLocked(sessionId, callback));
+        }
+
+        public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+                @NonNull IPredictionCallback callback) {
+            runForUserLocked((service) ->
+                    service.unregisterPredictionUpdatesLocked(sessionId, callback));
+        }
+
+        @Override
+        public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
+            runForUserLocked((service) -> service.requestPredictionUpdateLocked(sessionId));
+        }
+
+        @Override
+        public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
+            runForUserLocked((service) -> service.onDestroyPredictionSessionLocked(sessionId));
+        }
+
+        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+                @Nullable FileDescriptor err,
+                @NonNull String[] args, @Nullable ShellCallback callback,
+                @NonNull ResultReceiver resultReceiver) throws RemoteException {
+            new AppPredictionManagerServiceShellCommand(AppPredictionManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+
+        private void runForUserLocked(@NonNull Consumer<AppPredictionPerUserService> c) {
+            final int userId = UserHandle.getCallingUserId();
+            // TODO(b/111701043): Determine what permission model we want for this
+            long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    final AppPredictionPerUserService service = getServiceForUserLocked(userId);
+                    c.accept(service);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerServiceShellCommand.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerServiceShellCommand.java
new file mode 100644
index 0000000..ed7cc67
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerServiceShellCommand.java
@@ -0,0 +1,83 @@
+/*
+ * 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.appprediction;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the AppPredictionManagerService.
+ */
+public class AppPredictionManagerServiceShellCommand extends ShellCommand {
+
+    private static final String TAG =
+            AppPredictionManagerServiceShellCommand.class.getSimpleName();
+
+    private final AppPredictionManagerService mService;
+
+    public AppPredictionManagerServiceShellCommand(@NonNull AppPredictionManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "set": {
+                final String what = getNextArgRequired();
+                switch (what) {
+                    case "temporary-service": {
+                        final int userId = Integer.parseInt(getNextArgRequired());
+                        String serviceName = getNextArg();
+                        if (serviceName == null) {
+                            mService.resetTemporaryService(userId);
+                            return 0;
+                        }
+                        final int duration = Integer.parseInt(getNextArgRequired());
+                        mService.setTemporaryService(userId, serviceName, duration);
+                        pw.println("AppPredictionService temporarily set to " + serviceName
+                                + " for " + duration + "ms");
+                        break;
+                    }
+                }
+            }
+            break;
+            default:
+                return handleDefaultCommands(cmd);
+        }
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter()) {
+            pw.println("AppPredictionManagerService commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+            pw.println("    Temporarily (for DURATION ms) changes the service implemtation.");
+            pw.println("    To reset, call with just the USER_ID argument.");
+            pw.println("");
+        }
+    }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
new file mode 100644
index 0000000..24d592c
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -0,0 +1,218 @@
+/*
+ * 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.appprediction;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.service.appprediction.AppPredictionService;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user instance of {@link AppPredictionManagerService}.
+ */
+public class AppPredictionPerUserService extends
+        AbstractPerUserSystemService<AppPredictionPerUserService, AppPredictionManagerService>
+             implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks {
+
+    private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
+
+    @Nullable
+    @GuardedBy("mLock")
+    private RemoteAppPredictionService mRemoteService;
+
+    protected AppPredictionPerUserService(AppPredictionManagerService master,
+            Object lock, int userId) {
+        super(master, lock, userId);
+    }
+
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException {
+
+        ServiceInfo si;
+        try {
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new NameNotFoundException("Could not get service for " + serviceComponent);
+        }
+        // TODO(b/111701043): must check that either the service is from a system component,
+        // or it matches a service set by shell cmd (so it can be used on CTS tests and when
+        // OEMs are implementing the real service and also verify the proper permissions
+        return si;
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        final boolean enabledChanged = super.updateLocked(disabled);
+        if (enabledChanged) {
+            if (!isEnabledLocked()) {
+                // Clear the remote service for the next call
+                mRemoteService = null;
+            }
+        }
+        return enabledChanged;
+    }
+
+    /**
+     * Notifies the service of a new prediction session.
+     */
+    @GuardedBy("mLock")
+    public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
+            @NonNull AppPredictionSessionId sessionId) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.onCreatePredictionSession(context, sessionId);
+        }
+    }
+
+    /**
+     * Records an app target event to the service.
+     */
+    @GuardedBy("mLock")
+    public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
+            @NonNull AppTargetEvent event) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.notifyAppTargetEvent(sessionId, event);
+        }
+    }
+
+    /**
+     * Records when a launch location is shown.
+     */
+    @GuardedBy("mLock")
+    public void notifyLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
+            @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.notifyLocationShown(sessionId, launchLocation, targetIds);
+        }
+    }
+
+    /**
+     * Requests the service to sort a list of apps or shortcuts.
+     */
+    @GuardedBy("mLock")
+    public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
+            @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.sortAppTargets(sessionId, targets, callback);
+        }
+    }
+
+    /**
+     * Registers a callback for continuous updates of predicted apps or shortcuts.
+     */
+    @GuardedBy("mLock")
+    public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
+            @NonNull IPredictionCallback callback) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.registerPredictionUpdates(sessionId, callback);
+        }
+    }
+
+    /**
+     * Unregisters a callback for continuous updates of predicted apps or shortcuts.
+     */
+    @GuardedBy("mLock")
+    public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
+            @NonNull IPredictionCallback callback) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.unregisterPredictionUpdates(sessionId, callback);
+        }
+    }
+
+    /**
+     * Requests a new set of predicted apps or shortcuts.
+     */
+    @GuardedBy("mLock")
+    public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.requestPredictionUpdate(sessionId);
+        }
+    }
+
+    /**
+     * Notifies the service of the end of an existing prediction session.
+     */
+    @GuardedBy("mLock")
+    public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.onDestroyPredictionSession(sessionId);
+        }
+    }
+
+    @Override
+    public void onFailureOrTimeout(boolean timedOut) {
+        if (isDebug()) {
+            Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
+        }
+
+        // Do nothing, we are just proxying to the prediction service
+    }
+
+    @Override
+    public void onServiceDied(RemoteAppPredictionService service) {
+        if (isDebug()) {
+            Slog.d(TAG, "onServiceDied():");
+        }
+
+        // Do nothing, we are just proxying to the prediction service
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteAppPredictionService getRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "getRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteAppPredictionService(getContext(),
+                    AppPredictionService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
+                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
+
+        return mRemoteService;
+    }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
new file mode 100644
index 0000000..45ea86f
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
@@ -0,0 +1,142 @@
+/*
+ * 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.appprediction;
+
+import android.annotation.NonNull;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.IBinder;
+import android.service.appprediction.IPredictionService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+
+/**
+ * Proxy to the {@link android.service.appprediction.AppPredictionService} implemention in another
+ * process.
+ */
+public class RemoteAppPredictionService extends
+        AbstractMultiplePendingRequestsRemoteService<RemoteAppPredictionService,
+                IPredictionService> {
+
+    private static final String TAG = "RemoteAppPredictionService";
+
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+    public RemoteAppPredictionService(Context context, String serviceInterface,
+            ComponentName componentName, int userId,
+            RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed,
+            boolean verbose) {
+        super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+                verbose, /* initialCapacity= */ 1);
+    }
+
+    @Override
+    protected IPredictionService getServiceInterface(IBinder service) {
+        return IPredictionService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return PERMANENT_BOUND_TIMEOUT_MS;
+    }
+
+    @Override
+    protected long getRemoteRequestMillis() {
+        return TIMEOUT_REMOTE_REQUEST_MILLIS;
+    }
+
+    /**
+     * Notifies the service of a new prediction session.
+     */
+    public void onCreatePredictionSession(@NonNull AppPredictionContext context,
+            @NonNull AppPredictionSessionId sessionId) {
+        scheduleAsyncRequest((s) -> s.onCreatePredictionSession(context, sessionId));
+    }
+
+    /**
+     * Records an app target event to the service.
+     */
+    public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
+            @NonNull AppTargetEvent event) {
+        scheduleAsyncRequest((s) -> s.notifyAppTargetEvent(sessionId, event));
+    }
+
+    /**
+     * Records when a launch location is shown.
+     */
+    public void notifyLocationShown(@NonNull AppPredictionSessionId sessionId,
+            @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
+        scheduleAsyncRequest((s) -> s.notifyLocationShown(sessionId, launchLocation, targetIds));
+    }
+
+    /**
+     * Requests the service to sort a list of apps or shortcuts.
+     */
+    public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
+            @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
+        scheduleAsyncRequest((s) -> s.sortAppTargets(sessionId, targets, callback));
+    }
+
+
+    /**
+     * Registers a callback for continuous updates of predicted apps or shortcuts.
+     */
+    public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+            @NonNull IPredictionCallback callback) {
+        scheduleAsyncRequest((s) -> s.registerPredictionUpdates(sessionId, callback));
+    }
+
+    /**
+     * Unregisters a callback for continuous updates of predicted apps or shortcuts.
+     */
+    public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+            @NonNull IPredictionCallback callback) {
+        scheduleAsyncRequest((s) -> s.unregisterPredictionUpdates(sessionId, callback));
+    }
+
+    /**
+     * Requests a new set of predicted apps or shortcuts.
+     */
+    public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
+        scheduleAsyncRequest((s) -> s.requestPredictionUpdate(sessionId));
+    }
+
+    /**
+     * Notifies the service of the end of an existing prediction session.
+     */
+    public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
+        scheduleAsyncRequest((s) -> s.onDestroyPredictionSession(sessionId));
+    }
+
+    /**
+     * Failure callback
+     */
+    public interface RemoteAppPredictionServiceCallbacks
+            extends VultureCallback<RemoteAppPredictionService> {
+
+        /**
+         * Notifies a the failure or timeout of a remote call.
+         */
+        void onFailureOrTimeout(boolean timedOut);
+    }
+}
diff --git a/services/art-profile b/services/art-profile
index af9d7a9..7e4884b 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -6970,6 +6970,7 @@
 PLcom/android/server/am/ActivityManagerService$LocalService;->getMemoryStateForProcesses()Ljava/util/List;
 PLcom/android/server/am/ActivityManagerService$LocalService;->getTopVisibleActivities()Ljava/util/List;
 PLcom/android/server/am/ActivityManagerService$LocalService;->getUidProcessState(I)I
+PLcom/android/server/am/ActivityManagerService$LocalService;->isAppForeground(I)Z
 PLcom/android/server/am/ActivityManagerService$LocalService;->isCallerRecents(I)Z
 PLcom/android/server/am/ActivityManagerService$LocalService;->isRuntimeRestarted()Z
 PLcom/android/server/am/ActivityManagerService$LocalService;->isSystemReady()Z
@@ -7125,7 +7126,6 @@
 PLcom/android/server/am/ActivityManagerService;->installEncryptionUnawareProviders(I)V
 PLcom/android/server/am/ActivityManagerService;->installSystemProviders()V
 PLcom/android/server/am/ActivityManagerService;->isAllowedWhileBooting(Landroid/content/pm/ApplicationInfo;)Z
-PLcom/android/server/am/ActivityManagerService;->isAppForeground(I)Z
 PLcom/android/server/am/ActivityManagerService;->isAssistDataAllowedOnCurrentActivity()Z
 PLcom/android/server/am/ActivityManagerService;->isGetTasksAllowed(Ljava/lang/String;II)Z
 PLcom/android/server/am/ActivityManagerService;->isInLockTaskMode()Z
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 1a6bee9..c992da4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
 import static android.util.DebugUtils.flagsToString;
+import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -70,6 +71,7 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.SyncResultReceiver;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.autofill.ui.AutoFillUI;
@@ -100,8 +102,6 @@
 
     private static final Object sLock = AutofillManagerService.class;
 
-    private static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
-
     /**
      * IME supports Smart Suggestions.
      */
@@ -252,9 +252,8 @@
     @Override // from AbstractMasterSystemService
     protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
-        return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory,
-                mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState,
-                disabled);
+        return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory,
+                mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled);
     }
 
     @Override // AbstractMasterSystemService
@@ -291,6 +290,13 @@
         return mSupportedSmartSuggestionModes;
     }
 
+    /**
+     * Logs a request so it's dumped later...
+     */
+    void logRequestLocked(@NonNull String historyItem) {
+        mRequestsHistory.log(historyItem);
+    }
+
     // Called by AutofillManagerServiceImpl, doesn't need to check permission
     boolean isInstantServiceAllowed() {
         return mAllowInstantService;
@@ -606,15 +612,15 @@
     }
 
     private void send(@NonNull IResultReceiver receiver, @Nullable String value) {
-        send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+        send(receiver, SyncResultReceiver.bundleFor(value));
     }
 
     private void send(@NonNull IResultReceiver receiver, @Nullable String[] value) {
-        send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+        send(receiver, SyncResultReceiver.bundleFor(value));
     }
 
     private void send(@NonNull IResultReceiver receiver, @Nullable Parcelable value) {
-        send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+        send(receiver, SyncResultReceiver.bundleFor(value));
     }
 
     private void send(@NonNull IResultReceiver receiver, boolean value) {
@@ -701,6 +707,11 @@
         public void onBackKeyPressed() {
             if (sDebug) Slog.d(TAG, "onBackKeyPressed()");
             mUi.hideAll(null);
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        getServiceForUserLocked(UserHandle.getCallingUserId());
+                service.onBackKeyPressed();
+            }
         }
 
         @Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4b7efe1..954b67e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -108,7 +108,6 @@
 
     private static final Random sRandom = new Random();
 
-    private final LocalLog mRequestsHistory;
     private final LocalLog mUiLatencyHistory;
     private final LocalLog mWtfHistory;
     private final FieldClassificationStrategy mFieldClassificationStrategy;
@@ -166,12 +165,12 @@
     @Nullable
     private RemoteAugmentedAutofillService mRemoteAugmentedAutofillService;
 
-    AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
+    AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
-            AutofillCompatState autofillCompatState, boolean disabled) {
+            AutofillCompatState autofillCompatState,
+            boolean disabled) {
         super(master, lock, userId);
 
-        mRequestsHistory = requestsHistory;
         mUiLatencyHistory = uiLatencyHistory;
         mWtfHistory = wtfHistory;
         mUi = ui;
@@ -187,6 +186,15 @@
     }
 
     @GuardedBy("mLock")
+    void onBackKeyPressed() {
+        final RemoteAugmentedAutofillService remoteService =
+                getRemoteAugmentedAutofillServiceLocked();
+        if (remoteService != null) {
+            remoteService.onDestroyAutofillWindowsRequest();
+        }
+    }
+
+    @GuardedBy("mLock")
     @Override // from PerUserSystemService
     protected boolean updateLocked(boolean disabled) {
         destroySessionsLocked();
@@ -301,7 +309,7 @@
                 + " s=" + mInfo.getServiceInfo().packageName
                 + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds
                 + " hc=" + hasCallback + " f=" + flags;
-        mRequestsHistory.log(historyItem);
+        mMaster.logRequestLocked(historyItem);
 
         newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
 
@@ -870,8 +878,11 @@
         }
         pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
                 .getString(R.string.config_defaultAutofillService));
-        pw.print(prefix); pw.print("mAugmentedAutofillNamer: ");
-        mAugmentedAutofillResolver.dumpShort(pw); pw.println();
+
+        pw.print(prefix); pw.println("mAugmentedAutofillNamer: ");
+        pw.print(prefix2); mAugmentedAutofillResolver.dumpShort(pw); pw.println();
+        pw.print(prefix2); mAugmentedAutofillResolver.dumpShort(pw, mUserId); pw.println();
+
         if (mRemoteAugmentedAutofillService != null) {
             pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
             mRemoteAugmentedAutofillService.dump(prefix2, pw);
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 239a386..5d8d8fa 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -109,8 +109,8 @@
     /**
      * Called by {@link Session} when it's time to destroy all augmented autofill requests.
      */
-    public void onDestroyAutofillWindowsRequest(int sessionId) {
-        scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId));
+    public void onDestroyAutofillWindowsRequest() {
+        scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest());
     }
 
     // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass
@@ -168,7 +168,7 @@
                 }
             };
 
-            // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
+            // TODO(b/122728762): set cancellation signal, timeout (from both mClient and service),
             // cache IAugmentedAutofillManagerClient reference, etc...
             try {
                 mClient.getAugmentedAutofillClient(receiver);
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 34fe5d9..e8a52b4 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -139,9 +139,6 @@
         if (mPendingRequest == pendingRequest) {
             mPendingRequest = null;
         }
-        if (mPendingRequest == null) {
-            scheduleUnbind();
-        }
         return true;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0348f2b..7dfd8fe 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2581,7 +2581,10 @@
 
         final RemoteAugmentedAutofillService remoteService = mService
                 .getRemoteAugmentedAutofillServiceLocked();
-        if (remoteService == null) return null;
+        if (remoteService == null) {
+            if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user");
+            return null;
+        }
 
         // Define which mode will be used
         final int mode;
@@ -2607,6 +2610,13 @@
                     + " when server returned null for session " + this.id);
         }
 
+        final String historyItem =
+                "aug:id=" + id + " u=" + uid + " m=" + mode
+                + " a=" + ComponentName.flattenToShortString(mComponentName)
+                + " f=" + mCurrentViewId
+                + " s=" + remoteService.getComponentName();
+        mService.getMaster().logRequestLocked(historyItem);
+
         final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue();
 
         // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
@@ -2616,7 +2626,7 @@
                 currentValue);
 
         if (mAugmentedAutofillDestroyer == null) {
-            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(id);
+            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
         }
         return mAugmentedAutofillDestroyer;
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8b07db7..ff378b3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -39,7 +39,6 @@
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Slog;
@@ -127,7 +126,7 @@
     protected void startServiceForUser(int userId) {
         UserBackupManagerService userBackupManagerService =
                 UserBackupManagerService.createAndInitializeService(
-                        userId, mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+                        userId, mContext, mTrampoline, mTransportWhitelist);
         startServiceForUser(userId, userBackupManagerService);
     }
 
@@ -139,20 +138,21 @@
         mServiceUsers.put(userId, userBackupManagerService);
 
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
-        try {
-            // TODO(b/121198604): Make enable file per-user and clean up indirection.
-            mTrampoline.setBackupEnabledForUser(
-                    userId, UserBackupManagerFilePersistedSettings.readBackupEnableState(userId));
-        } catch (RemoteException e) {
-            // Can't happen, it's a local object.
-        }
+        userBackupManagerService.initializeBackupEnableState();
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     /** Stops the backup service for user {@code userId} when the user is stopped. */
     @VisibleForTesting
     protected void stopServiceForUser(int userId) {
-        mServiceUsers.remove(userId);
+        UserBackupManagerService userBackupManagerService = mServiceUsers.removeReturnOld(userId);
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.tearDownService();
+
+            KeyValueBackupJob.cancel(userId, mContext);
+            FullBackupJob.cancel(userId, mContext);
+        }
     }
 
     SparseArray<UserBackupManagerService> getServiceUsers() {
@@ -580,9 +580,9 @@
      * @return Whether ongoing work will continue. The return value here will be passed along as the
      *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
      */
-    public boolean beginFullBackup(FullBackupJob scheduledJob) {
+    public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
         UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
+                getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
 
         return userBackupManagerService != null
                 && userBackupManagerService.beginFullBackup(scheduledJob);
@@ -592,9 +592,9 @@
      * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
      * longer met for running the full backup job.
      */
-    public void endFullBackup() {
+    public void endFullBackup(@UserIdInt int userId) {
         UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
+                getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
 
         if (userBackupManagerService != null) {
             userBackupManagerService.endFullBackup();
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 82638b4..33d21cb0 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -22,21 +22,30 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 public class FullBackupJob extends JobService {
-    private static final String TAG = "FullBackupJob";
-    private static final boolean DEBUG = true;
+    private static final String USER_ID_EXTRA_KEY = "userId";
+
+    @VisibleForTesting
+    static final int MIN_JOB_ID = 52418896;
+    @VisibleForTesting
+    static final int MAX_JOB_ID = 52419896;
 
     private static ComponentName sIdleService =
             new ComponentName("android", FullBackupJob.class.getName());
 
-    private static final int JOB_ID = 0x5038;
+    @GuardedBy("mParamsForUser")
+    private final SparseArray<JobParameters> mParamsForUser = new SparseArray<>();
 
-    JobParameters mParams;
-
-    public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
+    public static void schedule(int userId, Context ctx, long minDelay,
+            BackupManagerConstants constants) {
         JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService);
+        JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId), sIdleService);
         synchronized (constants) {
             builder.setRequiresDeviceIdle(true)
                     .setRequiredNetworkType(constants.getFullBackupRequiredNetworkType())
@@ -45,14 +54,28 @@
         if (minDelay > 0) {
             builder.setMinimumLatency(minDelay);
         }
+
+        Bundle extraInfo = new Bundle();
+        extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+        builder.setTransientExtras(extraInfo);
+
         js.schedule(builder.build());
     }
 
+    public static void cancel(int userId, Context ctx) {
+        JobScheduler js = (JobScheduler) ctx.getSystemService(
+                Context.JOB_SCHEDULER_SERVICE);
+        js.cancel(getJobIdForUserId(userId));
+    }
+
     // callback from the Backup Manager Service: it's finished its work for this pass
-    public void finishBackupPass() {
-        if (mParams != null) {
-            jobFinished(mParams, false);
-            mParams = null;
+    public void finishBackupPass(int userId) {
+        synchronized (mParamsForUser) {
+            JobParameters jobParameters = mParamsForUser.get(userId);
+            if (jobParameters != null) {
+                jobFinished(jobParameters, false);
+                mParamsForUser.remove(userId);
+            }
         }
     }
 
@@ -60,19 +83,33 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
-        mParams = params;
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+        synchronized (mParamsForUser) {
+            mParamsForUser.put(userId, params);
+        }
+
         Trampoline service = BackupManagerService.getInstance();
-        return service.beginFullBackup(this);
+        return service.beginFullBackup(userId, this);
     }
 
     @Override
     public boolean onStopJob(JobParameters params) {
-        if (mParams != null) {
-            mParams = null;
-            Trampoline service = BackupManagerService.getInstance();
-            service.endFullBackup();
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+        synchronized (mParamsForUser) {
+            if (mParamsForUser.removeReturnOld(userId) == null) {
+                return false;
+            }
         }
+
+        Trampoline service = BackupManagerService.getInstance();
+        service.endFullBackup(userId);
+
         return false;
     }
 
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/JobIdManager.java b/services/backup/java/com/android/server/backup/JobIdManager.java
new file mode 100644
index 0000000..2e834dbf
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/JobIdManager.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 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;
+
+/**
+ * Allocates job IDs for {@link FullBackupJob} and {@link KeyValueBackupJob}
+ */
+public class JobIdManager {
+    public static int getJobIdForUserId(int minJobId, int maxJobId, int userId) {
+        if (minJobId + userId > maxJobId) {
+            throw new RuntimeException("No job IDs available in the given range");
+        }
+
+        return minJobId + userId;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bed520e..3184bd8 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -13,8 +13,6 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.server.backup.restore.PerformAdbRestoreTask;
-
 import libcore.io.IoUtils;
 
 import java.io.File;
@@ -42,11 +40,10 @@
     private final UserBackupManagerService mBackupManagerService;
     private final File mDataDir;
 
-    FileMetadata mInfo;
-    PerformAdbRestoreTask mRestoreTask;
-    ParcelFileDescriptor mInFD;
-    IBackupAgent mAgent;
-    int mToken;
+    private final FileMetadata mInfo;
+    private final ParcelFileDescriptor mInFD;
+    private final IBackupAgent mAgent;
+    private final int mToken;
 
     public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
             File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index f2e7435..d43859e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -25,8 +25,14 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Random;
 
@@ -38,7 +44,8 @@
     private static final String TAG = "KeyValueBackupJob";
     private static ComponentName sKeyValueJobService =
             new ComponentName("android", KeyValueBackupJob.class.getName());
-    private static final int JOB_ID = 0x5039;
+
+    private static final String USER_ID_EXTRA_KEY = "userId";
 
     // Once someone asks for a backup, this is how long we hold off until we find
     // an on-charging opportunity.  If we hit this max latency we will run the operation
@@ -46,16 +53,22 @@
     // BackupManager.backupNow().
     private static final long MAX_DEFERRAL = AlarmManager.INTERVAL_DAY;
 
-    private static boolean sScheduled = false;
-    private static long sNextScheduled = 0;
+    @GuardedBy("KeyValueBackupJob.class")
+    private static final SparseBooleanArray sScheduledForUserId = new SparseBooleanArray();
+    @GuardedBy("KeyValueBackupJob.class")
+    private static final SparseLongArray sNextScheduledForUserId = new SparseLongArray();
 
-    public static void schedule(Context ctx, BackupManagerConstants constants) {
-        schedule(ctx, 0, constants);
+    private static final int MIN_JOB_ID = 52417896;
+    private static final int MAX_JOB_ID = 52418896;
+
+    public static void schedule(int userId, Context ctx, BackupManagerConstants constants) {
+        schedule(userId, ctx, 0, constants);
     }
 
-    public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+    public static void schedule(int userId, Context ctx, long delay,
+            BackupManagerConstants constants) {
         synchronized (KeyValueBackupJob.class) {
-            if (sScheduled) {
+            if (sScheduledForUserId.get(userId)) {
                 return;
             }
 
@@ -76,51 +89,61 @@
             if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
             }
-            JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
+
+            JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId),
+                    sKeyValueJobService)
                     .setMinimumLatency(delay)
                     .setRequiredNetworkType(networkType)
                     .setRequiresCharging(needsCharging)
                     .setOverrideDeadline(MAX_DEFERRAL);
+
+            Bundle extraInfo = new Bundle();
+            extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+            builder.setTransientExtras(extraInfo);
+
             JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
             js.schedule(builder.build());
 
-            sNextScheduled = System.currentTimeMillis() + delay;
-            sScheduled = true;
+            sScheduledForUserId.put(userId, true);
+            sNextScheduledForUserId.put(userId, System.currentTimeMillis() + delay);
         }
     }
 
-    public static void cancel(Context ctx) {
+    public static void cancel(int userId, Context ctx) {
         synchronized (KeyValueBackupJob.class) {
-            JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-            js.cancel(JOB_ID);
-            sNextScheduled = 0;
-            sScheduled = false;
+            JobScheduler js = (JobScheduler) ctx.getSystemService(
+                    Context.JOB_SCHEDULER_SERVICE);
+            js.cancel(getJobIdForUserId(userId));
+
+            clearScheduledForUserId(userId);
         }
     }
 
-    public static long nextScheduled() {
+    public static long nextScheduled(int userId) {
         synchronized (KeyValueBackupJob.class) {
-            return sNextScheduled;
+            return sNextScheduledForUserId.get(userId);
         }
     }
 
-    public static boolean isScheduled() {
+    @VisibleForTesting
+    public static boolean isScheduled(int userId) {
         synchronized (KeyValueBackupJob.class) {
-            return sScheduled;
+            return sScheduledForUserId.get(userId);
         }
     }
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
         synchronized (KeyValueBackupJob.class) {
-            sNextScheduled = 0;
-            sScheduled = false;
+            clearScheduledForUserId(userId);
         }
 
         // Time to run a key/value backup!
         Trampoline service = BackupManagerService.getInstance();
         try {
-            service.backupNow();
+            service.backupNowForUser(userId);
         } catch (RemoteException e) {}
 
         // This was just a trigger; ongoing wakelock management is done by the
@@ -134,4 +157,13 @@
         return false;
     }
 
+    @GuardedBy("KeyValueBackupJob.class")
+    private static void clearScheduledForUserId(int userId) {
+        sScheduledForUserId.delete(userId);
+        sNextScheduledForUserId.delete(userId);
+    }
+
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index e4ce62d..4a1e5b9 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -23,9 +23,9 @@
 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.ResolveInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
 import android.content.pm.SigningInfo;
 import android.os.Build;
@@ -49,9 +49,8 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
-
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * We back up the signatures of each package so that during a system restore,
@@ -95,6 +94,7 @@
     // is coming from pre-Android P device.
     private static final int UNDEFINED_ANCESTRAL_RECORD_VERSION = -1;
 
+    private int mUserId;
     private List<PackageInfo> mAllPackages;
     private PackageManager mPackageManager;
     // version & signature info of each app in a restore set
@@ -129,17 +129,18 @@
 
     // We're constructed with the set of applications that are participating
     // in backup.  This set changes as apps are installed & removed.
-    public PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
-        init(packageMgr, packages);
+    public PackageManagerBackupAgent(
+            PackageManager packageMgr, List<PackageInfo> packages, int userId) {
+        init(packageMgr, packages, userId);
     }
 
-    public PackageManagerBackupAgent(PackageManager packageMgr) {
-        init(packageMgr, null);
+    public PackageManagerBackupAgent(PackageManager packageMgr, int userId) {
+        init(packageMgr, null, userId);
 
         evaluateStorablePackages();
     }
 
-    private void init(PackageManager packageMgr, List<PackageInfo> packages) {
+    private void init(PackageManager packageMgr, List<PackageInfo> packages, int userId) {
         mPackageManager = packageMgr;
         mAllPackages = packages;
         mRestoredSignatures = null;
@@ -147,17 +148,19 @@
 
         mStoredSdkVersion = Build.VERSION.SDK_INT;
         mStoredIncrementalVersion = Build.VERSION.INCREMENTAL;
+        mUserId = userId;
     }
 
     // We will need to refresh our understanding of what is eligible for
     // backup periodically; this entry point serves that purpose.
     public void evaluateStorablePackages() {
-        mAllPackages = getStorableApplications(mPackageManager);
+        mAllPackages = getStorableApplications(mPackageManager, mUserId);
     }
 
-    public static List<PackageInfo> getStorableApplications(PackageManager pm) {
-        List<PackageInfo> pkgs;
-        pkgs = pm.getInstalledPackages(PackageManager.GET_SIGNING_CERTIFICATES);
+    /** Gets all packages installed on user {@code userId} eligible for backup. */
+    public static List<PackageInfo> getStorableApplications(PackageManager pm, int userId) {
+        List<PackageInfo> pkgs =
+                pm.getInstalledPackagesAsUser(PackageManager.GET_SIGNING_CERTIFICATES, userId);
         int N = pkgs.size();
         for (int a = N-1; a >= 0; a--) {
             PackageInfo pkg = pkgs.get(a);
@@ -237,8 +240,8 @@
         ComponentName home = getPreferredHomeComponent();
         if (home != null) {
             try {
-                homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
-                        PackageManager.GET_SIGNING_CERTIFICATES);
+                homeInfo = mPackageManager.getPackageInfoAsUser(home.getPackageName(),
+                        PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
                 homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
                 homeVersion = homeInfo.getLongVersionCode();
                 SigningInfo signingInfo = homeInfo.signingInfo;
@@ -315,8 +318,8 @@
                 } else {
                     PackageInfo info = null;
                     try {
-                        info = mPackageManager.getPackageInfo(packName,
-                                PackageManager.GET_SIGNING_CERTIFICATES);
+                        info = mPackageManager.getPackageInfoAsUser(packName,
+                                PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
                     } catch (NameNotFoundException e) {
                         // Weird; we just found it, and now are told it doesn't exist.
                         // Treat it as having been removed from the device.
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 79d4a2c..4ca2545 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -698,15 +698,15 @@
 
     // Full backup/restore entry points - non-Binder; called directly
     // by the full-backup scheduled job
-    /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
+    /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
+        return (svc != null) ? svc.beginFullBackup(userId, scheduledJob) : false;
     }
 
-    /* package */ void endFullBackup() {
+    /* package */ void endFullBackup(@UserIdInt int userId) {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.endFullBackup();
+            svc.endFullBackup(userId);
         }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index ddce6bb..a7bada0 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -17,6 +17,7 @@
 package com.android.server.backup;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupTransport;
@@ -29,7 +30,6 @@
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -61,7 +61,7 @@
     public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
 
     private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
-    private final Context mContext;
+    private final @UserIdInt int mUserId;
     private final PackageManager mPackageManager;
     private final Set<ComponentName> mTransportWhitelist;
     private final TransportClientManager mTransportClientManager;
@@ -86,22 +86,24 @@
     @Nullable
     private volatile String mCurrentTransportName;
 
-    TransportManager(Context context, Set<ComponentName> whitelist, String selectedTransport) {
-        mContext = context;
+    TransportManager(@UserIdInt int userId, Context context, Set<ComponentName> whitelist,
+            String selectedTransport) {
+        mUserId = userId;
         mPackageManager = context.getPackageManager();
         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
         mCurrentTransportName = selectedTransport;
         mTransportStats = new TransportStats();
-        mTransportClientManager = new TransportClientManager(context, mTransportStats);
+        mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats);
     }
 
     @VisibleForTesting
     TransportManager(
+            @UserIdInt int userId,
             Context context,
             Set<ComponentName> whitelist,
             String selectedTransport,
             TransportClientManager transportClientManager) {
-        mContext = context;
+        mUserId = userId;
         mPackageManager = context.getPackageManager();
         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
         mCurrentTransportName = selectedTransport;
@@ -560,7 +562,7 @@
     private void registerTransportsFromPackage(
             String packageName, Predicate<ComponentName> transportComponentFilter) {
         try {
-            mPackageManager.getPackageInfo(packageName, 0);
+            mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, "Trying to register transports from package not found " + packageName);
             return;
@@ -575,7 +577,7 @@
     private void registerTransportsForIntent(
             Intent intent, Predicate<ComponentName> transportComponentFilter) {
         List<ResolveInfo> hosts =
-                mPackageManager.queryIntentServicesAsUser(intent, 0, UserHandle.USER_SYSTEM);
+                mPackageManager.queryIntentServicesAsUser(intent, 0, mUserId);
         if (hosts == null) {
             return;
         }
@@ -597,7 +599,8 @@
             return false;
         }
         try {
-            PackageInfo packInfo = mPackageManager.getPackageInfo(transport.getPackageName(), 0);
+            PackageInfo packInfo =
+                    mPackageManager.getPackageInfoAsUser(transport.getPackageName(), 0, mUserId);
             if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
                     == 0) {
                 Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged");
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
index a0feaf9..aabd41a 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -17,29 +17,35 @@
 package com.android.server.backup;
 
 import android.os.Environment;
+import android.os.UserHandle;
 
 import java.io.File;
 
 /** Directories used for user specific backup/restore persistent state and book-keeping. */
-public final class UserBackupManagerFiles {
+final class UserBackupManagerFiles {
     // Name of the directories the service stores bookkeeping data under.
     private static final String BACKUP_PERSISTENT_DIR = "backup";
     private static final String BACKUP_STAGING_DIR = "backup_stage";
 
+    private static File getBaseDir(int userId) {
+        return Environment.getDataSystemCeDirectory(userId);
+    }
+
     static File getBaseStateDir(int userId) {
-        // TODO (b/120424138) this should be per user
+        if (userId != UserHandle.USER_SYSTEM) {
+            return new File(getBaseDir(userId), BACKUP_PERSISTENT_DIR);
+        }
+        // TODO (b/120424138) remove if clause above and use same logic for system user.
+        // simultaneously, copy below dir to new system user dir
         return new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
     }
 
     static File getDataDir(int userId) {
-        // TODO (b/120424138) this should be per user
-        // This dir on /cache is managed directly in init.rc
+        if (userId != UserHandle.USER_SYSTEM) {
+            return new File(getBaseDir(userId), BACKUP_STAGING_DIR);
+        }
+        // TODO (b/120424138) remove if clause above and use same logic for system user. Since this
+        // is a staging dir, we dont need to copy below dir to new system user dir
         return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
     }
-
-    /** Directory used by full backup engine to store state. */
-    public static File getFullBackupEngineFilesDir(int userId) {
-        // TODO (b/120424138) this should be per user
-        return new File("/data/system");
-    }
 }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 6425508..79f8a7e 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -39,6 +39,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
@@ -103,6 +104,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
 import com.android.server.backup.fullbackup.FullBackupEntry;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
 import com.android.server.backup.internal.BackupHandler;
@@ -110,9 +112,9 @@
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
 import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.ProvisionedObserver;
 import com.android.server.backup.internal.RunBackupReceiver;
 import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.internal.SetupObserver;
 import com.android.server.backup.keyvalue.BackupRequest;
 import com.android.server.backup.params.AdbBackupParams;
 import com.android.server.backup.params.AdbParams;
@@ -208,8 +210,8 @@
 
     public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
     public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
-    public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
-    public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+    private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+    private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
 
     // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
     // pending operations list.
@@ -245,26 +247,28 @@
     private final @UserIdInt int mUserId;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final TransportManager mTransportManager;
+    private final HandlerThread mUserBackupThread;
 
-    private Context mContext;
-    private PackageManager mPackageManager;
-    private IPackageManager mPackageManagerBinder;
-    private IActivityManager mActivityManager;
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final IPackageManager mPackageManagerBinder;
+    private final IActivityManager mActivityManager;
+    private final ActivityManagerInternal mActivityManagerInternal;
     private PowerManager mPowerManager;
-    private AlarmManager mAlarmManager;
-    private IStorageManager mStorageManager;
-    private BackupManagerConstants mConstants;
-    private PowerManager.WakeLock mWakelock;
-    private BackupHandler mBackupHandler;
+    private final AlarmManager mAlarmManager;
+    private final IStorageManager mStorageManager;
+    private final BackupManagerConstants mConstants;
+    private final PowerManager.WakeLock mWakelock;
+    private final BackupHandler mBackupHandler;
 
-    private IBackupManager mBackupManagerBinder;
+    private final IBackupManager mBackupManagerBinder;
 
     private boolean mEnabled;   // access to this is synchronized on 'this'
-    private boolean mProvisioned;
+    private boolean mSetupComplete;
     private boolean mAutoRestore;
 
-    private PendingIntent mRunBackupIntent;
-    private PendingIntent mRunInitIntent;
+    private final PendingIntent mRunBackupIntent;
+    private final PendingIntent mRunInitIntent;
 
     private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
 
@@ -272,7 +276,7 @@
     private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
 
     // Backups that we haven't started yet.  Keys are package names.
-    private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+    private final HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
 
     // locking around the pending-backup management
     private final Object mQueueLock = new Object();
@@ -314,9 +318,6 @@
 
     private ActiveRestoreSession mActiveRestoreSession;
 
-    // Watch the device provisioning operation during setup
-    private ContentObserver mProvisionedObserver;
-
     /**
      * mCurrentOperations contains the list of currently active operations.
      *
@@ -342,7 +343,7 @@
     private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
     private final Object mCurrentOpLock = new Object();
     private final Random mTokenGenerator = new Random();
-    final AtomicInteger mNextToken = new AtomicInteger();
+    private final AtomicInteger mNextToken = new AtomicInteger();
 
     // Where we keep our journal files and other bookkeeping.
     private final File mBaseStateDir;
@@ -350,7 +351,7 @@
     private final File mJournalDir;
     @Nullable
     private DataChangedJournal mJournal;
-    private File mFullBackupScheduleFile;
+    private final File mFullBackupScheduleFile;
 
     // Keep a log of all the apps we've ever backed up.
     private ProcessedPackagesJournal mProcessedPackagesJournal;
@@ -371,11 +372,10 @@
             @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
-            HandlerThread backupThread,
             Set<ComponentName> transportWhitelist) {
         String currentTransport =
-                Settings.Secure.getString(
-                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+                Settings.Secure.getStringForUser(
+                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT, userId);
         if (TextUtils.isEmpty(currentTransport)) {
             currentTransport = null;
         }
@@ -384,13 +384,26 @@
             Slog.v(TAG, "Starting with transport " + currentTransport);
         }
         TransportManager transportManager =
-                new TransportManager(context, transportWhitelist, currentTransport);
+                new TransportManager(userId, context, transportWhitelist, currentTransport);
 
         File baseStateDir = UserBackupManagerFiles.getBaseStateDir(userId);
         File dataDir = UserBackupManagerFiles.getDataDir(userId);
 
+        HandlerThread userBackupThread =
+                new HandlerThread("backup-" + userId, Process.THREAD_PRIORITY_BACKGROUND);
+        userBackupThread.start();
+        if (DEBUG) {
+            Slog.d(TAG, "Started thread " + userBackupThread.getName() + " for user " + userId);
+        }
+
         return createAndInitializeService(
-                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId,
+                context,
+                trampoline,
+                userBackupThread,
+                baseStateDir,
+                dataDir,
+                transportManager);
     }
 
     /**
@@ -399,7 +412,7 @@
      * @param userId The user which this service is for.
      * @param context The system server context.
      * @param trampoline A reference to the proxy to {@link BackupManagerService}.
-     * @param backupThread The thread running backup/restore operations for the user.
+     * @param userBackupThread The thread running backup/restore operations for the user.
      * @param baseStateDir The directory we store the user's persistent bookkeeping data.
      * @param dataDir The directory we store the user's temporary staging data.
      * @param transportManager The {@link TransportManager} responsible for handling the user's
@@ -410,19 +423,38 @@
             @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
-            HandlerThread backupThread,
+            HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
         return new UserBackupManagerService(
-                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId,
+                context,
+                trampoline,
+                userBackupThread,
+                baseStateDir,
+                dataDir,
+                transportManager);
+    }
+
+    /**
+     * Returns the value of {@link Settings.Secure#USER_SETUP_COMPLETE} for the specified user
+     * {@code userId} as a {@code boolean}.
+     */
+    public static boolean getSetupCompleteSettingForUser(Context context, int userId) {
+        return Settings.Secure.getIntForUser(
+                context.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                0,
+                userId)
+                != 0;
     }
 
     private UserBackupManagerService(
             @UserIdInt int userId,
             Context context,
             Trampoline parent,
-            HandlerThread backupThread,
+            HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
@@ -431,6 +463,7 @@
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
         mActivityManager = ActivityManager.getService();
+        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
 
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -443,52 +476,78 @@
                 BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
         mAgentTimeoutParameters.start();
 
-        // spin up the backup/restore handler thread
-        checkNotNull(backupThread, "backupThread cannot be null");
-        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+        checkNotNull(userBackupThread, "userBackupThread cannot be null");
+        mUserBackupThread = userBackupThread;
+        mBackupHandler = new BackupHandler(this, userBackupThread.getLooper());
 
         // Set up our bookkeeping
         final ContentResolver resolver = context.getContentResolver();
-        mProvisioned = Settings.Global.getInt(resolver,
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
-        mAutoRestore = Settings.Secure.getInt(resolver,
-                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+        mSetupComplete = getSetupCompleteSettingForUser(context, userId);
+        mAutoRestore = Settings.Secure.getIntForUser(resolver,
+                Settings.Secure.BACKUP_AUTO_RESTORE, 1, userId) != 0;
 
-        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+        ContentObserver setupObserver = new SetupObserver(this, mBackupHandler);
         resolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                false, mProvisionedObserver);
+                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+                /* notifyForDescendents */ false,
+                setupObserver,
+                mUserId);
 
         mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
-        mBaseStateDir.mkdirs();
-        if (!SELinux.restorecon(mBaseStateDir)) {
-            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        // TODO (b/120424138): Remove once the system user is migrated to use the per-user CE
+        // directory. Per-user CE directories are managed by vold.
+        if (userId == UserHandle.USER_SYSTEM) {
+            mBaseStateDir.mkdirs();
+            if (!SELinux.restorecon(mBaseStateDir)) {
+                Slog.w(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+            }
         }
 
+        // TODO (b/120424138): The system user currently uses the cache which is managed by init.rc
+        // Initialization and restorecon is managed by vold for per-user CE directories.
         mDataDir = checkNotNull(dataDir, "dataDir cannot be null");
-
         mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
 
-        // Alarm receivers for scheduled backups & initialization operations
-        BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+        // Receivers for scheduled backups and transport initialization operations.
+        BroadcastReceiver runBackupReceiver = new RunBackupReceiver(this);
         IntentFilter filter = new IntentFilter();
         filter.addAction(RUN_BACKUP_ACTION);
-        context.registerReceiver(mRunBackupReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
+        context.registerReceiverAsUser(
+                runBackupReceiver,
+                UserHandle.of(userId),
+                filter,
+                android.Manifest.permission.BACKUP,
+                /* scheduler */ null);
 
-        BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+        BroadcastReceiver runInitReceiver = new RunInitializeReceiver(this);
         filter = new IntentFilter();
         filter.addAction(RUN_INITIALIZE_ACTION);
-        context.registerReceiver(mRunInitReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
+        context.registerReceiverAsUser(
+                runInitReceiver,
+                UserHandle.of(userId),
+                filter,
+                android.Manifest.permission.BACKUP,
+                /* scheduler */ null);
 
         Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
         backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+        mRunBackupIntent =
+                PendingIntent.getBroadcastAsUser(
+                        context,
+                        /* requestCode */ 0,
+                        backupIntent,
+                        /* flags */ 0,
+                        UserHandle.of(userId));
 
         Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
         initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+        mRunInitIntent =
+                PendingIntent.getBroadcastAsUser(
+                        context,
+                        /* requestCode */ 0,
+                        initIntent,
+                        /* flags */ 0,
+                        UserHandle.of(userId));
 
         // Set up the backup-request journaling
         mJournalDir = new File(mBaseStateDir, "pending");
@@ -523,7 +582,21 @@
         mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
 
         // Power management
-        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*-" + userId);
+    }
+
+    void initializeBackupEnableState() {
+        boolean isEnabled = UserBackupManagerFilePersistedSettings.readBackupEnableState(mUserId);
+        setBackupEnabled(isEnabled);
+    }
+
+    /** Cleans up state when the user of this service is stopped. */
+    void tearDownService() {
+        mUserBackupThread.quit();
+    }
+
+    public @UserIdInt int getUserId() {
+        return mUserId;
     }
 
     public BackupManagerConstants getConstants() {
@@ -538,51 +611,27 @@
         return mContext;
     }
 
-    public void setContext(Context context) {
-        mContext = context;
-    }
-
     public PackageManager getPackageManager() {
         return mPackageManager;
     }
 
-    public void setPackageManager(PackageManager packageManager) {
-        mPackageManager = packageManager;
-    }
-
     public IPackageManager getPackageManagerBinder() {
         return mPackageManagerBinder;
     }
 
-    public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
-        mPackageManagerBinder = packageManagerBinder;
-    }
-
     public IActivityManager getActivityManager() {
         return mActivityManager;
     }
 
-    public void setActivityManager(IActivityManager activityManager) {
-        mActivityManager = activityManager;
-    }
-
     public AlarmManager getAlarmManager() {
         return mAlarmManager;
     }
 
-    public void setAlarmManager(AlarmManager alarmManager) {
-        mAlarmManager = alarmManager;
-    }
-
     @VisibleForTesting
     void setPowerManager(PowerManager powerManager) {
         mPowerManager = powerManager;
     }
 
-    public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
-        mBackupManagerBinder = backupManagerBinder;
-    }
-
     public TransportManager getTransportManager() {
         return mTransportManager;
     }
@@ -595,12 +644,12 @@
         mEnabled = enabled;
     }
 
-    public boolean isProvisioned() {
-        return mProvisioned;
+    public boolean isSetupComplete() {
+        return mSetupComplete;
     }
 
-    public void setProvisioned(boolean provisioned) {
-        mProvisioned = provisioned;
+    public void setSetupComplete(boolean setupComplete) {
+        mSetupComplete = setupComplete;
     }
 
     public PowerManager.WakeLock getWakelock() {
@@ -617,35 +666,18 @@
         mWakelock.setWorkSource(workSource);
     }
 
-    public void setWakelock(PowerManager.WakeLock wakelock) {
-        mWakelock = wakelock;
-    }
-
     public Handler getBackupHandler() {
         return mBackupHandler;
     }
 
-    public void setBackupHandler(BackupHandler backupHandler) {
-        mBackupHandler = backupHandler;
-    }
-
     public PendingIntent getRunInitIntent() {
         return mRunInitIntent;
     }
 
-    public void setRunInitIntent(PendingIntent runInitIntent) {
-        mRunInitIntent = runInitIntent;
-    }
-
     public HashMap<String, BackupRequest> getPendingBackups() {
         return mPendingBackups;
     }
 
-    public void setPendingBackups(
-            HashMap<String, BackupRequest> pendingBackups) {
-        mPendingBackups = pendingBackups;
-    }
-
     public Object getQueueLock() {
         return mQueueLock;
     }
@@ -658,10 +690,6 @@
         mBackupRunning = backupRunning;
     }
 
-    public long getLastBackupPass() {
-        return mLastBackupPass;
-    }
-
     public void setLastBackupPass(long lastBackupPass) {
         mLastBackupPass = lastBackupPass;
     }
@@ -670,10 +698,6 @@
         return mClearDataLock;
     }
 
-    public boolean isClearingData() {
-        return mClearingData;
-    }
-
     public void setClearingData(boolean clearingData) {
         mClearingData = clearingData;
     }
@@ -694,11 +718,6 @@
         return mActiveRestoreSession;
     }
 
-    public void setActiveRestoreSession(
-            ActiveRestoreSession activeRestoreSession) {
-        mActiveRestoreSession = activeRestoreSession;
-    }
-
     public SparseArray<Operation> getCurrentOperations() {
         return mCurrentOperations;
     }
@@ -732,18 +751,10 @@
         return mRng;
     }
 
-    public Set<String> getAncestralPackages() {
-        return mAncestralPackages;
-    }
-
     public void setAncestralPackages(Set<String> ancestralPackages) {
         mAncestralPackages = ancestralPackages;
     }
 
-    public long getAncestralToken() {
-        return mAncestralToken;
-    }
-
     public void setAncestralToken(long ancestralToken) {
         mAncestralToken = ancestralToken;
     }
@@ -791,7 +802,7 @@
      * non-lifecycle agent instance, so we manually set up the context topology for it.
      */
     public BackupAgent makeMetadataAgent() {
-        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId);
         pmAgent.attach(mContext);
         pmAgent.onCreate();
         return pmAgent;
@@ -802,7 +813,7 @@
      */
     public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
         PackageManagerBackupAgent pmAgent =
-                new PackageManagerBackupAgent(mPackageManager, packages);
+                new PackageManagerBackupAgent(mPackageManager, packages, mUserId);
         pmAgent.attach(mContext);
         pmAgent.onCreate();
         return pmAgent;
@@ -844,26 +855,36 @@
             mFullBackupQueue = readFullBackupSchedule();
         }
 
-        // Register for broadcasts about package install, etc., so we can
-        // update the provider list.
+        // Register for broadcasts about package changes.
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
-        mContext.registerReceiver(mBroadcastReceiver, filter);
+        mContext.registerReceiverAsUser(
+                mBroadcastReceiver,
+                UserHandle.of(mUserId),
+                filter,
+                /* broadcastPermission */ null,
+                /* scheduler */ null);
+
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+        mContext.registerReceiverAsUser(
+                mBroadcastReceiver,
+                UserHandle.of(mUserId),
+                sdFilter,
+                /* broadcastPermission */ null,
+                /* scheduler */ null);
     }
 
     private ArrayList<FullBackupEntry> readFullBackupSchedule() {
         boolean changed = false;
         ArrayList<FullBackupEntry> schedule = null;
         List<PackageInfo> apps =
-                PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+                PackageManagerBackupAgent.getStorableApplications(mPackageManager, mUserId);
 
         if (mFullBackupScheduleFile.exists()) {
             try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
@@ -1121,17 +1142,23 @@
         }
     }
 
-    // ----- Track installation/removal of packages -----
+    /**
+     * A {@link BroadcastReceiver} tracking changes to packages and sd cards in order to update our
+     * internal bookkeeping.
+     */
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
-            if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "Received broadcast " + intent);
+            }
 
             String action = intent.getAction();
             boolean replacing = false;
             boolean added = false;
             boolean changed = false;
             Bundle extras = intent.getExtras();
-            String[] pkgList = null;
+            String[] packageList = null;
+
             if (Intent.ACTION_PACKAGE_ADDED.equals(action)
                     || Intent.ACTION_PACKAGE_REMOVED.equals(action)
                     || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -1139,69 +1166,70 @@
                 if (uri == null) {
                     return;
                 }
-                final String pkgName = uri.getSchemeSpecificPart();
-                if (pkgName != null) {
-                    pkgList = new String[]{pkgName};
-                }
-                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
 
-                // At package-changed we only care about looking at new transport states
+                String packageName = uri.getSchemeSpecificPart();
+                if (packageName != null) {
+                    packageList = new String[]{packageName};
+                }
+
+                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
                 if (changed) {
-                    final String[] components =
+                    // Look at new transport states for package changed events.
+                    String[] components =
                             intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
 
                     if (MORE_DEBUG) {
-                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+                        Slog.i(TAG, "Package " + packageName + " changed");
                         for (int i = 0; i < components.length; i++) {
                             Slog.i(TAG, "   * " + components[i]);
                         }
                     }
 
                     mBackupHandler.post(
-                            () -> mTransportManager.onPackageChanged(pkgName, components));
-                    return; // nothing more to do in the PACKAGE_CHANGED case
+                            () -> mTransportManager.onPackageChanged(packageName, components));
+                    return;
                 }
 
                 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
                 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                 added = true;
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 added = false;
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
 
-            if (pkgList == null || pkgList.length == 0) {
+            if (packageList == null || packageList.length == 0) {
                 return;
             }
 
-            final int uid = extras.getInt(Intent.EXTRA_UID);
+            int uid = extras.getInt(Intent.EXTRA_UID);
             if (added) {
                 synchronized (mBackupParticipants) {
                     if (replacing) {
-                        // This is the package-replaced case; we just remove the entry
-                        // under the old uid and fall through to re-add.  If an app
-                        // just added key/value backup participation, this picks it up
-                        // as a known participant.
-                        removePackageParticipantsLocked(pkgList, uid);
+                        // Remove the entry under the old uid and fall through to re-add. If an app
+                        // just opted into key/value backup, add it as a known participant.
+                        removePackageParticipantsLocked(packageList, uid);
                     }
-                    addPackageParticipantsLocked(pkgList);
+                    addPackageParticipantsLocked(packageList);
                 }
-                // If they're full-backup candidates, add them there instead
-                final long now = System.currentTimeMillis();
-                for (final String packageName : pkgList) {
+
+                long now = System.currentTimeMillis();
+                for (String packageName : packageList) {
                     try {
-                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
+                        PackageInfo app =
+                                mPackageManager.getPackageInfoAsUser(
+                                        packageName, /* flags */ 0, mUserId);
                         if (AppBackupUtils.appGetsFullBackup(app)
                                 && AppBackupUtils.appIsEligibleForBackup(
                                 app.applicationInfo, mPackageManager)) {
                             enqueueFullBackup(packageName, now);
                             scheduleNextFullBackupJob(0);
                         } else {
-                            // The app might have just transitioned out of full-data into
-                            // doing key/value backups, or might have just disabled backups
-                            // entirely.  Make sure it is no longer in the full-data queue.
+                            // The app might have just transitioned out of full-data into doing
+                            // key/value backups, or might have just disabled backups entirely. Make
+                            // sure it is no longer in the full-data queue.
                             synchronized (mQueueLock) {
                                 dequeueFullBackupLocked(packageName);
                             }
@@ -1210,32 +1238,28 @@
 
                         mBackupHandler.post(
                                 () -> mTransportManager.onPackageAdded(packageName));
-
                     } catch (NameNotFoundException e) {
-                        // doesn't really exist; ignore it
                         if (DEBUG) {
                             Slog.w(TAG, "Can't resolve new app " + packageName);
                         }
                     }
                 }
 
-                // Whenever a package is added or updated we need to update
-                // the package metadata bookkeeping.
+                // Whenever a package is added or updated we need to update the package metadata
+                // bookkeeping.
                 dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
             } else {
-                if (replacing) {
-                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
-                } else {
-                    // Outright removal.  In the full-data case, the app will be dropped
-                    // from the queue when its (now obsolete) name comes up again for
-                    // backup.
+                if (!replacing) {
+                    // Outright removal. In the full-data case, the app will be dropped from the
+                    // queue when its (now obsolete) name comes up again for backup.
                     synchronized (mBackupParticipants) {
-                        removePackageParticipantsLocked(pkgList, uid);
+                        removePackageParticipantsLocked(packageList, uid);
                     }
                 }
-                for (final String pkgName : pkgList) {
+
+                for (String packageName : packageList) {
                     mBackupHandler.post(
-                            () -> mTransportManager.onPackageRemoved(pkgName));
+                            () -> mTransportManager.onPackageRemoved(packageName));
                 }
             }
         }
@@ -1326,7 +1350,7 @@
     private List<PackageInfo> allAgentPackages() {
         // !!! TODO: cache this and regenerate only when necessary
         int flags = PackageManager.GET_SIGNING_CERTIFICATES;
-        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+        List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(flags, mUserId);
         int numPackages = packages.size();
         for (int a = numPackages - 1; a >= 0; a--) {
             PackageInfo pkg = packages.get(a);
@@ -1340,8 +1364,8 @@
                     // we will need the shared library path, so look that up and store it here.
                     // This is used implicitly when we pass the PackageInfo object off to
                     // the Activity Manager to launch the app for backup/restore purposes.
-                    app = mPackageManager.getApplicationInfo(pkg.packageName,
-                            PackageManager.GET_SHARED_LIBRARY_FILES);
+                    app = mPackageManager.getApplicationInfoAsUser(pkg.packageName,
+                            PackageManager.GET_SHARED_LIBRARY_FILES, mUserId);
                     pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
                     pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
                 }
@@ -1366,7 +1390,7 @@
             notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
                     | Intent.FLAG_RECEIVER_FOREGROUND);
             notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
-            mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
+            mContext.sendBroadcastAsUser(notification, UserHandle.of(mUserId));
         }
 
         mProcessedPackagesJournal.addPackage(packageName);
@@ -1409,8 +1433,7 @@
             mConnecting = true;
             mConnectedAgent = null;
             try {
-                if (mActivityManager.bindBackupAgent(app.packageName, mode,
-                        UserHandle.USER_OWNER)) {
+                if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId)) {
                     Slog.d(TAG, "awaiting agent for " + app);
 
                     // success; wait for the agent to arrive
@@ -1441,11 +1464,7 @@
             }
         }
         if (agent == null) {
-            try {
-                mActivityManager.clearPendingBackup();
-            } catch (RemoteException e) {
-                // can't happen - ActivityManager is local
-            }
+            mActivityManagerInternal.clearPendingBackup(mUserId);
         }
         return agent;
     }
@@ -1469,7 +1488,7 @@
     public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
         // Don't wipe packages marked allowClearUserData=false
         try {
-            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+            PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
             if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
                 if (MORE_DEBUG) {
                     Slog.i(TAG, "allowClearUserData=false so not wiping "
@@ -1488,7 +1507,7 @@
             mClearingData = true;
             try {
                 mActivityManager.clearApplicationUserData(
-                        packageName, keepSystemState, observer, 0);
+                        packageName, keepSystemState, observer, mUserId);
             } catch (RemoteException e) {
                 // can't happen because the activity manager is in this process
             }
@@ -1553,11 +1572,16 @@
             throw new IllegalArgumentException("No packages are provided for backup");
         }
 
-        if (!mEnabled || !mProvisioned) {
-            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+        if (!mEnabled || !mSetupComplete) {
+            Slog.i(
+                    TAG,
+                    "Backup requested but enabled="
+                            + mEnabled
+                            + " setupComplete="
+                            + mSetupComplete);
             BackupObserverUtils.sendBackupFinished(observer,
                     BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-            final int logTag = mProvisioned
+            final int logTag = mSetupComplete
                     ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
                     : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
             monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
@@ -1592,8 +1616,8 @@
                 continue;
             }
             try {
-                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
-                        PackageManager.GET_SIGNING_CERTIFICATES);
+                PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
+                        PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
                 if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
                         mPackageManager)) {
                     BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
@@ -1650,8 +1674,8 @@
             }
             // We don't want the backup jobs to kick in any time soon.
             // Reschedules them to run in the distant future.
-            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
-            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            KeyValueBackupJob.schedule(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
         } finally {
             Binder.restoreCallingIdentity(oldToken);
         }
@@ -1886,7 +1910,7 @@
                 Runnable r = new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, latency, mConstants);
+                        FullBackupJob.schedule(mUserId, mContext, latency, mConstants);
                     }
                 };
                 mBackupHandler.postDelayed(r, 2500);
@@ -1992,13 +2016,13 @@
         FullBackupEntry entry = null;
         long latency = fullBackupInterval;
 
-        if (!mEnabled || !mProvisioned) {
+        if (!mEnabled || !mSetupComplete) {
             // Backups are globally disabled, so don't proceed.  We also don't reschedule
             // the job driving automatic backups; that job will be scheduled again when
             // the user enables backup.
             if (MORE_DEBUG) {
-                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
-                        + " p=" + mProvisioned + "; ignoring");
+                Slog.i(TAG, "beginFullBackup but enabled=" + mEnabled
+                        + " setupComplete=" + mSetupComplete + "; ignoring");
             }
             return false;
         }
@@ -2009,7 +2033,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
         if (result.batterySaverEnabled) {
             if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
-            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+            FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, mConstants);
             return false;
         }
 
@@ -2087,7 +2111,8 @@
 
                         final int privFlags = appInfo.applicationInfo.privateFlags;
                         headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
-                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+                                && mActivityManagerInternal.isAppForeground(
+                                        appInfo.applicationInfo.uid);
 
                         if (headBusy) {
                             final long nextEligible = System.currentTimeMillis()
@@ -2110,8 +2135,6 @@
                         // queue entirely and move on, but if there's nothing else in the queue
                         // we should bail entirely.  headBusy cannot have been set to true yet.
                         runBackup = (mFullBackupQueue.size() > 1);
-                    } catch (RemoteException e) {
-                        // Cannot happen; the Activity Manager is in the same process
                     }
                 }
             } while (headBusy);
@@ -2124,7 +2147,7 @@
                 mBackupHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, deferTime, mConstants);
+                        FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants);
                     }
                 });
                 return false;
@@ -2183,11 +2206,10 @@
     /** Used by both incremental and full restore to restore widget data. */
     public void restoreWidgetData(String packageName, byte[] widgetData) {
         // Apply the restored widget state and generate the ID update for the app
-        // TODO: http://b/22388012
         if (MORE_DEBUG) {
             Slog.i(TAG, "Incorporating restored widget data");
         }
-        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
+        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, mUserId);
     }
 
     // *****************************
@@ -2228,7 +2250,7 @@
         }
 
         // ...and schedule a backup pass if necessary
-        KeyValueBackupJob.schedule(mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
     }
 
     // Note: packageName is currently unused, but may be in the future
@@ -2266,20 +2288,6 @@
 
     /** Sent from an app's backup agent to let the service know that there's new data to backup. */
     public void dataChanged(final String packageName) {
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            // TODO: http://b/22388012
-            // App is running under a non-owner user profile.  For now, we do not back
-            // up data from secondary user profiles.
-            // TODO: backups for all user profiles although don't add backup for profiles
-            // without adding admin control in DevicePolicyManager.
-            if (MORE_DEBUG) {
-                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
-                        + callingUserHandle);
-            }
-            return;
-        }
-
         final HashSet<String> targets = dataChangedTargets(packageName);
         if (targets == null) {
             Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
@@ -2316,8 +2324,8 @@
         if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
         PackageInfo info;
         try {
-            info = mPackageManager.getPackageInfo(packageName,
-                    PackageManager.GET_SIGNING_CERTIFICATES);
+            info = mPackageManager.getPackageInfoAsUser(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
         } catch (NameNotFoundException e) {
             Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
             return;
@@ -2378,7 +2386,8 @@
                     mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
             if (result.batterySaverEnabled) {
                 if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
-                KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
+                // Try again in several hours.
+                KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
             } else {
                 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
                 synchronized (mQueueLock) {
@@ -2391,7 +2400,7 @@
                     }
 
                     // ...and cancel any pending scheduled job, because we've just superseded it
-                    KeyValueBackupJob.cancel(mContext);
+                    KeyValueBackupJob.cancel(mUserId, mContext);
                 }
             }
         } finally {
@@ -2399,12 +2408,6 @@
         }
     }
 
-    /** Returns {@code true} if the system user has gone through SUW. */
-    public boolean deviceIsProvisioned() {
-        final ContentResolver resolver = mContext.getContentResolver();
-        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
-    }
-
     /**
      * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
      * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
@@ -2437,8 +2440,7 @@
 
         long oldId = Binder.clearCallingIdentity();
         try {
-            // Doesn't make sense to do a full backup prior to setup
-            if (!deviceIsProvisioned()) {
+            if (!mSetupComplete) {
                 Slog.i(TAG, "Backup not supported before setup");
                 return;
             }
@@ -2564,9 +2566,7 @@
         long oldId = Binder.clearCallingIdentity();
 
         try {
-            // Check whether the device has been provisioned -- we don't handle
-            // full restores prior to completing the setup process.
-            if (!deviceIsProvisioned()) {
+            if (!mSetupComplete) {
                 Slog.i(TAG, "Full restore not permitted before setup");
                 return;
             }
@@ -2721,20 +2721,20 @@
             }
 
             synchronized (mQueueLock) {
-                if (enable && !wasEnabled && mProvisioned) {
+                if (enable && !wasEnabled && mSetupComplete) {
                     // if we've just been enabled, start scheduling backup passes
-                    KeyValueBackupJob.schedule(mContext, mConstants);
+                    KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
                     scheduleNextFullBackupJob(0);
                 } else if (!enable) {
                     // No longer enabled, so stop running backups
                     if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
 
-                    KeyValueBackupJob.cancel(mContext);
+                    KeyValueBackupJob.cancel(mUserId, mContext);
 
                     // This also constitutes an opt-out, so we wipe any data for
                     // this device from the backend.  We start that process with
                     // an alarm in order to guarantee wakelock states.
-                    if (wasEnabled && mProvisioned) {
+                    if (wasEnabled && mSetupComplete) {
                         // NOTE: we currently flush every registered transport, not just
                         // the currently-active one.
                         List<String> transportNames = new ArrayList<>();
@@ -2782,8 +2782,8 @@
         final long oldId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+                Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0, mUserId);
                 mAutoRestore = doAutoRestore;
             }
         } finally {
@@ -2904,7 +2904,7 @@
         try {
             int transportUid =
                     mContext.getPackageManager()
-                            .getPackageUid(transportComponent.getPackageName(), 0);
+                            .getPackageUidAsUser(transportComponent.getPackageName(), 0, mUserId);
             if (callingUid != transportUid) {
                 throw new SecurityException("Only the transport can change its description");
             }
@@ -2996,8 +2996,8 @@
 
     private void updateStateForTransport(String newTransportName) {
         // Publish the name change
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT, newTransportName, mUserId);
 
         // And update our current-dataset bookkeeping
         String callerLogString = "BMS.updateStateForTransport()";
@@ -3112,8 +3112,7 @@
         synchronized (mAgentConnectLock) {
             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
                 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
-                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
-                mConnectedAgent = agent;
+                mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder);
                 mConnecting = false;
             } else {
                 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
@@ -3244,7 +3243,7 @@
             if (packageName != null) {
                 PackageInfo app = null;
                 try {
-                    app = mPackageManager.getPackageInfo(packageName, 0);
+                    app = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
                 } catch (NameNotFoundException nnf) {
                     Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
                     throw new IllegalArgumentException("Package " + packageName + " not found");
@@ -3348,7 +3347,7 @@
                     mTransportManager.getCurrentTransportClient(callerLogString);
             boolean eligible =
                     AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
-                            transportClient, packageName, mPackageManager);
+                            transportClient, packageName, mPackageManager, mUserId);
             if (transportClient != null) {
                 mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
             }
@@ -3372,7 +3371,7 @@
             for (String packageName : packages) {
                 if (AppBackupUtils
                         .appIsRunningAndEligibleForBackupWithTransport(
-                                transportClient, packageName, mPackageManager)) {
+                                transportClient, packageName, mPackageManager, mUserId)) {
                     eligibleApps.add(packageName);
                 }
             }
@@ -3431,14 +3430,14 @@
     private void dumpInternal(PrintWriter pw) {
         synchronized (mQueueLock) {
             pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
-                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+                    + " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
                     + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
             pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
             if (mBackupRunning) pw.println("Backup currently running");
             pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
             pw.println("Last backup pass started: " + mLastBackupPass
                     + " (now = " + System.currentTimeMillis() + ')');
-            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
+            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
 
             pw.println("Transport whitelist:");
             for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index bace1aa..d13f711 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -7,6 +7,7 @@
 import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
 import android.content.pm.PackageInfo;
@@ -15,7 +16,6 @@
 import android.content.pm.SigningInfo;
 import android.os.Build;
 import android.os.Environment;
-import android.os.UserHandle;
 import android.util.Log;
 import android.util.StringBuilderPrinter;
 
@@ -254,12 +254,11 @@
      * for 'adb backup'.
      */
     // TODO(b/113807190): Investigate and potentially remove.
-    public void backupObb(PackageInfo packageInfo) {
+    public void backupObb(@UserIdInt int userId, PackageInfo packageInfo) {
         // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM doesn't have access to
         // external storage.
-        // TODO: http://b/22388012
         Environment.UserEnvironment userEnv =
-                new Environment.UserEnvironment(UserHandle.USER_SYSTEM);
+                new Environment.UserEnvironment(userId);
         File obbDir = userEnv.buildExternalStorageAppObbDirs(packageInfo.packageName)[0];
         if (obbDir != null) {
             if (MORE_DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 45ca2af..7ea1892 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -34,14 +34,12 @@
 import android.content.pm.PackageManager;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
-import com.android.server.backup.UserBackupManagerFiles;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.remote.RemoteCall;
 import com.android.server.backup.utils.FullBackupUtils;
@@ -56,12 +54,12 @@
  */
 public class FullBackupEngine {
     private UserBackupManagerService backupManagerService;
-    OutputStream mOutput;
-    FullBackupPreflight mPreflightHook;
-    BackupRestoreTask mTimeoutMonitor;
-    IBackupAgent mAgent;
-    boolean mIncludeApks;
-    PackageInfo mPkg;
+    private OutputStream mOutput;
+    private FullBackupPreflight mPreflightHook;
+    private BackupRestoreTask mTimeoutMonitor;
+    private IBackupAgent mAgent;
+    private boolean mIncludeApks;
+    private PackageInfo mPkg;
     private final long mQuota;
     private final int mOpToken;
     private final int mTransportFlags;
@@ -78,21 +76,21 @@
         private final File mFilesDir;
 
         FullBackupRunner(
+                UserBackupManagerService userBackupManagerService,
                 PackageInfo packageInfo,
                 IBackupAgent agent,
                 ParcelFileDescriptor pipe,
                 int token,
                 boolean includeApks)
                 throws IOException {
-            // TODO: http://b/22388012
-            mUserId = UserHandle.USER_SYSTEM;
+            mUserId = userBackupManagerService.getUserId();
             mPackageManager = backupManagerService.getPackageManager();
             mPackage = packageInfo;
             mAgent = agent;
             mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
             mToken = token;
             mIncludeApks = includeApks;
-            mFilesDir = UserBackupManagerFiles.getFullBackupEngineFilesDir(mUserId);
+            mFilesDir = userBackupManagerService.getDataDir();
         }
 
         @Override
@@ -132,7 +130,7 @@
                 // TODO(b/113807190): Look into removing, only used for 'adb backup'.
                 if (writeApk) {
                     appMetadataBackupWriter.backupApk(mPackage);
-                    appMetadataBackupWriter.backupObb(mPackage);
+                    appMetadataBackupWriter.backupObb(mUserId, mPackage);
                 }
 
                 if (DEBUG) {
@@ -155,9 +153,12 @@
                         backupManagerService.getBackupManagerBinder(),
                         mTransportFlags);
             } catch (IOException e) {
-                Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
+                Slog.e(TAG, "Error running full backup for " + mPackage.packageName, e);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Remote agent vanished during full backup of " + mPackage.packageName);
+                Slog.e(
+                        TAG,
+                        "Remote agent vanished during full backup of " + mPackage.packageName,
+                        e);
             } finally {
                 try {
                     mPipe.close();
@@ -233,7 +234,13 @@
                 pipes = ParcelFileDescriptor.createPipe();
 
                 FullBackupRunner runner =
-                        new FullBackupRunner(mPkg, mAgent, pipes[1], mOpToken, mIncludeApks);
+                        new FullBackupRunner(
+                                backupManagerService,
+                                mPkg,
+                                mAgent,
+                                pipes[1],
+                                mOpToken,
+                                mIncludeApks);
                 pipes[1].close(); // the runner has dup'd it
                 pipes[1] = null;
                 Thread t = new Thread(runner, "app-data-runner");
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 43a80c4..31786d7 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -66,24 +66,22 @@
  */
 public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
 
-    private UserBackupManagerService backupManagerService;
-    FullBackupEngine mBackupEngine;
-    final AtomicBoolean mLatch;
+    private final UserBackupManagerService mUserBackupManagerService;
+    private final AtomicBoolean mLatch;
 
-    ParcelFileDescriptor mOutputFile;
-    DeflaterOutputStream mDeflater;
-    boolean mIncludeApks;
-    boolean mIncludeObbs;
-    boolean mIncludeShared;
-    boolean mDoWidgets;
-    boolean mAllApps;
-    boolean mIncludeSystem;
-    boolean mCompress;
-    boolean mKeyValue;
-    ArrayList<String> mPackages;
-    PackageInfo mCurrentTarget;
-    String mCurrentPassword;
-    String mEncryptPassword;
+    private final ParcelFileDescriptor mOutputFile;
+    private final boolean mIncludeApks;
+    private final boolean mIncludeObbs;
+    private final boolean mIncludeShared;
+    private final boolean mDoWidgets;
+    private final boolean mAllApps;
+    private final boolean mIncludeSystem;
+    private final boolean mCompress;
+    private final boolean mKeyValue;
+    private final ArrayList<String> mPackages;
+    private PackageInfo mCurrentTarget;
+    private final String mCurrentPassword;
+    private final String mEncryptPassword;
     private final int mCurrentOpToken;
 
     public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
@@ -92,7 +90,7 @@
             String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
             boolean doCompress, boolean doKeyValue, String[] packages, AtomicBoolean latch) {
         super(observer);
-        this.backupManagerService = backupManagerService;
+        mUserBackupManagerService = backupManagerService;
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mLatch = latch;
 
@@ -104,7 +102,7 @@
         mAllApps = doAllApps;
         mIncludeSystem = doSystem;
         mPackages = (packages == null)
-                ? new ArrayList<String>()
+                ? new ArrayList<>()
                 : new ArrayList<>(Arrays.asList(packages));
         mCurrentPassword = curPassword;
         // when backing up, if there is a current backup password, we require that
@@ -123,11 +121,11 @@
         mKeyValue = doKeyValue;
     }
 
-    void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
+    private void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
         for (String pkgName : pkgNames) {
             if (!set.containsKey(pkgName)) {
                 try {
-                    PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(
+                    PackageInfo info = mUserBackupManagerService.getPackageManager().getPackageInfo(
                             pkgName,
                             PackageManager.GET_SIGNING_CERTIFICATES);
                     set.put(pkgName, info);
@@ -141,7 +139,7 @@
     private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
             OutputStream ofstream) throws Exception {
         // User key will be used to encrypt the master key.
-        byte[] newUserSalt = backupManagerService
+        byte[] newUserSalt = mUserBackupManagerService
                 .randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
         SecretKey userKey = PasswordUtils
                 .buildPasswordKey(PBKDF_CURRENT, mEncryptPassword,
@@ -150,8 +148,8 @@
 
         // the master key is random for each backup
         byte[] masterPw = new byte[256 / 8];
-        backupManagerService.getRng().nextBytes(masterPw);
-        byte[] checksumSalt = backupManagerService
+        mUserBackupManagerService.getRng().nextBytes(masterPw);
+        byte[] checksumSalt = mUserBackupManagerService
                 .randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
 
         // primary encryption of the datastream with the random key
@@ -232,11 +230,11 @@
 
         TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<>();
         FullBackupObbConnection obbConnection = new FullBackupObbConnection(
-                backupManagerService);
+                mUserBackupManagerService);
         obbConnection.establish();  // we'll want this later
 
         sendStartBackup();
-        PackageManager pm = backupManagerService.getPackageManager();
+        PackageManager pm = mUserBackupManagerService.getPackageManager();
 
         // doAllApps supersedes the package set if any
         if (mAllApps) {
@@ -245,7 +243,7 @@
             for (int i = 0; i < allPackages.size(); i++) {
                 PackageInfo pkg = allPackages.get(i);
                 // Exclude system apps if we've been asked to do so
-                if (mIncludeSystem == true
+                if (mIncludeSystem
                         || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
                     packagesToBackup.put(pkg.packageName, pkg);
                 }
@@ -316,7 +314,7 @@
             boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
 
             // Only allow encrypted backups of encrypted devices
-            if (backupManagerService.deviceIsEncrypted() && !encrypting) {
+            if (mUserBackupManagerService.deviceIsEncrypted() && !encrypting) {
                 Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
                 return;
             }
@@ -325,7 +323,7 @@
 
             // Verify that the given password matches the currently-active
             // backup password, if any
-            if (!backupManagerService.backupPasswordMatches(mCurrentPassword)) {
+            if (!mUserBackupManagerService.backupPasswordMatches(mCurrentPassword)) {
                 if (DEBUG) {
                     Slog.w(TAG, "Backup password mismatch; aborting");
                 }
@@ -390,7 +388,7 @@
             // Shared storage if requested
             if (mIncludeShared) {
                 try {
-                    pkg = backupManagerService.getPackageManager().getPackageInfo(
+                    pkg = mUserBackupManagerService.getPackageManager().getPackageInfo(
                             SHARED_BACKUP_AGENT_PACKAGE, 0);
                     backupQueue.add(pkg);
                 } catch (NameNotFoundException e) {
@@ -410,9 +408,17 @@
                         pkg.packageName.equals(
                                 SHARED_BACKUP_AGENT_PACKAGE);
 
-                mBackupEngine = new FullBackupEngine(backupManagerService, out,
-                        null, pkg, mIncludeApks, this, Long.MAX_VALUE,
-                        mCurrentOpToken, /*transportFlags=*/ 0);
+                FullBackupEngine mBackupEngine =
+                        new FullBackupEngine(
+                                mUserBackupManagerService,
+                                out,
+                                null,
+                                pkg,
+                                mIncludeApks,
+                                this,
+                                Long.MAX_VALUE,
+                                mCurrentOpToken,
+                                /*transportFlags=*/ 0);
                 sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
 
                 // Don't need to check preflight result as there is no preflight hook.
@@ -437,10 +443,10 @@
                     }
                     KeyValueAdbBackupEngine kvBackupEngine =
                             new KeyValueAdbBackupEngine(out, keyValuePackage,
-                                    backupManagerService,
-                                    backupManagerService.getPackageManager(),
-                                    backupManagerService.getBaseStateDir(),
-                                    backupManagerService.getDataDir());
+                                    mUserBackupManagerService,
+                                    mUserBackupManagerService.getPackageManager(),
+                                    mUserBackupManagerService.getBaseStateDir(),
+                                    mUserBackupManagerService.getDataDir());
                     sendOnBackupPackage(keyValuePackage.packageName);
                     kvBackupEngine.backupOnePackage();
                 }
@@ -471,7 +477,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "Full backup pass complete.");
             }
-            backupManagerService.getWakelock().release();
+            mUserBackupManagerService.getWakelock().release();
         }
     }
 
@@ -493,8 +499,8 @@
             Slog.w(TAG, "adb backup cancel of " + target);
         }
         if (target != null) {
-            backupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
+            mUserBackupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
         }
-        backupManagerService.removeOperation(mCurrentOpToken);
+        mUserBackupManagerService.removeOperation(mCurrentOpToken);
     }
 }
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 5b449c5..0fb4f93 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -317,16 +317,16 @@
         int backupRunStatus = BackupManager.SUCCESS;
 
         try {
-            if (!backupManagerService.isEnabled() || !backupManagerService.isProvisioned()) {
+            if (!backupManagerService.isEnabled() || !backupManagerService.isSetupComplete()) {
                 // Backups are globally disabled, so don't proceed.
                 if (DEBUG) {
                     Slog.i(TAG, "full backup requested but enabled=" + backupManagerService
                             .isEnabled()
-                            + " provisioned=" + backupManagerService.isProvisioned()
+                            + " setupComplete=" + backupManagerService.isSetupComplete()
                             + "; ignoring");
                 }
                 int monitoringEvent;
-                if (backupManagerService.isProvisioned()) {
+                if (backupManagerService.isSetupComplete()) {
                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED;
                 } else {
                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
@@ -633,7 +633,7 @@
             unregisterTask();
 
             if (mJob != null) {
-                mJob.finishBackupPass();
+                mJob.finishBackupPass(backupManagerService.getUserId());
             }
 
             synchronized (backupManagerService.getQueueLock()) {
diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
deleted file mode 100644
index 7e2ac79..0000000
--- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
+++ /dev/null
@@ -1,62 +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.internal;
-
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TAG;
-
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.util.Slog;
-
-import com.android.server.backup.KeyValueBackupJob;
-import com.android.server.backup.UserBackupManagerService;
-
-public class ProvisionedObserver extends ContentObserver {
-
-    private UserBackupManagerService backupManagerService;
-
-    public ProvisionedObserver(
-            UserBackupManagerService backupManagerService, Handler handler) {
-        super(handler);
-        this.backupManagerService = backupManagerService;
-    }
-
-    public void onChange(boolean selfChange) {
-        final boolean wasProvisioned = backupManagerService.isProvisioned();
-        final boolean isProvisioned = backupManagerService.deviceIsProvisioned();
-        // latch: never unprovision
-        backupManagerService.setProvisioned(wasProvisioned || isProvisioned);
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
-                    + " is=" + isProvisioned + " now=" + backupManagerService.isProvisioned());
-        }
-
-        synchronized (backupManagerService.getQueueLock()) {
-            if (backupManagerService.isProvisioned() && !wasProvisioned
-                    && backupManagerService.isEnabled()) {
-                // we're now good to go, so start the backup alarms
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Now provisioned, so starting backups");
-                }
-                KeyValueBackupJob.schedule(backupManagerService.getContext(),
-                        backupManagerService.getConstants());
-                backupManagerService.scheduleNextFullBackupJob(0);
-            }
-        }
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 2a5d913..d37b106 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -26,61 +26,84 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
 import android.os.Message;
 import android.util.Slog;
 
 import com.android.server.backup.UserBackupManagerService;
 
+/**
+ * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_BACKUP_ACTION}
+ * that runs an immediate backup operation if eligible.
+ */
 public class RunBackupReceiver extends BroadcastReceiver {
+    private final UserBackupManagerService mUserBackupManagerService;
 
-    private UserBackupManagerService backupManagerService;
-
-    public RunBackupReceiver(UserBackupManagerService backupManagerService) {
-        this.backupManagerService = backupManagerService;
+    public RunBackupReceiver(UserBackupManagerService userBackupManagerService) {
+        mUserBackupManagerService = userBackupManagerService;
     }
 
+    /**
+     * Run a backup pass if we're eligible. We're eligible if the following conditions are met:
+     *
+     * <ul>
+     *   <li>No transports are pending initialization (otherwise we kick off an initialization
+     *       operation instead).
+     *   <li>Backup is enabled for the user.
+     *   <li>The user has completed setup.
+     *   <li>No backup operation is currently running for the user.
+     * </ul>
+     */
     public void onReceive(Context context, Intent intent) {
-        if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
-            synchronized (backupManagerService.getQueueLock()) {
-                if (backupManagerService.getPendingInits().size() > 0) {
-                    // If there are pending init operations, we process those
-                    // and then settle into the usual periodic backup schedule.
-                    if (MORE_DEBUG) {
-                        Slog.v(TAG, "Init pending at scheduled backup");
-                    }
-                    try {
-                        backupManagerService.getAlarmManager().cancel(
-                                backupManagerService.getRunInitIntent());
-                        backupManagerService.getRunInitIntent().send();
-                    } catch (PendingIntent.CanceledException ce) {
-                        Slog.e(TAG, "Run init intent cancelled");
-                        // can't really do more than bail here
-                    }
-                } else {
-                    // Don't run backups now if we're disabled or not yet
-                    // fully set up.
-                    if (backupManagerService.isEnabled() && backupManagerService.isProvisioned()) {
-                        if (!backupManagerService.isBackupRunning()) {
-                            if (DEBUG) {
-                                Slog.v(TAG, "Running a backup pass");
-                            }
+        if (!RUN_BACKUP_ACTION.equals(intent.getAction())) {
+            return;
+        }
 
-                            // Acquire the wakelock and pass it to the backup thread.  it will
-                            // be released once backup concludes.
-                            backupManagerService.setBackupRunning(true);
-                            backupManagerService.getWakelock().acquire();
-
-                            Message msg = backupManagerService.getBackupHandler().obtainMessage(
-                                    MSG_RUN_BACKUP);
-                            backupManagerService.getBackupHandler().sendMessage(msg);
-                        } else {
-                            Slog.i(TAG, "Backup time but one already running");
-                        }
-                    } else {
-                        Slog.w(TAG, "Backup pass but e=" + backupManagerService.isEnabled() + " p="
-                                + backupManagerService.isProvisioned());
-                    }
+        synchronized (mUserBackupManagerService.getQueueLock()) {
+            if (mUserBackupManagerService.getPendingInits().size() > 0) {
+                // If there are pending init operations, we process those and then settle into the
+                // usual periodic backup schedule.
+                if (MORE_DEBUG) {
+                    Slog.v(TAG, "Init pending at scheduled backup");
                 }
+                try {
+                    PendingIntent runInitIntent = mUserBackupManagerService.getRunInitIntent();
+                    mUserBackupManagerService.getAlarmManager().cancel(runInitIntent);
+                    runInitIntent.send();
+                } catch (PendingIntent.CanceledException ce) {
+                    Slog.w(TAG, "Run init intent cancelled");
+                }
+            } else {
+                // Don't run backups if we're disabled or not yet set up.
+                if (!mUserBackupManagerService.isEnabled()
+                        || !mUserBackupManagerService.isSetupComplete()) {
+                    Slog.w(
+                            TAG,
+                            "Backup pass but enabled="
+                                    + mUserBackupManagerService.isEnabled()
+                                    + " setupComplete="
+                                    + mUserBackupManagerService.isSetupComplete());
+                    return;
+                }
+
+                // Don't run backups if one is already running.
+                if (mUserBackupManagerService.isBackupRunning()) {
+                    Slog.i(TAG, "Backup time but one already running");
+                    return;
+                }
+
+                if (DEBUG) {
+                    Slog.v(TAG, "Running a backup pass");
+                }
+
+                // Acquire the wakelock and pass it to the backup thread. It will be released once
+                // backup concludes.
+                mUserBackupManagerService.setBackupRunning(true);
+                mUserBackupManagerService.getWakelock().acquire();
+
+                Handler backupHandler = mUserBackupManagerService.getBackupHandler();
+                Message message = backupHandler.obtainMessage(MSG_RUN_BACKUP);
+                backupHandler.sendMessage(message);
             }
         }
     }
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 38870cb..97711e3 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -24,41 +24,50 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.PowerManager;
-import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.server.backup.UserBackupManagerService;
 
-public class RunInitializeReceiver extends BroadcastReceiver {
-    private final UserBackupManagerService mBackupManagerService;
+import java.util.Set;
 
-    public RunInitializeReceiver(UserBackupManagerService backupManagerService) {
-        mBackupManagerService = backupManagerService;
+/**
+ * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_INITIALIZE_ACTION}
+ * that runs an initialization operation on all pending transports.
+ */
+public class RunInitializeReceiver extends BroadcastReceiver {
+    private final UserBackupManagerService mUserBackupManagerService;
+
+    public RunInitializeReceiver(UserBackupManagerService userBackupManagerService) {
+        mUserBackupManagerService = userBackupManagerService;
     }
 
     public void onReceive(Context context, Intent intent) {
-        if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
-            synchronized (mBackupManagerService.getQueueLock()) {
-                final ArraySet<String> pendingInits = mBackupManagerService.getPendingInits();
-                if (DEBUG) {
-                    Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
-                }
+        if (!RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
+            return;
+        }
 
-                if (pendingInits.size() > 0) {
-                    final String[] transports =
-                            pendingInits.toArray(new String[pendingInits.size()]);
+        synchronized (mUserBackupManagerService.getQueueLock()) {
+            Set<String> pendingInits = mUserBackupManagerService.getPendingInits();
+            if (DEBUG) {
+                Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
+            }
 
-                    mBackupManagerService.clearPendingInits();
+            if (pendingInits.size() > 0) {
+                String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
 
-                    PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
-                    wakelock.acquire();
-                    OnTaskFinishedListener listener = caller -> wakelock.release();
+                mUserBackupManagerService.clearPendingInits();
 
-                    Runnable task =
-                            new PerformInitializeTask(
-                                    mBackupManagerService, transports, null, listener);
-                    mBackupManagerService.getBackupHandler().post(task);
-                }
+                PowerManager.WakeLock wakelock = mUserBackupManagerService.getWakelock();
+                wakelock.acquire();
+                OnTaskFinishedListener listener = caller -> wakelock.release();
+
+                Runnable task =
+                        new PerformInitializeTask(
+                                mUserBackupManagerService,
+                                transports,
+                                /* observer */ null,
+                                listener);
+                mUserBackupManagerService.getBackupHandler().post(task);
             }
         }
     }
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
new file mode 100644
index 0000000..c5e912e
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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.internal;
+
+import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.getSetupCompleteSettingForUser;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.UserBackupManagerService;
+
+/**
+ * A {@link ContentObserver} for changes to the setting {@link Settings.Secure#USER_SETUP_COMPLETE}
+ * for a particular user.
+ */
+public class SetupObserver extends ContentObserver {
+    private final UserBackupManagerService mUserBackupManagerService;
+    private final Context mContext;
+    private final int mUserId;
+
+    public SetupObserver(UserBackupManagerService userBackupManagerService, Handler handler) {
+        super(handler);
+        mUserBackupManagerService = userBackupManagerService;
+        mContext = userBackupManagerService.getContext();
+        mUserId = userBackupManagerService.getUserId();
+    }
+
+    /**
+     * Callback that executes when the setting {@link Settings.Secure#USER_SETUP_COMPLETE} changes
+     * for the user {@link #mUserId}. If the user is newly setup and backup is enabled, then we
+     * schedule a key value and full backup job for the user. If the user was previously setup and
+     * now the setting has changed to {@code false}, we don't reset the state as having gone through
+     * setup is a non-reversible action.
+     */
+    public void onChange(boolean selfChange) {
+        boolean previousSetupComplete = mUserBackupManagerService.isSetupComplete();
+        boolean newSetupComplete = getSetupCompleteSettingForUser(mContext, mUserId);
+
+        boolean resolvedSetupComplete = previousSetupComplete || newSetupComplete;
+        mUserBackupManagerService.setSetupComplete(resolvedSetupComplete);
+        if (MORE_DEBUG) {
+            Slog.d(
+                    TAG,
+                    "Setup complete change: was="
+                            + previousSetupComplete
+                            + " new="
+                            + newSetupComplete
+                            + " resolved="
+                            + resolvedSetupComplete);
+        }
+
+        synchronized (mUserBackupManagerService.getQueueLock()) {
+            // Start backup if the user is newly setup and backup is enabled.
+            if (resolvedSetupComplete
+                    && !previousSetupComplete
+                    && mUserBackupManagerService.isEnabled()) {
+                if (MORE_DEBUG) {
+                    Slog.d(TAG, "Setup complete so starting backups");
+                }
+                KeyValueBackupJob.schedule(mUserBackupManagerService.getUserId(), mContext,
+                        mUserBackupManagerService.getConstants());
+                mUserBackupManagerService.scheduleNextFullBackupJob(0);
+            }
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index f39d795..cfc129e 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -241,6 +241,7 @@
     private final boolean mUserInitiated;
     private final boolean mNonIncremental;
     private final int mCurrentOpToken;
+    private final int mUserId;
     private final File mStateDirectory;
     private final File mDataDirectory;
     private final File mBlankStateFile;
@@ -320,6 +321,7 @@
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mQueueLock = mBackupManagerService.getQueueLock();
         mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
+        mUserId = backupManagerService.getUserId();
     }
 
     private void registerTask() {
@@ -480,8 +482,8 @@
         final PackageInfo packageInfo;
         try {
             packageInfo =
-                    mPackageManager.getPackageInfo(
-                            packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+                    mPackageManager.getPackageInfoAsUser(
+                            packageName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
         } catch (PackageManager.NameNotFoundException e) {
             mReporter.onAgentUnknown(packageName);
             throw AgentException.permanent(e);
@@ -685,8 +687,12 @@
                     ParcelFileDescriptor.open(
                             mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
 
-            if (!SELinux.restorecon(mBackupDataFile)) {
-                mReporter.onRestoreconFailed(mBackupDataFile);
+            // TODO (b/120424138): Remove once the system user is migrated to use the per-user CE
+            // directory. Per-user CE directories are managed by vold.
+            if (mUserId == UserHandle.USER_SYSTEM) {
+                if (!SELinux.restorecon(mBackupDataFile)) {
+                    mReporter.onRestoreconFailed(mBackupDataFile);
+                }
             }
 
             IBackupTransport transport = mTransportClient.connectOrThrow("KVBT.extractAgentData()");
@@ -770,8 +776,7 @@
 
     private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
             throws IOException {
-        // TODO: http://b/22388012
-        byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, UserHandle.USER_SYSTEM);
+        byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, mUserId);
         File widgetFile = new File(mStateDirectory, pkgName + "_widget");
         boolean priorStateExists = widgetFile.exists();
         if (!priorStateExists && widgetState == null) {
@@ -1003,7 +1008,7 @@
             // Use the scheduler's default.
             delay = 0;
         }
-        KeyValueBackupJob.schedule(
+        KeyValueBackupJob.schedule(mBackupManagerService.getUserId(),
                 mBackupManagerService.getContext(), delay, mBackupManagerService.getConstants());
 
         for (String packageName : mOriginalQueue) {
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index e273b32..0fa0f89 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -54,6 +54,7 @@
     private final TransportManager mTransportManager;
     private final String mTransportName;
     private final UserBackupManagerService mBackupManagerService;
+    private final int mUserId;
     @Nullable private final String mPackageName;
     public RestoreSet[] mRestoreSets = null;
     boolean mEnded = false;
@@ -67,6 +68,7 @@
         mPackageName = packageName;
         mTransportManager = backupManagerService.getTransportManager();
         mTransportName = transportName;
+        mUserId = backupManagerService.getUserId();
     }
 
     public void markTimedOut() {
@@ -304,7 +306,8 @@
 
         final PackageInfo app;
         try {
-            app = mBackupManagerService.getPackageManager().getPackageInfo(packageName, 0);
+            app = mBackupManagerService.getPackageManager().getPackageInfoAsUser(
+                    packageName, 0, mUserId);
         } catch (NameNotFoundException nnf) {
             Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
             return -1;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 0d26ea5..b3d9fbc 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -68,6 +68,8 @@
 public class FullRestoreEngine extends RestoreEngine {
 
     private final UserBackupManagerService mBackupManagerService;
+    private final int mUserId;
+
     // Task in charge of monitoring timeouts
     private final BackupRestoreTask mMonitorTask;
 
@@ -146,6 +148,7 @@
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
         mIsAdbRestore = isAdbRestore;
+        mUserId = backupManagerService.getUserId();
     }
 
     public IBackupAgent getAgent() {
@@ -272,7 +275,7 @@
                                         instream, mBackupManagerService.getContext(),
                                         mDeleteObserver, mManifestSignatures,
                                         mPackagePolicies, info, installerPackageName,
-                                        bytesReadListener);
+                                        bytesReadListener, mUserId);
                                 // good to go; promote to ACCEPT
                                 mPackagePolicies.put(pkg, isSuccessfullyInstalled
                                         ? RestorePolicy.ACCEPT
@@ -329,9 +332,8 @@
                         }
 
                         try {
-                            mTargetApp =
-                                    mBackupManagerService.getPackageManager().getApplicationInfo(
-                                            pkg, 0);
+                            mTargetApp = mBackupManagerService.getPackageManager()
+                                    .getApplicationInfoAsUser(pkg, 0, mUserId);
 
                             // If we haven't sent any data to this app yet, we probably
                             // need to clear it first. Check that.
@@ -678,12 +680,13 @@
      * Returns whether the package is in the list of the packages for which clear app data should
      * be called despite the fact that they have backup agent.
      *
-     * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+     * <p>The list is read from {@link Settings.Secure#PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
      */
     private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
-        String packageListString = Settings.Secure.getString(
+        String packageListString = Settings.Secure.getStringForUser(
                 mBackupManagerService.getContext().getContentResolver(),
-                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+                mUserId);
         if (TextUtils.isEmpty(packageListString)) {
             return false;
         }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index f7efad6..d01f77b 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -48,7 +48,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
 
@@ -81,6 +80,7 @@
 public class PerformUnifiedRestoreTask implements BackupRestoreTask {
 
     private UserBackupManagerService backupManagerService;
+    private final int mUserId;
     private final TransportManager mTransportManager;
     // Transport client we're working with to do the restore
     private final TransportClient mTransportClient;
@@ -175,6 +175,7 @@
             @Nullable String[] filterSet,
             OnTaskFinishedListener listener) {
         this.backupManagerService = backupManagerService;
+        mUserId = backupManagerService.getUserId();
         mTransportManager = backupManagerService.getTransportManager();
         mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
         mState = UnifiedRestoreState.INITIAL;
@@ -204,7 +205,7 @@
                 // We want everything and a pony
                 List<PackageInfo> apps =
                         PackageManagerBackupAgent.getStorableApplications(
-                                backupManagerService.getPackageManager());
+                                backupManagerService.getPackageManager(), mUserId);
                 filterSet = packagesToNames(apps);
                 if (DEBUG) {
                     Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
@@ -221,7 +222,7 @@
             for (int i = 0; i < filterSet.length; i++) {
                 try {
                     PackageManager pm = backupManagerService.getPackageManager();
-                    PackageInfo info = pm.getPackageInfo(filterSet[i], 0);
+                    PackageInfo info = pm.getPackageInfoAsUser(filterSet[i], 0, mUserId);
                     if ("android".equals(info.packageName)) {
                         hasSystem = true;
                         continue;
@@ -240,16 +241,16 @@
             }
             if (hasSystem) {
                 try {
-                    mAcceptSet.add(0,
-                            backupManagerService.getPackageManager().getPackageInfo("android", 0));
+                    mAcceptSet.add(0, backupManagerService.getPackageManager().getPackageInfoAsUser(
+                                    "android", 0, mUserId));
                 } catch (NameNotFoundException e) {
                     // won't happen; we know a priori that it's valid
                 }
             }
             if (hasSettings) {
                 try {
-                    mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfo(
-                            SETTINGS_PACKAGE, 0));
+                    mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfoAsUser(
+                            SETTINGS_PACKAGE, 0, mUserId));
                 } catch (NameNotFoundException e) {
                     // this one is always valid too
                 }
@@ -359,8 +360,7 @@
 
         // If we're starting a full-system restore, set up to begin widget ID remapping
         if (mIsSystemRestore) {
-            // TODO: http://b/22388012
-            AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
+            AppWidgetBackupBridge.restoreStarting(mUserId);
         }
 
         try {
@@ -508,8 +508,8 @@
             }
 
             try {
-                mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo(
-                        pkgName, PackageManager.GET_SIGNING_CERTIFICATES);
+                mCurrentPackage = backupManagerService.getPackageManager().getPackageInfoAsUser(
+                        pkgName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
             } catch (NameNotFoundException e) {
                 // Whoops, we thought we could restore this package but it
                 // turns out not to be present.  Skip it.
@@ -1078,8 +1078,7 @@
         }
 
         // Kick off any work that may be needed regarding app widget restores
-        // TODO: http://b/22388012
-        AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
+        AppWidgetBackupBridge.restoreFinished(mUserId);
 
         // If this was a full-system restore, record the ancestral
         // dataset information
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index e4dcb25..7c5a57c 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.content.ComponentName;
 import android.content.Context;
@@ -34,7 +35,6 @@
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.EventLog;
-import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -79,6 +79,7 @@
     @VisibleForTesting static final String TAG = "TransportClient";
     private static final int LOG_BUFFER_SIZE = 5;
 
+    private final @UserIdInt int mUserId;
     private final Context mContext;
     private final TransportStats mTransportStats;
     private final Intent mBindIntent;
@@ -106,6 +107,7 @@
     private volatile IBackupTransport mTransport;
 
     TransportClient(
+            @UserIdInt int userId,
             Context context,
             TransportStats transportStats,
             Intent bindIntent,
@@ -113,6 +115,7 @@
             String identifier,
             String caller) {
         this(
+                userId,
                 context,
                 transportStats,
                 bindIntent,
@@ -124,6 +127,7 @@
 
     @VisibleForTesting
     TransportClient(
+            @UserIdInt int userId,
             Context context,
             TransportStats transportStats,
             Intent bindIntent,
@@ -131,6 +135,7 @@
             String identifier,
             String caller,
             Handler listenerHandler) {
+        mUserId = userId;
         mContext = context;
         mTransportStats = transportStats;
         mTransportComponent = transportComponent;
@@ -213,7 +218,7 @@
                                     mBindIntent,
                                     mConnection,
                                     Context.BIND_AUTO_CREATE,
-                                    UserHandle.SYSTEM);
+                                    UserHandle.of(mUserId));
                     if (hasBound) {
                         // We don't need to set a time-out because we are guaranteed to get a call
                         // back in ServiceConnection, either an onServiceConnected() or
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
index f4e3928..a4e9b10 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
@@ -19,12 +19,15 @@
 import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
 import static com.android.server.backup.transport.TransportUtils.formatMessage;
 
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.transport.TransportUtils.Priority;
+
 import java.io.PrintWriter;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -36,13 +39,16 @@
 public class TransportClientManager {
     private static final String TAG = "TransportClientManager";
 
+    private final @UserIdInt int mUserId;
     private final Context mContext;
     private final TransportStats mTransportStats;
     private final Object mTransportClientsLock = new Object();
     private int mTransportClientsCreated = 0;
     private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
 
-    public TransportClientManager(Context context, TransportStats transportStats) {
+    public TransportClientManager(@UserIdInt int userId, Context context,
+            TransportStats transportStats) {
+        mUserId = userId;
         mContext = context;
         mTransportStats = transportStats;
     }
@@ -89,6 +95,7 @@
         synchronized (mTransportClientsLock) {
             TransportClient transportClient =
                     new TransportClient(
+                            mUserId,
                             mContext,
                             mTransportStats,
                             bindIntent,
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index e465c7e..054879b 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -91,10 +91,13 @@
      * </ol>
      */
     public static boolean appIsRunningAndEligibleForBackupWithTransport(
-            @Nullable TransportClient transportClient, String packageName, PackageManager pm) {
+            @Nullable TransportClient transportClient,
+            String packageName,
+            PackageManager pm,
+            int userId) {
         try {
-            PackageInfo packageInfo = pm.getPackageInfo(packageName,
-                    PackageManager.GET_SIGNING_CERTIFICATES);
+            PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES, userId);
             ApplicationInfo applicationInfo = packageInfo.applicationInfo;
             if (!appIsEligibleForBackup(applicationInfo, pm)
                     || appIsStopped(applicationInfo)
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index df7e6d4..cce5b3b 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -72,7 +72,9 @@
             HashMap<String, Signature[]> manifestSignatures,
             HashMap<String, RestorePolicy> packagePolicies,
             FileMetadata info,
-            String installerPackageName, BytesReadListener bytesReadListener) {
+            String installerPackageName,
+            BytesReadListener bytesReadListener,
+            int userId) {
         boolean okay = true;
 
         if (DEBUG) {
@@ -144,8 +146,8 @@
                     uninstall = true;
                 } else {
                     try {
-                        PackageInfo pkg = packageManager.getPackageInfo(info.packageName,
-                                PackageManager.GET_SIGNING_CERTIFICATES);
+                        PackageInfo pkg = packageManager.getPackageInfoAsUser(info.packageName,
+                                PackageManager.GET_SIGNING_CERTIFICATES, userId);
                         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP)
                                 == 0) {
                             Slog.w(TAG, "Restore stream contains apk of package "
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 4b24ef9..e4bbcd6 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -32,6 +32,7 @@
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.LocalLog;
 import android.util.Slog;
 import android.view.contentcapture.IContentCaptureManager;
 
@@ -39,6 +40,7 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.SyncResultReceiver;
 import com.android.server.LocalServices;
 import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
@@ -50,8 +52,9 @@
 /**
  * A service used to observe the contents of the screen.
  *
- * <p>The data collected by this service can be analyzed and combined with other sources to provide
- * contextual data in other areas of the system such as Autofill.
+ * <p>The data collected by this service can be analyzed on-device and combined
+ * with other sources to provide contextual data in other areas of the system
+ * such as Autofill.
  */
 public final class ContentCaptureManagerService extends
         AbstractMasterSystemService<ContentCaptureManagerService, ContentCapturePerUserService> {
@@ -67,6 +70,8 @@
 
     private final LocalService mLocalService = new LocalService();
 
+    private final LocalLog mRequestsHistory = new LocalLog(20);
+
     public ContentCaptureManagerService(@NonNull Context context) {
         super(context, new FrameworkResourcesServiceNameResolver(context,
                 com.android.internal.R.string.config_defaultContentCaptureService),
@@ -152,6 +157,13 @@
         }
     }
 
+    /**
+     * Logs a request so it's dumped later...
+     */
+    void logRequestLocked(@NonNull String historyItem) {
+        mRequestsHistory.log(historyItem);
+    }
+
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -196,12 +208,48 @@
         }
 
         @Override
+        public void getReceiverServiceComponentName(@UserIdInt int userId,
+                IResultReceiver receiver) {
+            ComponentName connectedServiceComponentName;
+            synchronized (mLock) {
+                final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+                connectedServiceComponentName = service.getServiceComponentName();
+            }
+            try {
+                receiver.send(0, SyncResultReceiver.bundleFor(connectedServiceComponentName));
+            } catch (RemoteException e) {
+                // Ignore exception as we need to be resilient against app behavior.
+                Slog.w(TAG, "Unable to send service component name: " + e);
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
 
+            boolean showHistory = true;
+            if (args != null) {
+                for (String arg : args) {
+                    switch(arg) {
+                        case "--no-history":
+                            showHistory = false;
+                            break;
+                        case "--help":
+                            pw.println("Usage: dumpsys content_capture [--no-history]");
+                            return;
+                        default:
+                            Slog.w(TAG, "Ignoring invalid dump arg: " + arg);
+                    }
+                }
+            }
+
             synchronized (mLock) {
                 dumpLocked("", pw);
             }
+            if (showHistory) {
+                pw.println(); pw.println("Requests history:"); pw.println();
+                mRequestsHistory.reverseDump(fd, pw, args);
+            }
         }
 
         @Override
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index c467935..8d2c79b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -17,6 +17,9 @@
 package com.android.server.contentcapture;
 
 import static android.service.contentcapture.ContentCaptureService.setClientState;
+import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
+import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
+import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
 
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
@@ -36,10 +39,11 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.service.contentcapture.ContentCaptureService;
+import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
-import android.view.contentcapture.ContentCaptureSession;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
@@ -48,6 +52,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Per-user instance of {@link ContentCaptureManagerService}.
@@ -72,12 +77,14 @@
     @GuardedBy("mLock")
     private RemoteContentCaptureService mRemoteService;
 
+    private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback =
+            new ContentCaptureServiceRemoteCallback();
+
     // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
 
     ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
             @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
         super(master, lock, userId);
-
         updateRemoteServiceLocked(disabled);
     }
 
@@ -95,15 +102,15 @@
         final ComponentName serviceComponentName = updateServiceInfoLocked();
 
         if (serviceComponentName == null) {
-            Slog.w(TAG, "updateRemoteService(): no service componennt name");
+            if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name");
             return;
         }
 
         if (!disabled) {
-            mRemoteService = new RemoteContentCaptureService(
-                  mMaster.getContext(),
-                  ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, mUserId, this,
-                  mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+            mRemoteService = new RemoteContentCaptureService(mMaster.getContext(),
+                    ContentCaptureService.SERVICE_INTERFACE, serviceComponentName,
+                    mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(),
+                    mMaster.verbose);
         }
     }
 
@@ -162,11 +169,24 @@
             @NonNull ComponentName componentName, int taskId, int displayId,
             @NonNull String sessionId, int uid, int flags,
             @NonNull IResultReceiver clientReceiver) {
-        if (!isEnabledLocked()) {
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
+
+        final ComponentName serviceComponentName = getServiceComponentName();
+        final boolean enabled = isEnabledLocked();
+        final String historyItem =
+                "id=" + sessionId + " uid=" + uid
+                + " a=" + ComponentName.flattenToShortString(componentName)
+                + " t=" + taskId + " d=" + displayId
+                + " s=" + ComponentName.flattenToShortString(serviceComponentName)
+                + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)");
+        mMaster.logRequestLocked(historyItem);
+
+        if (!enabled) {
+            // TODO: it would be better to split in differet reasons, like
+            // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
+            setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
+                    /* binder= */ null);
             return;
         }
-        final ComponentName serviceComponentName = getServiceComponentName();
         if (serviceComponentName == null) {
             // TODO(b/111276913): this happens when the system service is starting, we should
             // probably handle it in a more elegant way (like waiting for boot_complete or
@@ -181,7 +201,7 @@
         if (existingSession != null) {
             Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
                     + ": ignoring because it already exists for " + existingSession.mActivityToken);
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID,
+            setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
                     /* binder=*/ null);
             return;
         }
@@ -194,9 +214,8 @@
             // TODO(b/119613670): log metrics
             Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
                     + ": ignoring because service is not set");
-            // TODO(b/111276913): use a new disabled state?
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED,
-                    /* binder=*/ null);
+            setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
+                    /* binder= */ null);
             return;
         }
 
@@ -285,7 +304,7 @@
         final int numSessions = mSessions.size();
         for (int i = 0; i < numSessions; i++) {
             final ContentCaptureServerSession session = mSessions.valueAt(i);
-            session.destroyLocked(true);
+            session.destroyLocked(/* notifyRemoteService= */ true);
         }
         mSessions.clear();
     }
@@ -335,4 +354,50 @@
         }
         return null;
     }
+
+    private final class ContentCaptureServiceRemoteCallback extends
+            IContentCaptureServiceCallback.Stub {
+
+        @Override
+        public void setContentCaptureWhitelist(List<String> packages,
+                List<ComponentName> activities) {
+            if (mMaster.verbose) {
+                Log.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities="
+                        + activities + ")");
+            }
+            // TODO(b/122595322): implement
+            // TODO(b/119613670): log metrics
+        }
+
+        @Override
+        public void setActivityContentCaptureEnabled(ComponentName activity, boolean enabled) {
+            if (mMaster.verbose) {
+                Log.v(TAG, "setActivityContentCaptureEnabled(activity=" + activity + ", enabled="
+                        + enabled + ")");
+            }
+            // TODO(b/122595322): implement
+            // TODO(b/119613670): log metrics
+        }
+
+        @Override
+        public void setPackageContentCaptureEnabled(String packageName, boolean enabled) {
+            if (mMaster.verbose) {
+                Log.v(TAG,
+                        "setPackageContentCaptureEnabled(packageName=" + packageName + ", enabled="
+                                + enabled + ")");
+            }
+            // TODO(b/122595322): implement
+            // TODO(b/119613670): log metrics
+        }
+
+        @Override
+        public void getContentCaptureDisabledActivities(IResultReceiver receiver) {
+            // TODO(b/122595322): implement
+        }
+
+        @Override
+        public void getContentCaptureDisabledPackages(IResultReceiver receiver) {
+            // TODO(b/122595322): implement
+        }
+    }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index ebe0083..3c52e17 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -83,6 +83,8 @@
      */
     @GuardedBy("mLock")
     public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
+        mService.getMaster().logRequestLocked("snapshot: id=" + mId);
+
         mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
     }
 
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 8241628..12742ca 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.service.contentcapture.IContentCaptureService;
+import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
 import android.text.format.DateUtils;
 import android.util.Slog;
@@ -35,12 +36,15 @@
 
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
 
+    private final IBinder mServerCallback;
+
     RemoteContentCaptureService(Context context, String serviceInterface,
-            ComponentName componentName, int userId,
+            ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId,
             ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
             boolean verbose) {
-        super(context, serviceInterface, componentName, userId, callbacks,
+        super(context, serviceInterface, serviceComponentName, userId, callbacks,
                 bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
+        mServerCallback = callback.asBinder();
 
         // Bind right away, which will trigger a onConnected() on service's
         scheduleBind();
@@ -69,7 +73,11 @@
             scheduleUnbind();
         }
         try {
-            mService.onConnectedStateChanged(state);
+            if (state) {
+                mService.onConnected(mServerCallback);
+            } else {
+                mService.onDisconnected();
+            }
         } catch (Exception e) {
             Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e);
         }
diff --git a/services/contentsuggestions/Android.bp b/services/contentsuggestions/Android.bp
new file mode 100644
index 0000000..fc09d2e
--- /dev/null
+++ b/services/contentsuggestions/Android.bp
@@ -0,0 +1,5 @@
+java_library_static {
+    name: "services.contentsuggestions",
+    srcs: ["java/**/*.java"],
+    libs: ["services.core"],
+}
\ No newline at end of file
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
new file mode 100644
index 0000000..58dbea4
--- /dev/null
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.contentsuggestions;
+
+import static android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.IClassificationsCallback;
+import android.app.contentsuggestions.IContentSuggestionsManager;
+import android.app.contentsuggestions.ISelectionsCallback;
+import android.app.contentsuggestions.SelectionsRequest;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.FileDescriptor;
+
+/**
+ * The system service for providing recents / overview with content suggestion selections and
+ * classifications.
+ *
+ * <p>Calls are received here from
+ * {@link android.app.contentsuggestions.ContentSuggestionsManager} then delegated to
+ * a per user version of the service. From there they are routed to the remote actual implementation
+ * that provides the suggestion selections and classifications.
+ */
+public class ContentSuggestionsManagerService extends
+        AbstractMasterSystemService<
+                        ContentSuggestionsManagerService, ContentSuggestionsPerUserService> {
+
+    private static final String TAG = ContentSuggestionsManagerService.class.getSimpleName();
+    private static final boolean VERBOSE = false; // TODO: make dynamic
+
+    private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+    private ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+    public ContentSuggestionsManagerService(Context context) {
+        super(context, new FrameworkResourcesServiceNameResolver(context,
+                com.android.internal.R.string.config_defaultContentSuggestionsService), null);
+        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+    }
+
+    @Override
+    protected ContentSuggestionsPerUserService newServiceLocked(int resolvedUserId,
+            boolean disabled) {
+        return new ContentSuggestionsPerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(
+                Context.CONTENT_SUGGESTIONS_SERVICE, new ContentSuggestionsManagerStub());
+    }
+
+    @Override
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_CONTENT_SUGGESTIONS, TAG);
+    }
+
+    @Override
+    protected int getMaximumTemporaryServiceDurationMs() {
+        return MAX_TEMP_SERVICE_DURATION_MS;
+    }
+
+    private boolean isCallerRecents(int userId) {
+        if (mServiceNameResolver.isTemporary(userId)) {
+            // If a temporary service is set then skip the recents check
+            return true;
+        }
+        return mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid());
+    }
+
+    private void enforceCallerIsRecents(int userId, String func) {
+        if (isCallerRecents(userId)) {
+            return;
+        }
+
+        String msg = "Permission Denial: " + func + " from pid="
+                + Binder.getCallingPid()
+                + ", uid=" + Binder.getCallingUid()
+                + " expected caller is recents";
+        Slog.w(TAG, msg);
+        throw new SecurityException(msg);
+    }
+
+    private class ContentSuggestionsManagerStub extends IContentSuggestionsManager.Stub {
+        @Override
+        public void provideContextImage(int taskId, @NonNull Bundle imageContextRequestExtras) {
+            if (imageContextRequestExtras == null) {
+                throw new IllegalArgumentException("Expected non-null imageContextRequestExtras");
+            }
+
+            final int userId = UserHandle.getCallingUserId();
+            enforceCallerIsRecents(userId, "provideContextImage");
+
+            synchronized (mLock) {
+                final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
+                if (service != null) {
+                    service.provideContextImageLocked(taskId, imageContextRequestExtras);
+                } else {
+                    if (VERBOSE) {
+                        Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void suggestContentSelections(
+                @NonNull SelectionsRequest selectionsRequest,
+                @NonNull ISelectionsCallback selectionsCallback) {
+            final int userId = UserHandle.getCallingUserId();
+            enforceCallerIsRecents(userId, "suggestContentSelections");
+
+            synchronized (mLock) {
+                final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
+                if (service != null) {
+                    service.suggestContentSelectionsLocked(selectionsRequest, selectionsCallback);
+                } else {
+                    if (VERBOSE) {
+                        Slog.v(TAG, "suggestContentSelectionsLocked: no service for " + userId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void classifyContentSelections(
+                @NonNull ClassificationsRequest classificationsRequest,
+                @NonNull IClassificationsCallback callback) {
+            final int userId = UserHandle.getCallingUserId();
+            enforceCallerIsRecents(userId, "classifyContentSelections");
+
+            synchronized (mLock) {
+                final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
+                if (service != null) {
+                    service.classifyContentSelectionsLocked(classificationsRequest, callback);
+                } else {
+                    if (VERBOSE) {
+                        Slog.v(TAG, "classifyContentSelectionsLocked: no service for " + userId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void notifyInteraction(@NonNull String requestId, @NonNull Bundle bundle) {
+            final int userId = UserHandle.getCallingUserId();
+            enforceCallerIsRecents(userId, "notifyInteraction");
+
+            synchronized (mLock) {
+                final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
+                if (service != null) {
+                    service.notifyInteractionLocked(requestId, bundle);
+                } else {
+                    if (VERBOSE) {
+                        Slog.v(TAG, "reportInteractionLocked: no service for " + userId);
+                    }
+                }
+            }
+        }
+
+        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+                @Nullable FileDescriptor err,
+                @NonNull String[] args, @Nullable ShellCallback callback,
+                @NonNull ResultReceiver resultReceiver) throws RemoteException {
+            // Ensure that the caller is the shell process
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid != android.os.Process.SHELL_UID
+                    && callingUid != android.os.Process.ROOT_UID) {
+                Slog.e(TAG, "Expected shell caller");
+                return;
+            }
+            new ContentSuggestionsManagerServiceShellCommand(ContentSuggestionsManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+    }
+}
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerServiceShellCommand.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerServiceShellCommand.java
new file mode 100644
index 0000000..e34f1ea
--- /dev/null
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerServiceShellCommand.java
@@ -0,0 +1,84 @@
+/*
+ * 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.contentsuggestions;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the ContentSuggestionsManagerService.
+ */
+public class ContentSuggestionsManagerServiceShellCommand extends ShellCommand {
+
+    private static final String TAG =
+            ContentSuggestionsManagerServiceShellCommand.class.getSimpleName();
+
+    private final ContentSuggestionsManagerService mService;
+
+    public ContentSuggestionsManagerServiceShellCommand(
+            @NonNull ContentSuggestionsManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "set": {
+                final String what = getNextArgRequired();
+                switch (what) {
+                    case "temporary-service": {
+                        final int userId = Integer.parseInt(getNextArgRequired());
+                        String serviceName = getNextArg();
+                        if (serviceName == null) {
+                            mService.resetTemporaryService(userId);
+                            return 0;
+                        }
+                        final int duration = Integer.parseInt(getNextArgRequired());
+                        mService.setTemporaryService(userId, serviceName, duration);
+                        pw.println("ContentSuggestionsService temporarily set to " + serviceName
+                                + " for " + duration + "ms");
+                        break;
+                    }
+                }
+            }
+            break;
+            default:
+                return handleDefaultCommands(cmd);
+        }
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter()) {
+            pw.println("ContentSuggestionsManagerService commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+            pw.println("    Temporarily (for DURATION ms) changes the service implemtation.");
+            pw.println("    To reset, call with just the USER_ID argument.");
+            pw.println("");
+        }
+    }
+}
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
new file mode 100644
index 0000000..385bc6c
--- /dev/null
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -0,0 +1,162 @@
+/*
+ * 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.contentsuggestions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.IClassificationsCallback;
+import android.app.contentsuggestions.ISelectionsCallback;
+import android.app.contentsuggestions.SelectionsRequest;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.graphics.GraphicBuffer;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+/**
+ * Per user delegate of {@link ContentSuggestionsManagerService}.
+ *
+ * <p>Main job is to forward calls to the remote implementation that can provide suggestion
+ * selections and classifications.
+ */
+public final class ContentSuggestionsPerUserService extends
+        AbstractPerUserSystemService<
+                        ContentSuggestionsPerUserService, ContentSuggestionsManagerService> {
+    private static final String TAG = ContentSuggestionsPerUserService.class.getSimpleName();
+
+    @Nullable
+    @GuardedBy("mLock")
+    private RemoteContentSuggestionsService mRemoteService;
+
+    @NonNull
+    private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+    ContentSuggestionsPerUserService(
+            ContentSuggestionsManagerService master, Object lock, int userId) {
+        super(master, lock, userId);
+        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws PackageManager.NameNotFoundException {
+        ServiceInfo si;
+        try {
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new PackageManager.NameNotFoundException(
+                    "Could not get service for " + serviceComponent);
+        }
+        return si;
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        final boolean enabledChanged = super.updateLocked(disabled);
+        if (enabledChanged) {
+            if (!isEnabledLocked()) {
+                // Clear the remote service for the next call
+                mRemoteService = null;
+            }
+        }
+        return enabledChanged;
+    }
+
+    @GuardedBy("mLock")
+    void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) {
+        RemoteContentSuggestionsService service = getRemoteServiceLocked();
+        if (service != null) {
+            ActivityManager.TaskSnapshot snapshot =
+                    mActivityTaskManagerInternal.getTaskSnapshot(taskId, false);
+            GraphicBuffer snapshotBuffer = null;
+            if (snapshot != null) {
+                snapshotBuffer = snapshot.getSnapshot();
+            }
+
+            service.provideContextImage(taskId, snapshotBuffer, imageContextRequestExtras);
+        }
+    }
+
+    @GuardedBy("mLock")
+    void suggestContentSelectionsLocked(
+            @NonNull SelectionsRequest selectionsRequest,
+            @NonNull ISelectionsCallback selectionsCallback) {
+        RemoteContentSuggestionsService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.suggestContentSelections(selectionsRequest, selectionsCallback);
+        }
+    }
+
+    @GuardedBy("mLock")
+    void classifyContentSelectionsLocked(
+            @NonNull ClassificationsRequest classificationsRequest,
+            @NonNull IClassificationsCallback callback) {
+        RemoteContentSuggestionsService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.classifyContentSelections(classificationsRequest, callback);
+        }
+    }
+
+    @GuardedBy("mLock")
+    void notifyInteractionLocked(@NonNull String requestId, @NonNull Bundle bundle) {
+        RemoteContentSuggestionsService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.notifyInteraction(requestId, bundle);
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteContentSuggestionsService getRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "getRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteContentSuggestionsService(getContext(),
+                    serviceComponent, mUserId,
+                    new RemoteContentSuggestionsService.Callbacks() {
+                        @Override
+                        public void onServiceDied(
+                                @NonNull RemoteContentSuggestionsService service) {
+                            // TODO(b/120865921): properly implement
+                            Slog.w(TAG, "remote content suggestions service died");
+                        }
+                    }, mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
+
+        return mRemoteService;
+    }
+}
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
new file mode 100644
index 0000000..bf48d76
--- /dev/null
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
@@ -0,0 +1,97 @@
+/*
+ * 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.contentsuggestions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.IClassificationsCallback;
+import android.app.contentsuggestions.ISelectionsCallback;
+import android.app.contentsuggestions.SelectionsRequest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.GraphicBuffer;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.service.contentsuggestions.ContentSuggestionsService;
+import android.service.contentsuggestions.IContentSuggestionsService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+/**
+ * Delegates calls from {@link ContentSuggestionsPerUserService} to the remote actual implementation
+ * of the suggestion selection and classification service.
+ */
+public class RemoteContentSuggestionsService extends
+        AbstractMultiplePendingRequestsRemoteService<RemoteContentSuggestionsService,
+                                IContentSuggestionsService> {
+
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+    RemoteContentSuggestionsService(Context context, ComponentName serviceName,
+            int userId, Callbacks callbacks,
+            boolean bindInstantServiceAllowed, boolean verbose) {
+        super(context, ContentSuggestionsService.SERVICE_INTERFACE, serviceName, userId, callbacks,
+                bindInstantServiceAllowed, verbose, /* initialCapacity= */ 1);
+    }
+
+    @Override
+    protected IContentSuggestionsService getServiceInterface(IBinder service) {
+        return IContentSuggestionsService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return PERMANENT_BOUND_TIMEOUT_MS;
+    }
+
+    @Override
+    protected long getRemoteRequestMillis() {
+        return TIMEOUT_REMOTE_REQUEST_MILLIS;
+    }
+
+    void provideContextImage(int taskId, @Nullable GraphicBuffer contextImage,
+            @NonNull Bundle imageContextRequestExtras) {
+        scheduleAsyncRequest((s) -> s.provideContextImage(taskId, contextImage,
+                imageContextRequestExtras));
+    }
+
+    void suggestContentSelections(
+            @NonNull SelectionsRequest selectionsRequest,
+            @NonNull ISelectionsCallback selectionsCallback) {
+        scheduleAsyncRequest(
+                (s) -> s.suggestContentSelections(selectionsRequest, selectionsCallback));
+    }
+
+    void classifyContentSelections(
+            @NonNull ClassificationsRequest classificationsRequest,
+            @NonNull IClassificationsCallback callback) {
+        scheduleAsyncRequest((s) -> s.classifyContentSelections(classificationsRequest, callback));
+    }
+
+    void notifyInteraction(@NonNull String requestId, @NonNull Bundle bundle) {
+        scheduleAsyncRequest((s) -> s.notifyInteraction(requestId, bundle));
+    }
+
+    interface Callbacks
+            extends VultureCallback<RemoteContentSuggestionsService> {
+        // NOTE: so far we don't need to notify the callback implementation
+        // (ContentSuggestionsManager) of the request results (success, timeouts, etc..), so this
+        // callback interface is empty.
+    }
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 7bbc543..a333381 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -279,7 +279,7 @@
 
                 Slog.d(TAG,
                         "Airplane Mode change - current state:  " + BluetoothAdapter.nameForState(
-                                st));
+                                st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
 
                 if (isAirplaneModeOn()) {
                     // Clear registered LE apps to force shut-off
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 66ceae4..00550d9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -25,6 +25,7 @@
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -37,6 +38,8 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkStack.NETWORKSTACK_PACKAGE_NAME;
+import static android.net.shared.NetworkMonitorUtils.isValidationRequired;
 import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
@@ -62,6 +65,8 @@
 import android.net.INetd;
 import android.net.INetdEventCallback;
 import android.net.INetworkManagementEventObserver;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
@@ -79,9 +84,11 @@
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
+import android.net.NetworkStack;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
+import android.net.PrivateDnsConfigParcel;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.UidRange;
@@ -90,12 +97,13 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
+import android.net.shared.NetworkMonitorUtils;
+import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -123,8 +131,8 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.LocalLog;
-import android.util.LocalLog.ReadOnlyLocalLog;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -149,7 +157,6 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.DnsManager;
-import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.KeepaliveTracker;
@@ -158,7 +165,6 @@
 import com.android.server.connectivity.MultipathPolicyTracker;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkDiagnostics;
-import com.android.server.connectivity.NetworkMonitor;
 import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.PermissionMonitor;
@@ -186,7 +192,6 @@
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -442,6 +447,43 @@
      */
     private static final int EVENT_DATA_SAVER_CHANGED = 40;
 
+     /**
+      * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
+      * been tested.
+      * obj = String representing URL that Internet probe was redirect to, if it was redirected.
+      * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
+      * arg2 = NetID.
+      */
+    public static final int EVENT_NETWORK_TESTED = 41;
+
+    /**
+     * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the private DNS
+     * config was resolved.
+     * obj = PrivateDnsConfig
+     * arg2 = netid
+     */
+    public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42;
+
+    /**
+     * Request ConnectivityService display provisioning notification.
+     * arg1    = Whether to make the notification visible.
+     * arg2    = NetID.
+     * obj     = Intent to be launched when notification selected by user, null if !arg1.
+     */
+    public static final int EVENT_PROVISIONING_NOTIFICATION = 43;
+
+    /**
+     * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
+     * should be shown.
+     */
+    public static final int PROVISIONING_NOTIFICATION_SHOW = 1;
+
+    /**
+     * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
+     * should be hidden.
+     */
+    public static final int PROVISIONING_NOTIFICATION_HIDE = 0;
+
     private static String eventName(int what) {
         return sMagicDecoderRing.get(what, Integer.toString(what));
     }
@@ -506,30 +548,6 @@
     private long mMaxWakelockDurationMs = 0;
     private long mLastWakeLockAcquireTimestamp = 0;
 
-    // Array of <Network,ReadOnlyLocalLogs> tracking network validation and results
-    private static final int MAX_VALIDATION_LOGS = 10;
-    private static class ValidationLog {
-        final Network mNetwork;
-        final String mName;
-        final ReadOnlyLocalLog mLog;
-
-        ValidationLog(Network network, String name, ReadOnlyLocalLog log) {
-            mNetwork = network;
-            mName = name;
-            mLog = log;
-        }
-    }
-    private final ArrayDeque<ValidationLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS);
-
-    private void addValidationLogs(ReadOnlyLocalLog log, Network network, String name) {
-        synchronized (mValidationLogs) {
-            while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
-                mValidationLogs.removeLast();
-            }
-            mValidationLogs.addFirst(new ValidationLog(network, name, log));
-        }
-    }
-
     private final IpConnectivityLog mMetricsLog;
 
     @GuardedBy("mBandwidthRequests")
@@ -1476,6 +1494,9 @@
             newNc.setUids(null);
             newNc.setSSID(null);
         }
+        if (newNc.getNetworkSpecifier() != null) {
+            newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
+        }
         return newNc;
     }
 
@@ -1684,7 +1705,11 @@
             // caller type. Need to re-factor NetdEventListenerService to allow multiple
             // NetworkMonitor registrants.
             if (nai != null && nai.satisfies(mDefaultRequest)) {
-                nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode);
+                try {
+                    nai.networkMonitor().notifyDnsResponse(returnCode);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
             }
         }
     };
@@ -2266,17 +2291,6 @@
 
         if (ArrayUtils.contains(args, SHORT_ARG) == false) {
             pw.println();
-            synchronized (mValidationLogs) {
-                pw.println("mValidationLogs (most recent first):");
-                for (ValidationLog p : mValidationLogs) {
-                    pw.println(p.mNetwork + " - " + p.mName);
-                    pw.increaseIndent();
-                    p.mLog.dump(fd, pw, args);
-                    pw.decreaseIndent();
-                }
-            }
-
-            pw.println();
             pw.println("mNetworkRequestInfoLogs (most recent first):");
             pw.increaseIndent();
             mNetworkRequestInfoLogs.reverseDump(fd, pw, args);
@@ -2455,11 +2469,11 @@
             switch (msg.what) {
                 default:
                     return false;
-                case NetworkMonitor.EVENT_NETWORK_TESTED: {
+                case EVENT_NETWORK_TESTED: {
                     final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                     if (nai == null) break;
 
-                    final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+                    final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID);
                     final boolean wasValidated = nai.lastValidated;
                     final boolean wasDefault = isDefaultNetwork(nai);
 
@@ -2497,7 +2511,7 @@
                     }
                     break;
                 }
-                case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
+                case EVENT_PROVISIONING_NOTIFICATION: {
                     final int netId = msg.arg2;
                     final boolean visible = toBool(msg.arg1);
                     final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
@@ -2530,7 +2544,7 @@
                     }
                     break;
                 }
-                case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
+                case EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
                     final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                     if (nai == null) break;
 
@@ -2572,8 +2586,61 @@
         }
     }
 
+    private class NetworkMonitorCallbacks extends INetworkMonitorCallbacks.Stub {
+        private final NetworkAgentInfo mNai;
+
+        private NetworkMonitorCallbacks(NetworkAgentInfo nai) {
+            mNai = nai;
+        }
+
+        @Override
+        public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT,
+                    new Pair<>(mNai, networkMonitor)));
+        }
+
+        @Override
+        public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
+            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
+                    testResult, mNai.network.netId, redirectUrl));
+        }
+
+        @Override
+        public void notifyPrivateDnsConfigResolved(PrivateDnsConfigParcel config) {
+            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+                    EVENT_PRIVATE_DNS_CONFIG_RESOLVED,
+                    0, mNai.network.netId, PrivateDnsConfig.fromParcel(config)));
+        }
+
+        @Override
+        public void showProvisioningNotification(String action) {
+            final Intent intent = new Intent(action);
+            intent.setPackage(NETWORKSTACK_PACKAGE_NAME);
+
+            final PendingIntent pendingIntent;
+            // Only the system server can register notifications with package "android"
+            final long token = Binder.clearCallingIdentity();
+            try {
+                pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+                    EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_SHOW,
+                    mNai.network.netId,
+                    pendingIntent));
+        }
+
+        @Override
+        public void hideProvisioningNotification() {
+            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+                    EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
+                    mNai.network.netId));
+        }
+    }
+
     private boolean networkRequiresValidation(NetworkAgentInfo nai) {
-        return NetworkMonitor.isValidationRequired(
+        return isValidationRequired(
                 mDefaultRequest.networkCapabilities, nai.networkCapabilities);
     }
 
@@ -2603,10 +2670,14 @@
         // Internet access and therefore also require validation.
         if (!networkRequiresValidation(nai)) return;
 
-        // Notify the NetworkMonitor thread in case it needs to cancel or
+        // Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or
         // schedule DNS resolutions. If a DNS resolution is required the
         // result will be sent back to us.
-        nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
+        try {
+            nai.networkMonitor().notifyPrivateDnsChanged(cfg.toParcel());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
 
         // With Private DNS bypass support, we can proceed to update the
         // Private DNS config immediately, even if we're in strict mode
@@ -2736,7 +2807,11 @@
             // Disable wakeup packet monitoring for each interface.
             wakeupModifyInterface(iface, nai.networkCapabilities, false);
         }
-        nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
+        try {
+            nai.networkMonitor().notifyNetworkDisconnected();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
         mNetworkAgentInfos.remove(nai.messenger);
         nai.maybeStopClat();
         synchronized (mNetworkForNetId) {
@@ -3096,7 +3171,11 @@
             NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
             if (nai == null) return;
             if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return;
-            nai.networkMonitor.sendMessage(NetworkMonitor.CMD_LAUNCH_CAPTIVE_PORTAL_APP);
+            try {
+                nai.networkMonitor().launchCaptivePortalApp();
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
         });
     }
 
@@ -3217,6 +3296,11 @@
         return mMultinetworkPolicyTracker.getMeteredMultipathPreference();
     }
 
+    @Override
+    public NetworkRequest getDefaultRequest() {
+        return mDefaultRequest;
+    }
+
     private class InternalHandler extends Handler {
         public InternalHandler(Looper looper) {
             super(looper);
@@ -3247,7 +3331,9 @@
                     break;
                 }
                 case EVENT_REGISTER_NETWORK_AGENT: {
-                    handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
+                    final Pair<NetworkAgentInfo, INetworkMonitor> arg =
+                            (Pair<NetworkAgentInfo, INetworkMonitor>) msg.obj;
+                    handleRegisterNetworkAgent(arg.first, arg.second);
                     break;
                 }
                 case EVENT_REGISTER_NETWORK_REQUEST:
@@ -3305,7 +3391,14 @@
                 }
                 case EVENT_SYSTEM_READY: {
                     for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-                        nai.networkMonitor.systemReady = true;
+                        // Might have been called already in handleRegisterNetworkAgent since
+                        // mSystemReady is set before sending EVENT_SYSTEM_READY, but calling
+                        // this several times is fine.
+                        try {
+                            nai.networkMonitor().notifySystemReady();
+                        } catch (RemoteException e) {
+                            e.rethrowFromSystemServer();
+                        }
                     }
                     mMultipathPolicyTracker.start();
                     break;
@@ -3577,7 +3670,11 @@
         if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
             return;
         }
-        nai.networkMonitor.forceReevaluation(uid);
+        try {
+            nai.networkMonitor().forceReevaluation(uid);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     @Override
@@ -4785,27 +4882,49 @@
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                 new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
-                mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
+                mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mNMS);
         // Make sure the network capabilities reflect what the agent info says.
         nai.networkCapabilities = mixInCapabilities(nai, nc);
-        synchronized (this) {
-            nai.networkMonitor.systemReady = mSystemReady;
-        }
         final String extraInfo = networkInfo.getExtraInfo();
         final String name = TextUtils.isEmpty(extraInfo)
                 ? nai.networkCapabilities.getSSID() : extraInfo;
-        addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, name);
         if (DBG) log("registerNetworkAgent " + nai);
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mContext.getSystemService(NetworkStack.class)
+                    .makeNetworkMonitor(nai.network, name, new NetworkMonitorCallbacks(nai));
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        // NetworkAgentInfo registration will finish when the NetworkMonitor is created.
+        // If the network disconnects or sends any other event before that, messages are deferred by
+        // NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the
+        // registration.
         return nai.network.netId;
     }
 
-    private void handleRegisterNetworkAgent(NetworkAgentInfo nai) {
+    private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
+        nai.onNetworkMonitorCreated(networkMonitor);
         if (VDBG) log("Got NetworkAgent Messenger");
         mNetworkAgentInfos.put(nai.messenger, nai);
         synchronized (mNetworkForNetId) {
             mNetworkForNetId.put(nai.network.netId, nai);
         }
+        synchronized (this) {
+            if (mSystemReady) {
+                try {
+                    networkMonitor.notifySystemReady();
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        try {
+            networkMonitor.start();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
         nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
         NetworkInfo networkInfo = nai.networkInfo;
         nai.networkInfo = null;
@@ -4855,6 +4974,11 @@
             networkAgent.updateClat(mNMS);
             notifyIfacesChangedForNetworkStats();
             if (networkAgent.everConnected) {
+                try {
+                    networkAgent.networkMonitor().notifyLinkPropertiesChanged();
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
                 notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
             }
         }
@@ -5092,6 +5216,11 @@
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
             rematchAllNetworksAndRequests(nai, oldScore);
+            try {
+                nai.networkMonitor().notifyNetworkCapabilitiesChanged();
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
 
@@ -5232,7 +5361,8 @@
         }
         switch (notificationType) {
             case ConnectivityManager.CALLBACK_AVAILABLE: {
-                putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
+                putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
+                        networkAgent.networkCapabilities, nri.mPid, nri.mUid));
                 putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
                 // For this notification, arg1 contains the blocked status.
                 msg.arg1 = arg1;
@@ -5339,6 +5469,11 @@
         }
 
         if (capabilitiesChanged) {
+            try {
+                nai.networkMonitor().notifyNetworkCapabilitiesChanged();
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
 
@@ -5739,7 +5874,15 @@
             updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
                     null);
 
-            networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+            // Until parceled LinkProperties are sent directly to NetworkMonitor, the connect
+            // command must be sent after updating LinkProperties to maximize chances of
+            // NetworkMonitor seeing the correct LinkProperties when starting.
+            // TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
+            try {
+                networkAgent.networkMonitor().notifyNetworkConnected();
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
             scheduleUnvalidatedPrompt(networkAgent);
 
             if (networkAgent.isVPN()) {
@@ -6020,7 +6163,7 @@
     @Override
     public String getCaptivePortalServerUrl() {
         enforceConnectivityInternalPermission();
-        return NetworkMonitor.getCaptivePortalServerHttpUrl(mContext);
+        return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(mContext);
     }
 
     @Override
@@ -6113,12 +6256,6 @@
     }
 
     @VisibleForTesting
-    public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
-            NetworkAgentInfo nai, NetworkRequest defaultRequest) {
-        return new NetworkMonitor(context, handler, nai, defaultRequest);
-    }
-
-    @VisibleForTesting
     MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
         return new MultinetworkPolicyTracker(c, h, r);
     }
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index ea80ac1..80fda19 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -16,6 +16,22 @@
 
 package com.android.server;
 
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.FastImmutableArraySet;
+import android.util.Log;
+import android.util.LogPrinter;
+import android.util.MutableInt;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.FastPrintWriter;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -25,23 +41,6 @@
 import java.util.List;
 import java.util.Set;
 
-import android.net.Uri;
-import android.util.FastImmutableArraySet;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.MutableInt;
-import android.util.PrintWriterPrinter;
-import android.util.Slog;
-import android.util.LogPrinter;
-import android.util.Printer;
-
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.util.FastPrintWriter;
-
 /**
  * {@hide}
  */
@@ -788,6 +787,7 @@
                         + filter.hasCategory(Intent.CATEGORY_DEFAULT));
                 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
                     final R oneResult = newResult(filter, match, userId);
+                    if (debug) Slog.v(TAG, "    Created result: " + oneResult);
                     if (oneResult != null) {
                         dest.add(oneResult);
                         if (debug) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 2346cfc..0b4c01e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,9 +17,14 @@
 package com.android.server;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationManager.FUSED_PROVIDER;
+import static android.location.LocationManager.GPS_PROVIDER;
+import static android.location.LocationManager.NETWORK_PROVIDER;
+import static android.location.LocationManager.PASSIVE_PROVIDER;
 import static android.location.LocationProvider.AVAILABLE;
 import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
 
 import android.Manifest;
@@ -29,7 +34,6 @@
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -64,7 +68,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -81,12 +84,14 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.GeocoderProxy;
@@ -116,7 +121,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
-import java.util.Set;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -137,16 +141,12 @@
 
     private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
             android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
-    private static final String INSTALL_LOCATION_PROVIDER =
-            android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
 
     private static final String NETWORK_LOCATION_SERVICE_ACTION =
             "com.android.location.service.v3.NetworkLocationProvider";
     private static final String FUSED_LOCATION_SERVICE_ACTION =
             "com.android.location.service.FusedLocationProvider";
 
-    private static final int MSG_LOCATION_CHANGED = 1;
-
     private static final long NANOS_PER_MILLI = 1000000L;
 
     // The maximum interval a location request can have and still be considered "high power".
@@ -170,75 +170,62 @@
 
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
-    private final Context mContext;
-    private final AppOpsManager mAppOps;
-
-    // used internally for synchronization
     private final Object mLock = new Object();
+    private final Context mContext;
+    private final Handler mHandler;
 
-    // --- fields below are final after systemRunning() ---
-    private LocationFudger mLocationFudger;
-    private GeofenceManager mGeofenceManager;
+    private AppOpsManager mAppOps;
     private PackageManager mPackageManager;
     private PowerManager mPowerManager;
     private ActivityManager mActivityManager;
     private UserManager mUserManager;
+
+    private GeofenceManager mGeofenceManager;
+    private LocationFudger mLocationFudger;
     private GeocoderProxy mGeocodeProvider;
     private GnssStatusListenerHelper mGnssStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
-    private LocationWorkerHandler mLocationHandler;
     private PassiveProvider mPassiveProvider;  // track passive provider for special cases
     private LocationBlacklist mBlacklist;
     private GnssMeasurementsProvider mGnssMeasurementsProvider;
     private GnssNavigationMessageProvider mGnssNavigationMessageProvider;
+    @GuardedBy("mLock")
     private String mLocationControllerExtraPackage;
     private boolean mLocationControllerExtraPackageEnabled;
     private IGpsGeofenceHardware mGpsGeofenceProxy;
 
-    // --- fields below are protected by mLock ---
+    // list of currently active providers
+    @GuardedBy("mLock")
+    private final ArrayList<LocationProvider> mProviders = new ArrayList<>();
 
-    // Mock (test) providers
-    private final HashMap<String, MockProvider> mMockProviders =
-            new HashMap<>();
+    // list of non-mock providers, so that when mock providers replace real providers, they can be
+    // later re-replaced
+    @GuardedBy("mLock")
+    private final ArrayList<LocationProvider> mRealProviders = new ArrayList<>();
 
-    // all receivers
+    @GuardedBy("mLock")
     private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
-
-    // currently installed providers (with mocks replacing real providers)
-    private final ArrayList<LocationProvider> mProviders =
-            new ArrayList<>();
-
-    // real providers, saved here when mocked out
-    private final HashMap<String, LocationProvider> mRealProviders =
-            new HashMap<>();
-
-    // mapping from provider name to provider
-    private final HashMap<String, LocationProvider> mProvidersByName =
-            new HashMap<>();
-
-    // mapping from provider name to all its UpdateRecords
     private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
             new HashMap<>();
 
     private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics();
 
     // mapping from provider name to last known location
+    @GuardedBy("mLock")
     private final HashMap<String, Location> mLastLocation = new HashMap<>();
 
     // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
     // locations stored here are not fudged for coarse permissions.
+    @GuardedBy("mLock")
     private final HashMap<String, Location> mLastLocationCoarseInterval =
             new HashMap<>();
 
-    // all providers that operate over proxy, for authorizing incoming location and whitelisting
-    // throttling
-    private final ArrayList<LocationProviderProxy> mProxyProviders =
-            new ArrayList<>();
-
     private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
 
+    @GuardedBy("mLock")
     private final ArrayMap<IBinder, Identity> mGnssMeasurementsListeners = new ArrayMap<>();
 
+    @GuardedBy("mLock")
     private final ArrayMap<IBinder, Identity>
             mGnssNavigationMessageListeners = new ArrayMap<>();
 
@@ -246,22 +233,22 @@
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
 
-    // Maximum age of last location returned to clients with foreground-only location permissions.
-    private long mLastLocationMaxAgeMs;
-
     private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
 
     private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
 
     private GnssBatchingProvider mGnssBatchingProvider;
+    @GuardedBy("mLock")
     private IBatchedLocationCallback mGnssBatchingCallback;
+    @GuardedBy("mLock")
     private LinkedCallback mGnssBatchingDeathCallback;
+    @GuardedBy("mLock")
     private boolean mGnssBatchingInProgress = false;
 
     public LocationManagerService(Context context) {
         super();
         mContext = context;
-        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mHandler = BackgroundThread.getHandler();
 
         // Let the package manager query which are the default location
         // providers as they get certain permissions granted by default.
@@ -270,135 +257,114 @@
         packageManagerInternal.setLocationPackagesProvider(
                 userId -> mContext.getResources().getStringArray(
                         com.android.internal.R.array.config_locationProviderPackageNames));
-
-        if (D) Log.d(TAG, "Constructed");
+        packageManagerInternal.setLocationExtraPackagesProvider(
+                userId -> mContext.getResources().getStringArray(
+                      com.android.internal.R.array.config_locationExtraPackageNames));
 
         // most startup is deferred until systemRunning()
     }
 
     public void systemRunning() {
         synchronized (mLock) {
-            if (D) Log.d(TAG, "systemRunning()");
-
-            // fetch package manager
-            mPackageManager = mContext.getPackageManager();
-
-            // fetch power manager
-            mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-
-            // fetch activity manager
-            mActivityManager
-                    = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-
-            // prepare worker thread
-            mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
-
-            // prepare mLocationHandler's dependents
-            mLocationFudger = new LocationFudger(mContext, mLocationHandler);
-            mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
-            mBlacklist.init();
-            mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
-
-            // Monitor for app ops mode changes.
-            AppOpsManager.OnOpChangedListener callback
-                    = new AppOpsManager.OnOpChangedInternalListener() {
-                public void onOpChanged(int op, String packageName) {
-                            mLocationHandler.post(() -> {
-                                synchronized (mLock) {
-                                    for (Receiver receiver : mReceivers.values()) {
-                                        receiver.updateMonitoring(true);
-                                    }
-                                    applyAllProviderRequirementsLocked();
-                                }
-                            });
-                }
-            };
-            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
-                    AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
-
-            PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
-                synchronized (mLock) {
-                    applyAllProviderRequirementsLocked();
-                }
-            };
-            mPackageManager.addOnPermissionsChangeListener(permissionListener);
-
-            // listen for background/foreground changes
-            ActivityManager.OnUidImportanceListener uidImportanceListener =
-                    (uid, importance) -> mLocationHandler.post(
-                            () -> onUidImportanceChanged(uid, importance));
-            mActivityManager.addOnUidImportanceListener(uidImportanceListener,
-                    FOREGROUND_IMPORTANCE_CUTOFF);
-
-            mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            updateUserProfiles(mCurrentUserId);
-
-            updateBackgroundThrottlingWhitelistLocked();
-            updateLastLocationMaxAgeLocked();
-
-            // prepare providers
-            loadProvidersLocked();
-            updateProvidersSettingsLocked();
-            for (LocationProvider provider : mProviders) {
-                applyRequirementsLocked(provider.getName());
-            }
+            initializeLocked();
         }
+    }
 
-        // listen for settings changes
+    @GuardedBy("mLock")
+    private void initializeLocked() {
+        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mPackageManager = mContext.getPackageManager();
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+        mLocationFudger = new LocationFudger(mContext, mHandler);
+        mBlacklist = new LocationBlacklist(mContext, mHandler);
+        mBlacklist.init();
+        mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+
+        // prepare providers
+        initializeProvidersLocked();
+
+        // add listeners
+        mAppOps.startWatchingMode(
+                AppOpsManager.OP_COARSE_LOCATION,
+                null,
+                AppOpsManager.WATCH_FOREGROUND_CHANGES,
+                new AppOpsManager.OnOpChangedInternalListener() {
+                    public void onOpChanged(int op, String packageName) {
+                        synchronized (mLock) {
+                            onAppOpChangedLocked();
+                        }
+                    }
+                });
+        mPackageManager.addOnPermissionsChangeListener(
+                uid -> {
+                    synchronized (mLock) {
+                        onPermissionsChangedLocked();
+                    }
+                });
+
+        mActivityManager.addOnUidImportanceListener(
+                (uid, importance) -> {
+                    synchronized (mLock) {
+                        onUidImportanceChangedLocked(uid, importance);
+                    }
+                },
+                FOREGROUND_IMPORTANCE_CUTOFF);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
-                new ContentObserver(mLocationHandler) {
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE), true,
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
-                            updateProvidersSettingsLocked();
+                            onLocationModeChangedLocked(true);
+                        }
+                    }
+                }, UserHandle.USER_ALL);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        synchronized (mLock) {
+                            onProviderAllowedChangedLocked(true);
                         }
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
                 true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
-                            for (LocationProvider provider : mProviders) {
-                                applyRequirementsLocked(provider.getName());
-                            }
+                            onBackgroundThrottleIntervalChangedLocked();
                         }
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
-                true,
-                new ContentObserver(mLocationHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            updateLastLocationMaxAgeLocked();
-                        }
-                    }
-                }
-        );
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(
                         Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
                 true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
-                            updateBackgroundThrottlingWhitelistLocked();
-                            for (LocationProvider provider : mProviders) {
-                                applyRequirementsLocked(provider.getName());
-                            }
+                            onBackgroundThrottleWhitelistChangedLocked();
                         }
                     }
                 }, UserHandle.USER_ALL);
 
-        mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
+        new PackageMonitor() {
+            @Override
+            public void onPackageDisappeared(String packageName, int reason) {
+                synchronized (mLock) {
+                    LocationManagerService.this.onPackageDisappearedLocked(packageName);
+                }
+            }
+        }.register(mContext, mHandler.getLooper(), true);
 
-        // listen for user change
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
         intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
@@ -407,81 +373,152 @@
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-                } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
-                        || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
-                    updateUserProfiles(mCurrentUserId);
+                synchronized (mLock) {
+                    String action = intent.getAction();
+                    if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                        onUserChangedLocked(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                    } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
+                            || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+                        onUserProfilesChangedLocked();
+                    }
                 }
             }
-        }, UserHandle.ALL, intentFilter, null, mLocationHandler);
+        }, UserHandle.ALL, intentFilter, null, mHandler);
+
+        // switching the user from null to system here performs the bulk of the initialization work.
+        // the user being changed will cause a reload of all user specific settings, which causes
+        // provider initialization, and propagates changes until a steady state is reached
+        mCurrentUserId = UserHandle.USER_NULL;
+        onUserChangedLocked(UserHandle.USER_SYSTEM);
+
+        // initialize in-memory settings values
+        onBackgroundThrottleWhitelistChangedLocked();
     }
 
-    private void onUidImportanceChanged(int uid, int importance) {
+    @GuardedBy("mLock")
+    private void onAppOpChangedLocked() {
+        for (Receiver receiver : mReceivers.values()) {
+            receiver.updateMonitoring(true);
+        }
+        for (LocationProvider p : mProviders) {
+            applyRequirementsLocked(p);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onPermissionsChangedLocked() {
+        for (LocationProvider p : mProviders) {
+            applyRequirementsLocked(p);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onLocationModeChangedLocked(boolean broadcast) {
+        for (LocationProvider p : mProviders) {
+            p.onLocationModeChangedLocked();
+        }
+
+        if (broadcast) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(LocationManager.MODE_CHANGED_ACTION),
+                    UserHandle.ALL);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onProviderAllowedChangedLocked(boolean broadcast) {
+        for (LocationProvider p : mProviders) {
+            p.onAllowedChangedLocked();
+        }
+
+        if (broadcast) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+                    UserHandle.ALL);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onPackageDisappearedLocked(String packageName) {
+        ArrayList<Receiver> deadReceivers = null;
+
+        for (Receiver receiver : mReceivers.values()) {
+            if (receiver.mIdentity.mPackageName.equals(packageName)) {
+                if (deadReceivers == null) {
+                    deadReceivers = new ArrayList<>();
+                }
+                deadReceivers.add(receiver);
+            }
+        }
+
+        // perform removal outside of mReceivers loop
+        if (deadReceivers != null) {
+            for (Receiver receiver : deadReceivers) {
+                removeUpdatesLocked(receiver);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onUidImportanceChangedLocked(int uid, int importance) {
         boolean foreground = isImportanceForeground(importance);
         HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
-        synchronized (mLock) {
-            for (Entry<String, ArrayList<UpdateRecord>> entry
-                    : mRecordsByProvider.entrySet()) {
-                String provider = entry.getKey();
-                for (UpdateRecord record : entry.getValue()) {
-                    if (record.mReceiver.mIdentity.mUid == uid
-                            && record.mIsForegroundUid != foreground) {
-                        if (D) {
-                            Log.d(TAG, "request from uid " + uid + " is now "
-                                    + (foreground ? "foreground" : "background)"));
-                        }
-                        record.updateForeground(foreground);
-
-                        if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
-                            affectedProviders.add(provider);
-                        }
-                    }
-                }
-            }
-            for (String provider : affectedProviders) {
-                applyRequirementsLocked(provider);
-            }
-
-            for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
-                Identity callerIdentity = entry.getValue();
-                if (callerIdentity.mUid == uid) {
+        for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
+            String provider = entry.getKey();
+            for (UpdateRecord record : entry.getValue()) {
+                if (record.mReceiver.mIdentity.mUid == uid
+                        && record.mIsForegroundUid != foreground) {
                     if (D) {
-                        Log.d(TAG, "gnss measurements listener from uid " + uid
-                                + " is now " + (foreground ? "foreground" : "background)"));
-                    }
-                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                        mGnssMeasurementsProvider.addListener(
-                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
-                                callerIdentity.mUid, callerIdentity.mPackageName);
-                    } else {
-                        mGnssMeasurementsProvider.removeListener(
-                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
-                    }
-                }
-            }
-
-            for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
-                Identity callerIdentity = entry.getValue();
-                if (callerIdentity.mUid == uid) {
-                    if (D) {
-                        Log.d(TAG, "gnss navigation message listener from uid "
-                                + uid + " is now "
+                        Log.d(TAG, "request from uid " + uid + " is now "
                                 + (foreground ? "foreground" : "background)"));
                     }
-                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                        mGnssNavigationMessageProvider.addListener(
-                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
-                                callerIdentity.mUid, callerIdentity.mPackageName);
-                    } else {
-                        mGnssNavigationMessageProvider.removeListener(
-                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+                    record.updateForeground(foreground);
+
+                    if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+                        affectedProviders.add(provider);
                     }
                 }
             }
+        }
+        for (String provider : affectedProviders) {
+            applyRequirementsLocked(provider);
+        }
 
-            // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
+        for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
+            Identity callerIdentity = entry.getValue();
+            if (callerIdentity.mUid == uid) {
+                if (D) {
+                    Log.d(TAG, "gnss measurements listener from uid " + uid
+                            + " is now " + (foreground ? "foreground" : "background)"));
+                }
+                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                    mGnssMeasurementsProvider.addListener(
+                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+                            callerIdentity.mUid, callerIdentity.mPackageName);
+                } else {
+                    mGnssMeasurementsProvider.removeListener(
+                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+                }
+            }
+        }
+
+        for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
+            Identity callerIdentity = entry.getValue();
+            if (callerIdentity.mUid == uid) {
+                if (D) {
+                    Log.d(TAG, "gnss navigation message listener from uid "
+                            + uid + " is now "
+                            + (foreground ? "foreground" : "background)"));
+                }
+                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                    mGnssNavigationMessageProvider.addListener(
+                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+                            callerIdentity.mUid, callerIdentity.mPackageName);
+                } else {
+                    mGnssNavigationMessageProvider.removeListener(
+                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+                }
+            }
         }
     }
 
@@ -489,30 +526,43 @@
         return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
     }
 
-    /**
-     * Makes a list of userids that are related to the current user. This is
-     * relevant when using managed profiles. Otherwise the list only contains
-     * the current user.
-     *
-     * @param currentUserId the current user, who might have an alter-ego.
-     */
-    private void updateUserProfiles(int currentUserId) {
-        int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
-        synchronized (mLock) {
-            mCurrentUserProfiles = profileIds;
+    @GuardedBy("mLock")
+    private void onBackgroundThrottleIntervalChangedLocked() {
+        for (LocationProvider provider : mProviders) {
+            applyRequirementsLocked(provider);
         }
     }
 
-    /**
-     * Checks if the specified userId matches any of the current foreground
-     * users stored in mCurrentUserProfiles.
-     */
-    private boolean isCurrentProfile(int userId) {
-        synchronized (mLock) {
-            return ArrayUtils.contains(mCurrentUserProfiles, userId);
+    @GuardedBy("mLock")
+    private void onBackgroundThrottleWhitelistChangedLocked() {
+        String setting = Settings.Global.getString(
+                mContext.getContentResolver(),
+                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+        if (setting == null) {
+            setting = "";
+        }
+
+        mBackgroundThrottlePackageWhitelist.clear();
+        mBackgroundThrottlePackageWhitelist.addAll(
+                SystemConfig.getInstance().getAllowUnthrottledLocation());
+        mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+
+        for (LocationProvider p : mProviders) {
+            applyRequirementsLocked(p);
         }
     }
 
+    @GuardedBy("mLock")
+    private void onUserProfilesChangedLocked() {
+        mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(mCurrentUserId);
+    }
+
+    @GuardedBy("mLock")
+    private boolean isCurrentProfileLocked(int userId) {
+        return ArrayUtils.contains(mCurrentUserProfiles, userId);
+    }
+
+    @GuardedBy("mLock")
     private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
         PackageManager pm = mContext.getPackageManager();
         String systemPackageName = mContext.getPackageName();
@@ -583,30 +633,30 @@
                 + "partition. The fallback must also be marked coreApp=\"true\" in the manifest");
     }
 
-    private void loadProvidersLocked() {
+    @GuardedBy("mLock")
+    private void initializeProvidersLocked() {
         // create a passive location provider, which is always enabled
-        LocationProvider passiveProviderManager = new LocationProvider(
-                LocationManager.PASSIVE_PROVIDER);
-        PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
-
+        LocationProvider passiveProviderManager = new LocationProvider(PASSIVE_PROVIDER);
         addProviderLocked(passiveProviderManager);
-        mPassiveProvider = passiveProvider;
+        mPassiveProvider = new PassiveProvider(passiveProviderManager);
+        passiveProviderManager.attachLocked(mPassiveProvider);
 
         if (GnssLocationProvider.isSupported()) {
             // Create a gps location provider
-            LocationProvider gnssProviderManager = new LocationProvider(
-                    LocationManager.GPS_PROVIDER);
+            LocationProvider gnssProviderManager = new LocationProvider(GPS_PROVIDER, true);
+            mRealProviders.add(gnssProviderManager);
+            addProviderLocked(gnssProviderManager);
+
             GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
                     gnssProviderManager,
-                    mLocationHandler.getLooper());
+                    mHandler.getLooper());
+            gnssProviderManager.attachLocked(gnssProvider);
 
             mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
             mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
             mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
             mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
             mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
-            addProviderLocked(gnssProviderManager);
-            mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
             mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
             mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
             mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
@@ -634,9 +684,7 @@
         ensureFallbackFusedProviderPresentLocked(pkgs);
 
         // bind to network provider
-
-        LocationProvider networkProviderManager = new LocationProvider(
-                LocationManager.NETWORK_PROVIDER);
+        LocationProvider networkProviderManager = new LocationProvider(NETWORK_PROVIDER, true);
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
                 mContext,
                 networkProviderManager,
@@ -645,16 +693,15 @@
                 com.android.internal.R.string.config_networkLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (networkProvider != null) {
-            mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
-            mProxyProviders.add(networkProvider);
+            mRealProviders.add(networkProviderManager);
             addProviderLocked(networkProviderManager);
+            networkProviderManager.attachLocked(networkProvider);
         } else {
             Slog.w(TAG, "no network location provider found");
         }
 
         // bind to fused provider
-        LocationProvider fusedProviderManager = new LocationProvider(
-                LocationManager.FUSED_PROVIDER);
+        LocationProvider fusedProviderManager = new LocationProvider(FUSED_PROVIDER);
         LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
                 mContext,
                 fusedProviderManager,
@@ -663,9 +710,9 @@
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (fusedProvider != null) {
+            mRealProviders.add(fusedProviderManager);
             addProviderLocked(fusedProviderManager);
-            mProxyProviders.add(fusedProvider);
-            mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
+            fusedProviderManager.attachLocked(fusedProvider);
         } else {
             Slog.e(TAG, "no fused location provider found",
                     new IllegalStateException("Location service needs a fused location provider"));
@@ -715,9 +762,6 @@
         for (String testProviderString : testProviderStrings) {
             String fragments[] = testProviderString.split(",");
             String name = fragments[0].trim();
-            if (mProvidersByName.get(name) != null) {
-                throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
-            }
             ProviderProperties properties = new ProviderProperties(
                     Boolean.parseBoolean(fragments[1]) /* requiresNetwork */,
                     Boolean.parseBoolean(fragments[2]) /* requiresSatellite */,
@@ -728,28 +772,37 @@
                     Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
                     Integer.parseInt(fragments[8]) /* powerRequirement */,
                     Integer.parseInt(fragments[9]) /* accuracy */);
-            addTestProviderLocked(name, properties);
+            LocationProvider testProviderManager = new LocationProvider(name);
+            addProviderLocked(testProviderManager);
+            new MockProvider(testProviderManager, properties);
         }
     }
 
-    /**
-     * Called when the device's active user changes.
-     *
-     * @param userId the new active user's UserId
-     */
-    private void switchUser(int userId) {
+    @GuardedBy("mLock")
+    private void onUserChangedLocked(int userId) {
         if (mCurrentUserId == userId) {
             return;
         }
-        mBlacklist.switchUser(userId);
-        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
-        synchronized (mLock) {
-            mLastLocation.clear();
-            mLastLocationCoarseInterval.clear();
-            updateUserProfiles(userId);
-            updateProvidersSettingsLocked();
-            mCurrentUserId = userId;
+
+        // this call has the side effect of forcing a write to the LOCATION_MODE setting in an OS
+        // upgrade case, and ensures that if anyone checks the LOCATION_MODE setting directly, they
+        // will see it in an appropriate state (at least after that user becomes foreground for the
+        // first time...)
+        isLocationEnabledForUser(userId);
+
+        // let providers know the current user is on the way out before changing the user
+        for (LocationProvider p : mProviders) {
+            p.onUserChangingLocked();
         }
+
+        mCurrentUserId = userId;
+        onUserProfilesChangedLocked();
+
+        mBlacklist.switchUser(userId);
+
+        // if the user changes, per-user settings may also have changed
+        onLocationModeChangedLocked(false);
+        onProviderAllowedChangedLocked(false);
     }
 
     private static final class Identity {
@@ -767,158 +820,381 @@
     private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
 
         private final String mName;
-        private AbstractLocationProvider mProvider;
 
-        // whether the provider is enabled in location settings
-        private boolean mSettingsEnabled;
+        // whether this provider should respect LOCATION_PROVIDERS_ALLOWED (ie gps and network)
+        private final boolean mIsManagedBySettings;
 
-        // whether the provider considers itself enabled
-        private volatile boolean mEnabled;
+        // remember to clear binder identity before invoking any provider operation
+        @GuardedBy("mLock")
+        @Nullable protected AbstractLocationProvider mProvider;
 
-        @Nullable
-        private volatile ProviderProperties mProperties;
+        @GuardedBy("mLock")
+        private boolean mUseable;  // combined state
+        @GuardedBy("mLock")
+        private boolean mAllowed;  // state of LOCATION_PROVIDERS_ALLOWED
+        @GuardedBy("mLock")
+        private boolean mEnabled;  // state of provider
+
+        @GuardedBy("mLock")
+        @Nullable private ProviderProperties mProperties;
 
         private LocationProvider(String name) {
-            mName = name;
-            // TODO: initialize settings enabled?
+            this(name, false);
         }
 
-        @Override
-        public void onAttachProvider(AbstractLocationProvider provider, boolean initiallyEnabled) {
-            checkState(mProvider == null);
+        private LocationProvider(String name, boolean isManagedBySettings) {
+            mName = name;
+            mIsManagedBySettings = isManagedBySettings;
 
-            // the provider is not yet fully constructed at this point, so we may not do anything
-            // except save a reference for later use here. do not call any provider methods.
-            mProvider = provider;
-            mEnabled = initiallyEnabled;
+            mProvider = null;
+            mUseable = false;
+            mAllowed = !mIsManagedBySettings;
+            mEnabled = false;
             mProperties = null;
         }
 
+        @GuardedBy("mLock")
+        public void attachLocked(AbstractLocationProvider provider) {
+            checkNotNull(provider);
+            checkState(mProvider == null);
+            mProvider = provider;
+
+            onUseableChangedLocked();
+        }
+
         public String getName() {
             return mName;
         }
 
-        public boolean isEnabled() {
-            return mSettingsEnabled && mEnabled;
+        @GuardedBy("mLock")
+        @Nullable
+        public String getPackageLocked() {
+            if (mProvider == null) {
+                return null;
+            } else if (mProvider instanceof LocationProviderProxy) {
+                // safe to not clear binder context since this doesn't call into the actual provider
+                return ((LocationProviderProxy) mProvider).getConnectedPackageName();
+            } else {
+                return mContext.getPackageName();
+            }
         }
 
+        public boolean isMock() {
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        public boolean isPassiveLocked() {
+            return mProvider == mPassiveProvider;
+        }
+
+        @GuardedBy("mLock")
         @Nullable
-        public ProviderProperties getProperties() {
+        public ProviderProperties getPropertiesLocked() {
             return mProperties;
         }
 
-        public void setRequest(ProviderRequest request, WorkSource workSource) {
-            mProvider.setRequest(request, workSource);
+        @GuardedBy("mLock")
+        public void setRequestLocked(ProviderRequest request, WorkSource workSource) {
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    mProvider.setRequest(request, workSource);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
         }
 
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        @GuardedBy("mLock")
+        public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
             pw.println(mName + " provider:");
-            pw.println(" setting=" + mSettingsEnabled);
+            if (isMock()) {
+                pw.println(" mock=true");
+            }
+            pw.println(" attached=" + (mProvider != null));
+            if (mIsManagedBySettings) {
+                pw.println(" allowed=" + mAllowed);
+            }
             pw.println(" enabled=" + mEnabled);
+            pw.println(" useable=" + mUseable);
             pw.println(" properties=" + mProperties);
-            mProvider.dump(fd, pw, args);
+
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    mProvider.dump(fd, pw, args);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
         }
 
-        public long getStatusUpdateTime() {
-            return mProvider.getStatusUpdateTime();
+        @GuardedBy("mLock")
+        public long getStatusUpdateTimeLocked() {
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    return mProvider.getStatusUpdateTime();
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            } else {
+                return 0;
+            }
         }
 
-        public int getStatus(Bundle extras) {
-            return mProvider.getStatus(extras);
+        @GuardedBy("mLock")
+        public int getStatusLocked(Bundle extras) {
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    return mProvider.getStatus(extras);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            } else {
+                return AVAILABLE;
+            }
         }
 
-        public void sendExtraCommand(String command, Bundle extras) {
-            mProvider.sendExtraCommand(command, extras);
+        @GuardedBy("mLock")
+        public void sendExtraCommandLocked(String command, Bundle extras) {
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    mProvider.sendExtraCommand(command, extras);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
         }
 
         // called from any thread
         @Override
         public void onReportLocation(Location location) {
-            runOnHandler(() -> LocationManagerService.this.reportLocation(location,
-                    mProvider == mPassiveProvider));
+            // no security check necessary because this is coming from an internal-only interface
+            // move calls coming from below LMS onto a different thread to avoid deadlock
+            runInternal(() -> {
+                synchronized (mLock) {
+                    handleLocationChangedLocked(location, this);
+                }
+            });
         }
 
         // called from any thread
         @Override
         public void onReportLocation(List<Location> locations) {
-            runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+            // move calls coming from below LMS onto a different thread to avoid deadlock
+            runInternal(() -> {
+                synchronized (mLock) {
+                    LocationProvider gpsProvider = getLocationProviderLocked(GPS_PROVIDER);
+                    if (gpsProvider == null || !gpsProvider.isUseableLocked()) {
+                        Slog.w(TAG, "reportLocationBatch() called without user permission");
+                        return;
+                    }
+
+                    if (mGnssBatchingCallback == null) {
+                        Slog.e(TAG, "reportLocationBatch() called without active Callback");
+                        return;
+                    }
+
+                    try {
+                        mGnssBatchingCallback.onLocationBatch(locations);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
+                    }
+                }
+            });
         }
 
         // called from any thread
         @Override
         public void onSetEnabled(boolean enabled) {
-            runOnHandler(() -> {
-                if (enabled == mEnabled) {
-                    return;
-                }
-
-                mEnabled = enabled;
-
-                if (!mSettingsEnabled) {
-                    // this provider was disabled in settings anyways, so a change to it's own
-                    // enabled status won't have any affect.
-                    return;
-                }
-
-                // traditionally clients can listen for changes to the LOCATION_PROVIDERS_ALLOWED
-                // setting to detect when providers are enabled or disabled (even though they aren't
-                // supposed to). to continue to support this we must force a change to this setting.
-                // we use the fused provider because this is forced to be always enabled in settings
-                // anyways, and so won't have any visible effect beyond triggering content observers
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        "+" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
-
+            // move calls coming from below LMS onto a different thread to avoid deadlock
+            runInternal(() -> {
                 synchronized (mLock) {
-                    if (!enabled) {
-                        // If any provider has been disabled, clear all last locations for all
-                        // providers. This is to be on the safe side in case a provider has location
-                        // derived from this disabled provider.
-                        mLastLocation.clear();
-                        mLastLocationCoarseInterval.clear();
+                    if (enabled == mEnabled) {
+                        return;
                     }
 
-                    updateProviderListenersLocked(mName);
-                }
+                    mEnabled = enabled;
 
-                mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
-                        UserHandle.ALL);
+                    // update provider allowed settings to reflect enabled status
+                    if (mIsManagedBySettings) {
+                        if (mEnabled && !mAllowed) {
+                            Settings.Secure.putStringForUser(
+                                    mContext.getContentResolver(),
+                                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                                    "+" + mName,
+                                    mCurrentUserId);
+                        } else if (!mEnabled && mAllowed) {
+                            Settings.Secure.putStringForUser(
+                                    mContext.getContentResolver(),
+                                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                                    "-" + mName,
+                                    mCurrentUserId);
+                        }
+                    }
+
+                    onUseableChangedLocked();
+                }
             });
         }
 
         @Override
         public void onSetProperties(ProviderProperties properties) {
-            runOnHandler(() -> mProperties = properties);
+            // because this does not invoke any other methods which might result in calling back
+            // into the location provider, it is safe to run this on the calling thread. it is also
+            // currently necessary to run this on the calling thread to ensure that property changes
+            // are publicly visibly immediately, ie for mock providers which are created.
+            synchronized (mLock) {
+                mProperties = properties;
+            }
         }
 
-        private void setSettingsEnabled(boolean enabled) {
-            synchronized (mLock) {
-                if (mSettingsEnabled == enabled) {
+        @GuardedBy("mLock")
+        public void onLocationModeChangedLocked() {
+            onUseableChangedLocked();
+        }
+
+        private boolean isAllowed() {
+            return isAllowedForUser(mCurrentUserId);
+        }
+
+        private boolean isAllowedForUser(int userId) {
+            String allowedProviders = Settings.Secure.getStringForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                    userId);
+            return TextUtils.delimitedStringContains(allowedProviders, ',', mName);
+        }
+
+        @GuardedBy("mLock")
+        public void onAllowedChangedLocked() {
+            if (mIsManagedBySettings) {
+                boolean allowed = isAllowed();
+                if (allowed == mAllowed) {
                     return;
                 }
+                mAllowed = allowed;
 
-                mSettingsEnabled = enabled;
-                if (!mSettingsEnabled) {
-                    // if any provider has been disabled, clear all last locations for all
-                    // providers. this is to be on the safe side in case a provider has location
-                    // derived from this disabled provider.
-                    mLastLocation.clear();
-                    mLastLocationCoarseInterval.clear();
-                    updateProviderListenersLocked(mName);
-                } else if (mEnabled) {
-                    updateProviderListenersLocked(mName);
+                // make a best effort to keep the setting matching the real enabled state of the
+                // provider so that legacy applications aren't broken.
+                if (mAllowed && !mEnabled) {
+                    Settings.Secure.putStringForUser(
+                            mContext.getContentResolver(),
+                            Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                            "-" + mName,
+                            mCurrentUserId);
+                }
+
+                onUseableChangedLocked();
+            }
+        }
+
+        @GuardedBy("mLock")
+        public boolean isUseableLocked() {
+            return isUseableForUserLocked(mCurrentUserId);
+        }
+
+        @GuardedBy("mLock")
+        public boolean isUseableForUserLocked(int userId) {
+            return userId == mCurrentUserId && mUseable;
+        }
+
+        @GuardedBy("mLock")
+        public void onUseableChangedLocked() {
+            // if any property that contributes to "useability" here changes state, it MUST result
+            // in a direct or indrect call to onUseableChangedLocked. this allows the provider to
+            // guarantee that it will always eventually reach the correct state.
+            boolean useable = mProvider != null
+                    && mProviders.contains(this) && isLocationEnabled() && mAllowed && mEnabled;
+            if (useable == mUseable) {
+                return;
+            }
+            mUseable = useable;
+
+            if (!mUseable) {
+                // If any provider has been disabled, clear all last locations for all
+                // providers. This is to be on the safe side in case a provider has location
+                // derived from this disabled provider.
+                mLastLocation.clear();
+                mLastLocationCoarseInterval.clear();
+            }
+
+            updateProviderUseableLocked(this);
+        }
+
+        @GuardedBy("mLock")
+        public void onUserChangingLocked() {
+            // when the user is about to change, we set this provider to un-useable, and notify all
+            // of the current user clients. when the user is finished changing, useability will be
+            // updated back via onLocationModeChanged() and onAllowedChanged().
+            mUseable = false;
+            updateProviderUseableLocked(this);
+        }
+
+        // binder transactions coming from below LMS (ie location providers) need to be moved onto
+        // a different thread to avoid potential deadlock as code reenters the location providers
+        private void runInternal(Runnable runnable) {
+            if (Looper.myLooper() == mHandler.getLooper()) {
+                runnable.run();
+            } else {
+                mHandler.post(runnable);
+            }
+        }
+    }
+
+    private class MockLocationProvider extends LocationProvider {
+
+        private MockLocationProvider(String name) {
+            super(name);
+        }
+
+        @Override
+        public void attachLocked(AbstractLocationProvider provider) {
+            checkState(provider instanceof MockProvider);
+            super.attachLocked(provider);
+        }
+
+        public boolean isMock() {
+            return true;
+        }
+
+        @GuardedBy("mLock")
+        public void setEnabledLocked(boolean enabled) {
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    ((MockProvider) mProvider).setEnabled(enabled);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
                 }
             }
         }
 
-        private void runOnHandler(Runnable runnable) {
-            if (Looper.myLooper() == mLocationHandler.getLooper()) {
-                runnable.run();
-            } else {
-                mLocationHandler.post(runnable);
+        @GuardedBy("mLock")
+        public void setLocationLocked(Location location) {
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    ((MockProvider) mProvider).setLocation(location);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        public void setStatusLocked(int status, Bundle extras, long updateTime) {
+            if (mProvider != null) {
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    ((MockProvider) mProvider).setStatus(status, extras, updateTime);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
             }
         }
     }
@@ -1022,19 +1298,18 @@
                 // See if receiver has any enabled update records.  Also note if any update records
                 // are high power (has a high power provider with an interval under a threshold).
                 for (UpdateRecord updateRecord : mUpdateRecords.values()) {
-                    if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
-                            mCurrentUserId)) {
-                        requestingLocation = true;
-                        LocationManagerService.LocationProvider locationProvider
-                                = mProvidersByName.get(updateRecord.mProvider);
-                        ProviderProperties properties = locationProvider != null
-                                ? locationProvider.getProperties() : null;
-                        if (properties != null
-                                && properties.mPowerRequirement == Criteria.POWER_HIGH
-                                && updateRecord.mRequest.getInterval() < HIGH_POWER_INTERVAL_MS) {
-                            requestingHighPowerLocation = true;
-                            break;
-                        }
+                    LocationProvider provider = getLocationProviderLocked(updateRecord.mProvider);
+                    if (provider == null || !provider.isUseableLocked()) {
+                        continue;
+                    }
+
+                    requestingLocation = true;
+                    ProviderProperties properties = provider.getPropertiesLocked();
+                    if (properties != null
+                            && properties.mPowerRequirement == Criteria.POWER_HIGH
+                            && updateRecord.mRequest.getInterval() < HIGH_POWER_INTERVAL_MS) {
+                        requestingHighPowerLocation = true;
+                        break;
                     }
                 }
             }
@@ -1122,7 +1397,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1158,7 +1433,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1201,7 +1476,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1273,16 +1548,16 @@
                 synchronized (receiver) {
                     // so wakelock calls will succeed
                     long identity = Binder.clearCallingIdentity();
-                    receiver.decrementPendingBroadcastsLocked();
-                    Binder.restoreCallingIdentity(identity);
+                    try {
+                        receiver.decrementPendingBroadcastsLocked();
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
                 }
             }
         }
     }
 
-    /**
-     * Returns the year of the GNSS hardware.
-     */
     @Override
     public int getGnssYearOfHardware() {
         if (mGnssSystemInfoProvider != null) {
@@ -1292,10 +1567,6 @@
         }
     }
 
-
-    /**
-     * Returns the model name of the GNSS hardware.
-     */
     @Override
     @Nullable
     public String getGnssHardwareModelName() {
@@ -1306,32 +1577,24 @@
         }
     }
 
-    /**
-     * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
-     * (try to) access GNSS information at this layer.
-     */
     private boolean hasGnssPermissions(String packageName) {
-        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-        checkResolutionLevelIsSufficientForProviderUse(
-                allowedResolutionLevel,
-                LocationManager.GPS_PROVIDER);
+        synchronized (mLock) {
+            int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+            checkResolutionLevelIsSufficientForProviderUseLocked(
+                    allowedResolutionLevel,
+                    GPS_PROVIDER);
 
-        int pid = Binder.getCallingPid();
-        int uid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
-        boolean hasLocationAccess;
-        try {
-            hasLocationAccess = checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+            int pid = Binder.getCallingPid();
+            int uid = Binder.getCallingUid();
+            long identity = Binder.clearCallingIdentity();
+            try {
+                return checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
-
-        return hasLocationAccess;
     }
 
-    /**
-     * Returns the GNSS batching size, if available.
-     */
     @Override
     public int getGnssBatchSize(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1344,10 +1607,6 @@
         }
     }
 
-    /**
-     * Adds a callback for GNSS Batching events, if permissions allow, which are transported
-     * to potentially multiple listeners by the BatchedLocationCallbackTransport above this.
-     */
     @Override
     public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1357,18 +1616,20 @@
             return false;
         }
 
-        mGnssBatchingCallback = callback;
-        mGnssBatchingDeathCallback = new LinkedCallback(callback);
-        try {
-            callback.asBinder().linkToDeath(mGnssBatchingDeathCallback, 0 /* flags */);
-        } catch (RemoteException e) {
-            // if the remote process registering the listener is already dead, just swallow the
-            // exception and return
-            Log.e(TAG, "Remote listener already died.", e);
-            return false;
-        }
+        synchronized (mLock) {
+            mGnssBatchingCallback = callback;
+            mGnssBatchingDeathCallback = new LinkedCallback(callback);
+            try {
+                callback.asBinder().linkToDeath(mGnssBatchingDeathCallback, 0 /* flags */);
+            } catch (RemoteException e) {
+                // if the remote process registering the listener is already dead, just swallow the
+                // exception and return
+                Log.e(TAG, "Remote listener already died.", e);
+                return false;
+            }
 
-        return true;
+            return true;
+        }
     }
 
     private class LinkedCallback implements IBinder.DeathRecipient {
@@ -1391,27 +1652,22 @@
         }
     }
 
-    /**
-     * Removes callback for GNSS batching
-     */
     @Override
     public void removeGnssBatchingCallback() {
-        try {
-            mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathCallback,
-                    0 /* flags */);
-        } catch (NoSuchElementException e) {
-            // if the death callback isn't connected (it should be...), log error, swallow the
-            // exception and return
-            Log.e(TAG, "Couldn't unlink death callback.", e);
+        synchronized (mLock) {
+            try {
+                mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathCallback,
+                        0 /* flags */);
+            } catch (NoSuchElementException e) {
+                // if the death callback isn't connected (it should be...), log error, swallow the
+                // exception and return
+                Log.e(TAG, "Couldn't unlink death callback.", e);
+            }
+            mGnssBatchingCallback = null;
+            mGnssBatchingDeathCallback = null;
         }
-        mGnssBatchingCallback = null;
-        mGnssBatchingDeathCallback = null;
     }
 
-
-    /**
-     * Starts GNSS batching, if available.
-     */
     @Override
     public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1421,20 +1677,20 @@
             return false;
         }
 
-        if (mGnssBatchingInProgress) {
-            // Current design does not expect multiple starts to be called repeatedly
-            Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
-            // Try to clean up anyway, and continue
-            stopGnssBatch();
-        }
+        synchronized (mLock) {
+            if (mGnssBatchingInProgress) {
+                // Current design does not expect multiple starts to be called repeatedly
+                Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
+                // Try to clean up anyway, and continue
+                stopGnssBatch();
+            }
 
-        mGnssBatchingInProgress = true;
-        return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
+            mGnssBatchingInProgress = true;
+            return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
+        }
     }
 
-    /**
-     * Flushes a GNSS batch in progress
-     */
+
     @Override
     public void flushGnssBatch(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1445,117 +1701,66 @@
             return;
         }
 
-        if (!mGnssBatchingInProgress) {
-            Log.w(TAG, "flushGnssBatch called with no batch in progress");
-        }
+        synchronized (mLock) {
+            if (!mGnssBatchingInProgress) {
+                Log.w(TAG, "flushGnssBatch called with no batch in progress");
+            }
 
-        if (mGnssBatchingProvider != null) {
-            mGnssBatchingProvider.flush();
+            if (mGnssBatchingProvider != null) {
+                mGnssBatchingProvider.flush();
+            }
         }
     }
 
-    /**
-     * Stops GNSS batching
-     */
     @Override
     public boolean stopGnssBatch() {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
                 "Location Hardware permission not granted to access hardware batching");
 
-        if (mGnssBatchingProvider != null) {
-            mGnssBatchingInProgress = false;
-            return mGnssBatchingProvider.stop();
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public void reportLocationBatch(List<Location> locations) {
-        checkCallerIsProvider();
-
-        // Currently used only for GNSS locations - update permissions check if changed
-        if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
-            if (mGnssBatchingCallback == null) {
-                Slog.e(TAG, "reportLocationBatch() called without active Callback");
-                return;
-            }
-            try {
-                mGnssBatchingCallback.onLocationBatch(locations);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
-            }
-        } else {
-            Slog.w(TAG, "reportLocationBatch() called without user permission, locations blocked");
-        }
-    }
-
-    private void addProviderLocked(LocationProvider provider) {
-        mProviders.add(provider);
-        mProvidersByName.put(provider.getName(), provider);
-    }
-
-    private void removeProviderLocked(LocationProvider provider) {
-        mProviders.remove(provider);
-        mProvidersByName.remove(provider.getName());
-    }
-
-    /**
-     * Returns "true" if access to the specified location provider is allowed by the specified
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     * @param userId   the user id to query
-     */
-    private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
-        if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUser(userId);
-        }
-        if (LocationManager.FUSED_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUser(userId);
-        }
         synchronized (mLock) {
-            if (mMockProviders.containsKey(provider)) {
-                return isLocationEnabledForUser(userId);
+            if (mGnssBatchingProvider != null) {
+                mGnssBatchingInProgress = false;
+                return mGnssBatchingProvider.stop();
+            } else {
+                return false;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void addProviderLocked(LocationProvider provider) {
+        Preconditions.checkState(getLocationProviderLocked(provider.getName()) == null);
+
+        mProviders.add(provider);
+
+        provider.onAllowedChangedLocked();  // allowed state may change while provider was inactive
+        provider.onUseableChangedLocked();
+    }
+
+    @GuardedBy("mLock")
+    private void removeProviderLocked(LocationProvider provider) {
+        if (mProviders.remove(provider)) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                provider.onUseableChangedLocked();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private LocationProvider getLocationProviderLocked(String providerName) {
+        for (LocationProvider provider : mProviders) {
+            if (providerName.equals(provider.getName())) {
+                return provider;
             }
         }
 
-        long identity = Binder.clearCallingIdentity();
-        try {
-            // Use system settings
-            ContentResolver cr = mContext.getContentResolver();
-            String allowedProviders = Settings.Secure.getStringForUser(
-                    cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
-            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        return null;
     }
 
-
-    /**
-     * Returns "true" if access to the specified location provider is allowed by the specified
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     * @param uid      the requestor's UID
-     * @param userId   the user id to query
-     */
-    private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) {
-        if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
-            return false;
-        }
-        return isAllowedByUserSettingsLockedForUser(provider, userId);
-    }
-
-    /**
-     * Returns the permission string associated with the specified resolution level.
-     *
-     * @param resolutionLevel the resolution level
-     * @return the permission string
-     */
     private String getResolutionPermission(int resolutionLevel) {
         switch (resolutionLevel) {
             case RESOLUTION_LEVEL_FINE:
@@ -1567,13 +1772,6 @@
         }
     }
 
-    /**
-     * Returns the resolution level allowed to the given PID/UID pair.
-     *
-     * @param pid the PID
-     * @param uid the UID
-     * @return resolution level allowed to the pid/uid pair
-     */
     private int getAllowedResolutionLevel(int pid, int uid) {
         if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
                 pid, uid) == PERMISSION_GRANTED) {
@@ -1586,39 +1784,22 @@
         }
     }
 
-    /**
-     * Returns the resolution level allowed to the caller
-     *
-     * @return resolution level allowed to caller
-     */
     private int getCallerAllowedResolutionLevel() {
         return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
     }
 
-    /**
-     * Throw SecurityException if specified resolution level is insufficient to use geofences.
-     *
-     * @param allowedResolutionLevel resolution level allowed to caller
-     */
     private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
         if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
             throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
         }
     }
 
-    /**
-     * Return the minimum resolution level required to use the specified location provider.
-     *
-     * @param provider the name of the location provider
-     * @return minimum resolution level required for provider
-     */
-    private int getMinimumResolutionLevelForProviderUse(String provider) {
-        if (LocationManager.GPS_PROVIDER.equals(provider) ||
-                LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+    @GuardedBy("mLock")
+    private int getMinimumResolutionLevelForProviderUseLocked(String provider) {
+        if (GPS_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) {
             // gps and passive providers require FINE permission
             return RESOLUTION_LEVEL_FINE;
-        } else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
-                LocationManager.FUSED_PROVIDER.equals(provider)) {
+        } else if (NETWORK_PROVIDER.equals(provider) || FUSED_PROVIDER.equals(provider)) {
             // network and fused providers are ok with COARSE or FINE
             return RESOLUTION_LEVEL_COARSE;
         } else {
@@ -1627,7 +1808,7 @@
                     continue;
                 }
 
-                ProviderProperties properties = lp.getProperties();
+                ProviderProperties properties = lp.getPropertiesLocked();
                 if (properties != null) {
                     if (properties.mRequiresSatellite) {
                         // provider requiring satellites require FINE permission
@@ -1643,16 +1824,10 @@
         return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
     }
 
-    /**
-     * Throw SecurityException if specified resolution level is insufficient to use the named
-     * location provider.
-     *
-     * @param allowedResolutionLevel resolution level allowed to caller
-     * @param providerName           the name of the location provider
-     */
-    private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
+    @GuardedBy("mLock")
+    private void checkResolutionLevelIsSufficientForProviderUseLocked(int allowedResolutionLevel,
             String providerName) {
-        int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
+        int requiredResolutionLevel = getMinimumResolutionLevelForProviderUseLocked(providerName);
         if (allowedResolutionLevel < requiredResolutionLevel) {
             switch (requiredResolutionLevel) {
                 case RESOLUTION_LEVEL_FINE:
@@ -1668,20 +1843,6 @@
         }
     }
 
-    /**
-     * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
-     * for battery).
-     */
-    private void checkDeviceStatsAllowed() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.UPDATE_DEVICE_STATS, null);
-    }
-
-    private void checkUpdateAppOpsAllowed() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
-    }
-
     public static int resolutionLevelToOp(int allowedResolutionLevel) {
         if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
             if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
@@ -1739,19 +1900,17 @@
      */
     @Override
     public List<String> getAllProviders() {
-        ArrayList<String> out;
         synchronized (mLock) {
-            out = new ArrayList<>(mProviders.size());
+            ArrayList<String> providers = new ArrayList<>(mProviders.size());
             for (LocationProvider provider : mProviders) {
                 String name = provider.getName();
-                if (LocationManager.FUSED_PROVIDER.equals(name)) {
+                if (FUSED_PROVIDER.equals(name)) {
                     continue;
                 }
-                out.add(name);
+                providers.add(name);
             }
+            return providers;
         }
-        if (D) Log.d(TAG, "getAllProviders()=" + out);
-        return out;
     }
 
     /**
@@ -1762,37 +1921,28 @@
     @Override
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-        ArrayList<String> out;
-        int uid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                out = new ArrayList<>(mProviders.size());
-                for (LocationProvider provider : mProviders) {
-                    String name = provider.getName();
-                    if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                        continue;
-                    }
-                    if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
-                        if (enabledOnly
-                                && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
-                            continue;
-                        }
-                        if (criteria != null
-                                && !android.location.LocationProvider.propertiesMeetCriteria(
-                                name, provider.getProperties(), criteria)) {
-                            continue;
-                        }
-                        out.add(name);
-                    }
+        synchronized (mLock) {
+            ArrayList<String> providers = new ArrayList<>(mProviders.size());
+            for (LocationProvider provider : mProviders) {
+                String name = provider.getName();
+                if (FUSED_PROVIDER.equals(name)) {
+                    continue;
                 }
+                if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUseLocked(name)) {
+                    continue;
+                }
+                if (enabledOnly && !provider.isUseableLocked()) {
+                    continue;
+                }
+                if (criteria != null
+                        && !android.location.LocationProvider.propertiesMeetCriteria(
+                        name, provider.getPropertiesLocked(), criteria)) {
+                    continue;
+                }
+                providers.add(name);
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+            return providers;
         }
-
-        if (D) Log.d(TAG, "getProviders()=" + out);
-        return out;
     }
 
     /**
@@ -1804,71 +1954,36 @@
      */
     @Override
     public String getBestProvider(Criteria criteria, boolean enabledOnly) {
-        String result;
-
         List<String> providers = getProviders(criteria, enabledOnly);
-        if (!providers.isEmpty()) {
-            result = pickBest(providers);
-            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
-            return result;
-        }
-        providers = getProviders(null, enabledOnly);
-        if (!providers.isEmpty()) {
-            result = pickBest(providers);
-            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
-            return result;
+        if (providers.isEmpty()) {
+            providers = getProviders(null, enabledOnly);
         }
 
-        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
+        if (!providers.isEmpty()) {
+            if (providers.contains(GPS_PROVIDER)) {
+                return GPS_PROVIDER;
+            } else if (providers.contains(NETWORK_PROVIDER)) {
+                return NETWORK_PROVIDER;
+            } else {
+                return providers.get(0);
+            }
+        }
+
         return null;
     }
 
-    private String pickBest(List<String> providers) {
-        if (providers.contains(LocationManager.GPS_PROVIDER)) {
-            return LocationManager.GPS_PROVIDER;
-        } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
-            return LocationManager.NETWORK_PROVIDER;
-        } else {
-            return providers.get(0);
-        }
-    }
-
-    @Override
-    public boolean providerMeetsCriteria(String provider, Criteria criteria) {
-        LocationProvider p = mProvidersByName.get(provider);
-        if (p == null) {
-            throw new IllegalArgumentException("provider=" + provider);
-        }
-
-        boolean result = android.location.LocationProvider.propertiesMeetCriteria(
-                p.getName(), p.getProperties(), criteria);
-        if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
-        return result;
-    }
-
-    private void updateProvidersSettingsLocked() {
-        for (LocationProvider p : mProviders) {
-            p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
-        }
-
-        mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
-                UserHandle.ALL);
-    }
-
-    private void updateProviderListenersLocked(String provider) {
-        LocationProvider p = mProvidersByName.get(provider);
-        if (p == null) return;
-
-        boolean enabled = p.isEnabled();
+    @GuardedBy("mLock")
+    private void updateProviderUseableLocked(LocationProvider provider) {
+        boolean useable = provider.isUseableLocked();
 
         ArrayList<Receiver> deadReceivers = null;
 
-        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
         if (records != null) {
             for (UpdateRecord record : records) {
-                if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+                if (isCurrentProfileLocked(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                     // Sends a notification message to the receiver
-                    if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
+                    if (!record.mReceiver.callProviderEnabledLocked(provider.getName(), useable)) {
                         if (deadReceivers == null) {
                             deadReceivers = new ArrayList<>();
                         }
@@ -1887,25 +2002,37 @@
         applyRequirementsLocked(provider);
     }
 
-    private void applyRequirementsLocked(String provider) {
-        LocationProvider p = mProvidersByName.get(provider);
-        if (p == null) return;
+    @GuardedBy("mLock")
+    private void applyRequirementsLocked(String providerName) {
+        LocationProvider provider = getLocationProviderLocked(providerName);
+        if (provider != null) {
+            applyRequirementsLocked(provider);
+        }
+    }
 
-        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+    @GuardedBy("mLock")
+    private void applyRequirementsLocked(LocationProvider provider) {
+        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
         WorkSource worksource = new WorkSource();
         ProviderRequest providerRequest = new ProviderRequest();
 
-        ContentResolver resolver = mContext.getContentResolver();
-        long backgroundThrottleInterval = Settings.Global.getLong(
-                resolver,
-                Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
-                DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+        long backgroundThrottleInterval;
 
-        if (p.isEnabled() && records != null && !records.isEmpty()) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            backgroundThrottleInterval = Settings.Global.getLong(
+                    mContext.getContentResolver(),
+                    Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+                    DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        if (provider.isUseableLocked() && records != null && !records.isEmpty()) {
             // initialize the low power mode to true and set to false if any of the records requires
             providerRequest.lowPowerMode = true;
             for (UpdateRecord record : records) {
-                if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+                if (isCurrentProfileLocked(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                     if (checkLocationAccess(
                             record.mReceiver.mIdentity.mPid,
                             record.mReceiver.mIdentity.mUid,
@@ -1945,7 +2072,8 @@
                 // under that threshold.
                 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
                 for (UpdateRecord record : records) {
-                    if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+                    if (isCurrentProfileLocked(
+                            UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                         LocationRequest locationRequest = record.mRequest;
 
                         // Don't assign battery blame for update records whose
@@ -1972,7 +2100,7 @@
         }
 
         if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
-        p.setRequest(providerRequest, worksource);
+        provider.setRequestLocked(providerRequest, worksource);
     }
 
     /**
@@ -1995,34 +2123,11 @@
     @Override
     public String[] getBackgroundThrottlingWhitelist() {
         synchronized (mLock) {
-            return mBackgroundThrottlePackageWhitelist.toArray(
-                    new String[0]);
+            return mBackgroundThrottlePackageWhitelist.toArray(new String[0]);
         }
     }
 
-    private void updateBackgroundThrottlingWhitelistLocked() {
-        String setting = Settings.Global.getString(
-                mContext.getContentResolver(),
-                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
-        if (setting == null) {
-            setting = "";
-        }
-
-        mBackgroundThrottlePackageWhitelist.clear();
-        mBackgroundThrottlePackageWhitelist.addAll(
-                SystemConfig.getInstance().getAllowUnthrottledLocation());
-        mBackgroundThrottlePackageWhitelist.addAll(
-                Arrays.asList(setting.split(",")));
-    }
-
-    private void updateLastLocationMaxAgeLocked() {
-        mLastLocationMaxAgeMs =
-                Settings.Global.getLong(
-                        mContext.getContentResolver(),
-                        Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
-                        DEFAULT_LAST_LOCATION_MAX_AGE_MS);
-    }
-
+    @GuardedBy("mLock")
     private boolean isThrottlingExemptLocked(Identity identity) {
         if (identity.mUid == Process.SYSTEM_UID) {
             return true;
@@ -2032,8 +2137,8 @@
             return true;
         }
 
-        for (LocationProviderProxy provider : mProxyProviders) {
-            if (identity.mPackageName.equals(provider.getConnectedPackageName())) {
+        for (LocationProvider provider : mProviders) {
+            if (identity.mPackageName.equals(provider.getPackageLocked())) {
                 return true;
             }
         }
@@ -2119,6 +2224,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
             String packageName, WorkSource workSource, boolean hideFromAppOps) {
         IBinder binder = listener.asBinder();
@@ -2137,6 +2243,7 @@
         return receiver;
     }
 
+    @GuardedBy("mLock")
     private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
             WorkSource workSource, boolean hideFromAppOps) {
         Receiver receiver = mReceivers.get(intent);
@@ -2202,67 +2309,65 @@
         throw new SecurityException("invalid package name: " + packageName);
     }
 
-    private void checkPendingIntent(PendingIntent intent) {
-        if (intent == null) {
-            throw new IllegalArgumentException("invalid pending intent: " + null);
-        }
-    }
-
-    private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
-            int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
-        if (intent == null && listener == null) {
-            throw new IllegalArgumentException("need either listener or intent");
-        } else if (intent != null && listener != null) {
-            throw new IllegalArgumentException("cannot register both listener and intent");
-        } else if (intent != null) {
-            checkPendingIntent(intent);
-            return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
-        } else {
-            return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
-        }
-    }
-
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
             PendingIntent intent, String packageName) {
-        if (request == null) request = DEFAULT_LOCATION_REQUEST;
-        checkPackageName(packageName);
-        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-        checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
-                request.getProvider());
-        WorkSource workSource = request.getWorkSource();
-        if (workSource != null && !workSource.isEmpty()) {
-            checkDeviceStatsAllowed();
-        }
-        boolean hideFromAppOps = request.getHideFromAppOps();
-        if (hideFromAppOps) {
-            checkUpdateAppOpsAllowed();
-        }
-        boolean callerHasLocationHardwarePermission =
-                mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
-                        == PERMISSION_GRANTED;
-        LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
-                callerHasLocationHardwarePermission);
-
-        final int pid = Binder.getCallingPid();
-        final int uid = Binder.getCallingUid();
-        // providers may use public location API's, need to clear identity
-        long identity = Binder.clearCallingIdentity();
-        try {
-            // We don't check for MODE_IGNORED here; we will do that when we go to deliver
-            // a location.
-            checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
-
-            synchronized (mLock) {
-                Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                        packageName, workSource, hideFromAppOps);
-                requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
+        synchronized (mLock) {
+            if (request == null) request = DEFAULT_LOCATION_REQUEST;
+            checkPackageName(packageName);
+            int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+            checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
+                    request.getProvider());
+            WorkSource workSource = request.getWorkSource();
+            if (workSource != null && !workSource.isEmpty()) {
+                mContext.enforceCallingOrSelfPermission(
+                        Manifest.permission.UPDATE_DEVICE_STATS, null);
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+            boolean hideFromAppOps = request.getHideFromAppOps();
+            if (hideFromAppOps) {
+                mContext.enforceCallingOrSelfPermission(
+                        Manifest.permission.UPDATE_APP_OPS_STATS, null);
+            }
+            boolean callerHasLocationHardwarePermission =
+                    mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
+                            == PERMISSION_GRANTED;
+            LocationRequest sanitizedRequest = createSanitizedRequest(request,
+                    allowedResolutionLevel,
+                    callerHasLocationHardwarePermission);
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+
+                // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+                // a location.
+                checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
+
+                if (intent == null && listener == null) {
+                    throw new IllegalArgumentException("need either listener or intent");
+                } else if (intent != null && listener != null) {
+                    throw new IllegalArgumentException(
+                            "cannot register both listener and intent");
+                }
+
+                Receiver receiver;
+                if (intent != null) {
+                    receiver = getReceiverLocked(intent, pid, uid, packageName, workSource,
+                            hideFromAppOps);
+                } else {
+                    receiver = getReceiverLocked(listener, pid, uid, packageName, workSource,
+                            hideFromAppOps);
+                }
+                requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
+    @GuardedBy("mLock")
     private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
             int uid, String packageName) {
         // Figure out the provider. Either its explicitly request (legacy use cases), or
@@ -2273,7 +2378,7 @@
             throw new IllegalArgumentException("provider name must not be null");
         }
 
-        LocationProvider provider = mProvidersByName.get(name);
+        LocationProvider provider = getLocationProviderLocked(name);
         if (provider == null) {
             throw new IllegalArgumentException("provider doesn't exist: " + name);
         }
@@ -2292,7 +2397,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        if (provider.isEnabled()) {
+        if (provider.isUseableLocked()) {
             applyRequirementsLocked(name);
         } else {
             // Notify the listener that updates are currently disabled
@@ -2308,14 +2413,23 @@
             String packageName) {
         checkPackageName(packageName);
 
-        final int pid = Binder.getCallingPid();
-        final int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
+        int uid = Binder.getCallingUid();
+
+        if (intent == null && listener == null) {
+            throw new IllegalArgumentException("need either listener or intent");
+        } else if (intent != null && listener != null) {
+            throw new IllegalArgumentException("cannot register both listener and intent");
+        }
 
         synchronized (mLock) {
-            Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                    packageName, null, false);
+            Receiver receiver;
+            if (intent != null) {
+                receiver = getReceiverLocked(intent, pid, uid, packageName, null, false);
+            } else {
+                receiver = getReceiverLocked(listener, pid, uid, packageName, null, false);
+            }
 
-            // providers may use public location API's, need to clear identity
             long identity = Binder.clearCallingIdentity();
             try {
                 removeUpdatesLocked(receiver);
@@ -2325,6 +2439,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void removeUpdatesLocked(Receiver receiver) {
         if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
 
@@ -2356,51 +2471,53 @@
         }
     }
 
-    private void applyAllProviderRequirementsLocked() {
-        for (LocationProvider p : mProviders) {
-            applyRequirementsLocked(p.getName());
-        }
-    }
-
     @Override
-    public Location getLastLocation(LocationRequest request, String packageName) {
-        if (D) Log.d(TAG, "getLastLocation: " + request);
-        if (request == null) request = DEFAULT_LOCATION_REQUEST;
-        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-        checkPackageName(packageName);
-        checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
-                request.getProvider());
-        // no need to sanitize this request, as only the provider name is used
+    public Location getLastLocation(LocationRequest r, String packageName) {
+        if (D) Log.d(TAG, "getLastLocation: " + r);
+        synchronized (mLock) {
+            LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
+            int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+            checkPackageName(packageName);
+            checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
+                    request.getProvider());
+            // no need to sanitize this request, as only the provider name is used
 
-        final int pid = Binder.getCallingPid();
-        final int uid = Binder.getCallingUid();
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            if (mBlacklist.isBlacklisted(packageName)) {
-                if (D) {
-                    Log.d(TAG, "not returning last loc for blacklisted app: " +
-                            packageName);
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (mBlacklist.isBlacklisted(packageName)) {
+                    if (D) {
+                        Log.d(TAG, "not returning last loc for blacklisted app: "
+                                + packageName);
+                    }
+                    return null;
                 }
-                return null;
-            }
 
-            if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel)) {
-                if (D) {
-                    Log.d(TAG, "not returning last loc for no op app: " +
-                            packageName);
+                if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel)) {
+                    if (D) {
+                        Log.d(TAG, "not returning last loc for no op app: "
+                                + packageName);
+                    }
+                    return null;
                 }
-                return null;
-            }
 
-            synchronized (mLock) {
                 // Figure out the provider. Either its explicitly request (deprecated API's),
                 // or use the fused provider
                 String name = request.getProvider();
                 if (name == null) name = LocationManager.FUSED_PROVIDER;
-                LocationProvider provider = mProvidersByName.get(name);
+                LocationProvider provider = getLocationProviderLocked(name);
                 if (provider == null) return null;
 
-                if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
+                // only the current user or location providers may get location this way
+                if (!isCurrentProfileLocked(UserHandle.getUserId(uid)) && !isLocationProviderLocked(
+                        uid)) {
+                    return null;
+                }
+
+                if (!provider.isUseableLocked()) {
+                    return null;
+                }
 
                 Location location;
                 if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
@@ -2416,9 +2533,12 @@
 
                 // Don't return stale location to apps with foreground-only location permission.
                 String op = resolutionLevelToOpStr(allowedResolutionLevel);
-                long locationAgeMs = SystemClock.elapsedRealtime() -
-                        location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
-                if ((locationAgeMs > mLastLocationMaxAgeMs)
+                long locationAgeMs = SystemClock.elapsedRealtime()
+                        - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+                if ((locationAgeMs > Settings.Global.getLong(
+                        mContext.getContentResolver(),
+                        Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+                        DEFAULT_LAST_LOCATION_MAX_AGE_MS))
                         && (mAppOps.unsafeCheckOp(op, uid, packageName)
                         == AppOpsManager.MODE_FOREGROUND)) {
                     return null;
@@ -2433,24 +2553,13 @@
                 } else {
                     return new Location(location);
                 }
+                return null;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            return null;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
         }
     }
 
-    /**
-     * Provides an interface to inject and set the last location if location is not available
-     * currently.
-     *
-     * This helps in cases where the product (Cars for example) has saved the last known location
-     * before powering off.  This interface lets the client inject the saved location while the GPS
-     * chipset is getting its first fix, there by improving user experience.
-     *
-     * @param location - Location object to inject
-     * @return true if update was successful, false if not
-     */
     @Override
     public boolean injectLocation(Location location) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -2464,38 +2573,23 @@
             }
             return false;
         }
-        LocationProvider p = null;
-        String provider = location.getProvider();
-        if (provider != null) {
-            p = mProvidersByName.get(provider);
-        }
-        if (p == null) {
-            if (D) {
-                Log.d(TAG, "injectLocation(): unknown provider");
-            }
-            return false;
-        }
+
         synchronized (mLock) {
-            if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
-                if (D) {
-                    Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
-                }
+            LocationProvider provider = getLocationProviderLocked(location.getProvider());
+            if (provider == null || !provider.isUseableLocked()) {
                 return false;
-            } else {
-                // NOTE: If last location is already available, location is not injected.  If
-                // provider's normal source (like a GPS chipset) have already provided an output,
-                // there is no need to inject this location.
-                if (mLastLocation.get(provider) == null) {
-                    updateLastLocationLocked(location, provider);
-                } else {
-                    if (D) {
-                        Log.d(TAG, "injectLocation(): Location exists. Not updating");
-                    }
-                    return false;
-                }
             }
+
+            // NOTE: If last location is already available, location is not injected.  If
+            // provider's normal source (like a GPS chipset) have already provided an output
+            // there is no need to inject this location.
+            if (mLastLocation.get(provider.getName()) != null) {
+                return false;
+            }
+
+            updateLastLocationLocked(location, provider.getName());
+            return true;
         }
-        return true;
     }
 
     @Override
@@ -2504,39 +2598,49 @@
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
-        checkPendingIntent(intent);
-        checkPackageName(packageName);
-        checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
-                request.getProvider());
-        // Require that caller can manage given document
-        boolean callerHasLocationHardwarePermission =
-                mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
-                        == PERMISSION_GRANTED;
-        LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
-                callerHasLocationHardwarePermission);
-
-        if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
-
-        // geo-fence manager uses the public location API, need to clear identity
-        int uid = Binder.getCallingUid();
-        // TODO: http://b/23822629
-        if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) {
-            // temporary measure until geofences work for secondary users
-            Log.w(TAG, "proximity alerts are currently available only to the primary user");
-            return;
+        if (intent == null) {
+            throw new IllegalArgumentException("invalid pending intent: " + null);
         }
-        long identity = Binder.clearCallingIdentity();
-        try {
-            mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
-                    uid, packageName);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        checkPackageName(packageName);
+        synchronized (mLock) {
+            checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
+                    request.getProvider());
+            // Require that caller can manage given document
+            boolean callerHasLocationHardwarePermission =
+                    mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
+                            == PERMISSION_GRANTED;
+            LocationRequest sanitizedRequest = createSanitizedRequest(request,
+                    allowedResolutionLevel,
+                    callerHasLocationHardwarePermission);
+
+            if (D) {
+                Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
+            }
+
+            // geo-fence manager uses the public location API, need to clear identity
+            int uid = Binder.getCallingUid();
+            // TODO: http://b/23822629
+            if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) {
+                // temporary measure until geofences work for secondary users
+                Log.w(TAG, "proximity alerts are currently available only to the primary user");
+                return;
+            }
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mGeofenceManager.addFence(sanitizedRequest, geofence, intent,
+                        allowedResolutionLevel,
+                        uid, packageName);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     @Override
     public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
-        checkPendingIntent(intent);
+        if (intent == null) {
+            throw new IllegalArgumentException("invalid pending intent: " + null);
+        }
         checkPackageName(packageName);
 
         if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2641,6 +2745,7 @@
         synchronized (mLock) {
             Identity callerIdentity
                     = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+
             // TODO(b/120481270): Register for client death notification and update map.
             mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
@@ -2670,25 +2775,26 @@
     }
 
     @Override
-    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
-        if (provider == null) {
+    public boolean sendExtraCommand(String providerName, String command, Bundle extras) {
+        if (providerName == null) {
             // throw NullPointerException to remain compatible with previous implementation
             throw new NullPointerException();
         }
-        checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
-                provider);
-
-        // and check for ACCESS_LOCATION_EXTRA_COMMANDS
-        if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
-                != PERMISSION_GRANTED)) {
-            throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
-        }
-
         synchronized (mLock) {
-            LocationProvider p = mProvidersByName.get(provider);
-            if (p == null) return false;
+            checkResolutionLevelIsSufficientForProviderUseLocked(getCallerAllowedResolutionLevel(),
+                    providerName);
 
-            p.sendExtraCommand(command, extras);
+            // and check for ACCESS_LOCATION_EXTRA_COMMANDS
+            if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
+                    != PERMISSION_GRANTED)) {
+                throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
+            }
+
+            LocationProvider provider = getLocationProviderLocked(providerName);
+            if (provider != null) {
+                provider.sendExtraCommandLocked(command, extras);
+            }
+
             return true;
         }
     }
@@ -2707,44 +2813,29 @@
         }
     }
 
-    /**
-     * @return null if the provider does not exist
-     * @throws SecurityException if the provider is not allowed to be
-     *                           accessed by the caller
-     */
     @Override
-    public ProviderProperties getProviderProperties(String provider) {
-        checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
-                provider);
-
-        LocationProvider p;
+    public ProviderProperties getProviderProperties(String providerName) {
         synchronized (mLock) {
-            p = mProvidersByName.get(provider);
-        }
+            checkResolutionLevelIsSufficientForProviderUseLocked(getCallerAllowedResolutionLevel(),
+                    providerName);
 
-        if (p == null) return null;
-        return p.getProperties();
+            LocationProvider provider = getLocationProviderLocked(providerName);
+            if (provider == null) {
+                return null;
+            }
+            return provider.getPropertiesLocked();
+        }
     }
 
-    /**
-     * @return null if the provider does not exist
-     * @throws SecurityException if the provider is not allowed to be
-     *                           accessed by the caller
-     */
     @Override
     public String getNetworkProviderPackage() {
-        LocationProvider p;
         synchronized (mLock) {
-            p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+            LocationProvider provider = getLocationProviderLocked(NETWORK_PROVIDER);
+            if (provider == null) {
+                return null;
+            }
+            return provider.getPackageLocked();
         }
-
-        if (p == null) {
-            return null;
-        }
-        if (p.mProvider instanceof LocationProviderProxy) {
-            return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
-        }
-        return null;
     }
 
     @Override
@@ -2780,241 +2871,96 @@
         }
     }
 
-    /**
-     * Returns the current location enabled/disabled status for a user
-     *
-     * @param userId the id of the user
-     * @return true if location is enabled
-     */
+    private boolean isLocationEnabled() {
+        return isLocationEnabledForUser(mCurrentUserId);
+    }
+
     @Override
     public boolean isLocationEnabledForUser(int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
+        }
 
         long identity = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                final String allowedProviders = Settings.Secure.getStringForUser(
+            boolean enabled;
+            try {
+                enabled = Settings.Secure.getIntForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_MODE,
+                        userId) != Settings.Secure.LOCATION_MODE_OFF;
+            } catch (Settings.SettingNotFoundException e) {
+                // OS upgrade case where mode isn't set yet
+                enabled = !TextUtils.isEmpty(Settings.Secure.getStringForUser(
                         mContext.getContentResolver(),
                         Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        userId);
-                if (allowedProviders == null) {
-                    return false;
+                        userId));
+
+                try {
+                    Settings.Secure.putIntForUser(
+                            mContext.getContentResolver(),
+                            Settings.Secure.LOCATION_MODE,
+                            enabled
+                                    ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
+                                    : Settings.Secure.LOCATION_MODE_OFF,
+                            userId);
+                } catch (RuntimeException ex) {
+                    // any problem with writing should not be propagated
+                    Slog.e(TAG, "error updating location mode", ex);
                 }
-                final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-                for (String provider : mRealProviders.keySet()) {
-                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                        continue;
-                    }
-                    if (providerList.contains(provider)) {
-                        return true;
-                    }
-                }
-                return false;
             }
+            return enabled;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
-    /**
-     * Enable or disable location for a user
-     *
-     * @param enabled true to enable location, false to disable location
-     * @param userId  the id of the user
-     */
-    @Override
-    public void setLocationEnabledForUser(boolean enabled, int userId) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS,
-                "Requires WRITE_SECURE_SETTINGS permission");
-
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                final Set<String> allRealProviders = mRealProviders.keySet();
-                // Update all providers on device plus gps and network provider when disabling
-                // location
-                Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
-                allProvidersSet.addAll(allRealProviders);
-                // When disabling location, disable gps and network provider that could have been
-                // enabled by location mode api.
-                if (!enabled) {
-                    allProvidersSet.add(LocationManager.GPS_PROVIDER);
-                    allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
-                }
-                if (allProvidersSet.isEmpty()) {
-                    return;
-                }
-                // to ensure thread safety, we write the provider name with a '+' or '-'
-                // and let the SettingsProvider handle it rather than reading and modifying
-                // the list of enabled providers.
-                final String prefix = enabled ? "+" : "-";
-                StringBuilder locationProvidersAllowed = new StringBuilder();
-                for (String provider : allProvidersSet) {
-                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                        continue;
-                    }
-                    locationProvidersAllowed.append(prefix);
-                    locationProvidersAllowed.append(provider);
-                    locationProvidersAllowed.append(",");
-                }
-                // Remove the trailing comma
-                locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
-                Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        locationProvidersAllowed.toString(),
-                        userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Returns the current enabled/disabled status of a location provider and user
-     *
-     * @param providerName name of the provider
-     * @param userId       the id of the user
-     * @return true if the provider exists and is enabled
-     */
     @Override
     public boolean isProviderEnabledForUser(String providerName, int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        if (!isLocationEnabledForUser(userId)) {
-            return false;
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
         }
 
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
-        if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
+        if (FUSED_PROVIDER.equals(providerName)) return false;
 
-        long identity = Binder.clearCallingIdentity();
-        try {
-            LocationProvider provider;
-            synchronized (mLock) {
-                provider = mProvidersByName.get(providerName);
-            }
-            return provider != null && provider.isEnabled();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        synchronized (mLock) {
+            LocationProvider provider = getLocationProviderLocked(providerName);
+            return provider != null && provider.isUseableForUserLocked(userId);
         }
     }
 
-    /**
-     * Enable or disable a single location provider.
-     *
-     * @param provider name of the provider
-     * @param enabled  true to enable the provider. False to disable the provider
-     * @param userId   the id of the user to set
-     * @return true if the value was set, false on errors
-     */
-    @Override
-    public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
-        return false;
-    }
-
-    /**
-     * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
-     * current user id
-     *
-     * @param userId the user id to get or set value
-     */
-    private void checkInteractAcrossUsersPermission(int userId) {
-        int uid = Binder.getCallingUid();
-        if (UserHandle.getUserId(uid) != userId) {
-            if (ActivityManager.checkComponentPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
-                    != PERMISSION_GRANTED) {
-                throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
-            }
-        }
-    }
-
-    /**
-     * Returns "true" if the UID belongs to a bound location provider.
-     *
-     * @param uid the uid
-     * @return true if uid belongs to a bound location provider
-     */
-    private boolean isUidALocationProvider(int uid) {
+    @GuardedBy("mLock")
+    private boolean isLocationProviderLocked(int uid) {
         if (uid == Process.SYSTEM_UID) {
             return true;
         }
-        if (mGeocodeProvider != null) {
-            if (doesUidHavePackage(uid, mGeocodeProvider.getConnectedPackageName())) return true;
-        }
-        for (LocationProviderProxy proxy : mProxyProviders) {
-            if (doesUidHavePackage(uid, proxy.getConnectedPackageName())) return true;
-        }
-        return false;
-    }
 
-    private void checkCallerIsProvider() {
-        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
-                == PERMISSION_GRANTED) {
-            return;
-        }
-
-        // Previously we only used the INSTALL_LOCATION_PROVIDER
-        // check. But that is system or signature
-        // protection level which is not flexible enough for
-        // providers installed oustide the system image. So
-        // also allow providers with a UID matching the
-        // currently bound package name
-
-        if (isUidALocationProvider(Binder.getCallingUid())) {
-            return;
-        }
-
-        throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
-                "or UID of a currently bound location provider");
-    }
-
-    /**
-     * Returns true if the given package belongs to the given uid.
-     */
-    private boolean doesUidHavePackage(int uid, String packageName) {
-        if (packageName == null) {
-            return false;
-        }
         String[] packageNames = mPackageManager.getPackagesForUid(uid);
         if (packageNames == null) {
             return false;
         }
-        for (String name : packageNames) {
-            if (packageName.equals(name)) {
+        for (LocationProvider provider : mProviders) {
+            String packageName = provider.getPackageLocked();
+            if (packageName == null) {
+                continue;
+            }
+            if (ArrayUtils.contains(packageNames, packageName)) {
                 return true;
             }
         }
         return false;
     }
 
-    @Override
-    public void reportLocation(Location location, boolean passive) {
-        checkCallerIsProvider();
-
-        if (!location.isComplete()) {
-            Log.w(TAG, "Dropping incomplete location: " + location);
-            return;
-        }
-
-        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
-        Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
-        m.arg1 = (passive ? 1 : 0);
-        mLocationHandler.sendMessageAtFrontOfQueue(m);
-    }
-
-
-    private static boolean shouldBroadcastSafe(
+    @GuardedBy("mLock")
+    private static boolean shouldBroadcastSafeLocked(
             Location loc, Location lastLoc, UpdateRecord record, long now) {
         // Always broadcast the first update
         if (lastLoc == null) {
@@ -3046,26 +2992,36 @@
         return record.mRealRequest.getExpireAt() >= now;
     }
 
-    private void handleLocationChangedLocked(Location location, boolean passive) {
+    @GuardedBy("mLock")
+    private void handleLocationChangedLocked(Location location, LocationProvider provider) {
+        if (!mProviders.contains(provider)) {
+            return;
+        }
+        if (!location.isComplete()) {
+            Log.w(TAG, "Dropping incomplete location: " + location);
+            return;
+        }
+
+        if (!provider.isPassiveLocked()) {
+            // notify passive provider of the new location
+            mPassiveProvider.updateLocation(location);
+        }
+
         if (D) Log.d(TAG, "incoming location: " + location);
         long now = SystemClock.elapsedRealtime();
-        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
-        // Skip if the provider is unknown.
-        LocationProvider p = mProvidersByName.get(provider);
-        if (p == null) return;
-        updateLastLocationLocked(location, provider);
+        updateLastLocationLocked(location, provider.getName());
         // mLastLocation should have been updated from the updateLastLocationLocked call above.
-        Location lastLocation = mLastLocation.get(provider);
+        Location lastLocation = mLastLocation.get(provider.getName());
         if (lastLocation == null) {
             Log.e(TAG, "handleLocationChangedLocked() updateLastLocation failed");
             return;
         }
 
         // Update last known coarse interval location if enough time has passed.
-        Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
+        Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider.getName());
         if (lastLocationCoarseInterval == null) {
             lastLocationCoarseInterval = new Location(location);
-            mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
+            mLastLocationCoarseInterval.put(provider.getName(), lastLocationCoarseInterval);
         }
         long timeDiffNanos = location.getElapsedRealtimeNanos()
                 - lastLocationCoarseInterval.getElapsedRealtimeNanos();
@@ -3079,7 +3035,7 @@
                 lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
 
         // Skip if there are no UpdateRecords for this provider.
-        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
         if (records == null || records.size() == 0) return;
 
         // Fetch coarse location
@@ -3088,13 +3044,6 @@
             coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
         }
 
-        // Fetch latest status update time
-        long newStatusUpdateTime = p.getStatusUpdateTime();
-
-        // Get latest status
-        Bundle extras = new Bundle();
-        int status = p.getStatus(extras);
-
         ArrayList<Receiver> deadReceivers = null;
         ArrayList<UpdateRecord> deadUpdateRecords = null;
 
@@ -3104,8 +3053,8 @@
             boolean receiverDead = false;
 
             int receiverUserId = UserHandle.getUserId(receiver.mIdentity.mUid);
-            if (!isCurrentProfile(receiverUserId)
-                    && !isUidALocationProvider(receiver.mIdentity.mUid)) {
+            if (!isCurrentProfileLocked(receiverUserId)
+                    && !isLocationProviderLocked(receiver.mIdentity.mUid)) {
                 if (D) {
                     Log.d(TAG, "skipping loc update for background user " + receiverUserId +
                             " (current user: " + mCurrentUserId + ", app: " +
@@ -3142,7 +3091,8 @@
             }
             if (notifyLocation != null) {
                 Location lastLoc = r.mLastFixBroadcast;
-                if ((lastLoc == null) || shouldBroadcastSafe(notifyLocation, lastLoc, r, now)) {
+                if ((lastLoc == null)
+                        || shouldBroadcastSafeLocked(notifyLocation, lastLoc, r, now)) {
                     if (lastLoc == null) {
                         lastLoc = new Location(notifyLocation);
                         r.mLastFixBroadcast = lastLoc;
@@ -3150,7 +3100,8 @@
                         lastLoc.set(notifyLocation);
                     }
                     if (!receiver.callLocationChangedLocked(notifyLocation)) {
-                        Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
+                        Slog.w(TAG, "RemoteException calling onLocationChanged on "
+                                + receiver);
                         receiverDead = true;
                     }
                     r.mRealRequest.decrementNumUpdates();
@@ -3161,12 +3112,16 @@
             // guarded behind this setting now. should be removed completely post-Q
             if (Settings.Global.getInt(mContext.getContentResolver(),
                     LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
+                long newStatusUpdateTime = provider.getStatusUpdateTimeLocked();
+                Bundle extras = new Bundle();
+                int status = provider.getStatusLocked(extras);
+
                 long prevStatusUpdateTime = r.mLastStatusBroadcast;
                 if ((newStatusUpdateTime > prevStatusUpdateTime)
                         && (prevStatusUpdateTime != 0 || status != AVAILABLE)) {
 
                     r.mLastStatusBroadcast = newStatusUpdateTime;
-                    if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+                    if (!receiver.callStatusChangedLocked(provider.getName(), status, extras)) {
                         receiverDead = true;
                         Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
                     }
@@ -3205,12 +3160,7 @@
         }
     }
 
-    /**
-     * Updates last location with the given location
-     *
-     * @param location new location to update
-     * @param provider Location provider to update for
-     */
+    @GuardedBy("mLock")
     private void updateLastLocationLocked(Location location, String provider) {
         Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
         Location lastNoGPSLocation;
@@ -3229,75 +3179,6 @@
         lastLocation.set(location);
     }
 
-    private class LocationWorkerHandler extends Handler {
-        public LocationWorkerHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_LOCATION_CHANGED:
-                    handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
-                    break;
-            }
-        }
-    }
-
-    private boolean isMockProvider(String provider) {
-        synchronized (mLock) {
-            return mMockProviders.containsKey(provider);
-        }
-    }
-
-    private void handleLocationChanged(Location location, boolean passive) {
-        // create a working copy of the incoming Location so that the service can modify it without
-        // disturbing the caller's copy
-        Location myLocation = new Location(location);
-        String provider = myLocation.getProvider();
-
-        // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
-        // bit if location did not come from a mock provider because passive/fused providers can
-        // forward locations from mock providers, and should not grant them legitimacy in doing so.
-        if (!myLocation.isFromMockProvider() && isMockProvider(provider)) {
-            myLocation.setIsFromMockProvider(true);
-        }
-
-        synchronized (mLock) {
-            if (!passive) {
-                // notify passive provider of the new location
-                mPassiveProvider.updateLocation(myLocation);
-            }
-            handleLocationChangedLocked(myLocation, passive);
-        }
-    }
-
-    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
-        @Override
-        public void onPackageDisappeared(String packageName, int reason) {
-            // remove all receivers associated with this package name
-            synchronized (mLock) {
-                ArrayList<Receiver> deadReceivers = null;
-
-                for (Receiver receiver : mReceivers.values()) {
-                    if (receiver.mIdentity.mPackageName.equals(packageName)) {
-                        if (deadReceivers == null) {
-                            deadReceivers = new ArrayList<>();
-                        }
-                        deadReceivers.add(receiver);
-                    }
-                }
-
-                // perform removal outside of mReceivers loop
-                if (deadReceivers != null) {
-                    for (Receiver receiver : deadReceivers) {
-                        removeUpdatesLocked(receiver);
-                    }
-                }
-            }
-        }
-    };
-
     // Geocoder
 
     @Override
@@ -3343,63 +3224,60 @@
             return;
         }
 
-        if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
+        if (PASSIVE_PROVIDER.equals(name)) {
             throw new IllegalArgumentException("Cannot mock the passive location provider");
         }
 
-        long identity = Binder.clearCallingIdentity();
         synchronized (mLock) {
-            // remove the real provider if we are replacing GPS or network provider
-            if (LocationManager.GPS_PROVIDER.equals(name)
-                    || LocationManager.NETWORK_PROVIDER.equals(name)
-                    || LocationManager.FUSED_PROVIDER.equals(name)) {
-                LocationProvider p = mProvidersByName.get(name);
-                if (p != null) {
-                    removeProviderLocked(p);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                LocationProvider oldProvider = getLocationProviderLocked(name);
+                if (oldProvider != null) {
+                    if (oldProvider.isMock()) {
+                        throw new IllegalArgumentException(
+                                "Provider \"" + name + "\" already exists");
+                    }
+
+                    removeProviderLocked(oldProvider);
                 }
+
+                MockLocationProvider mockProviderManager = new MockLocationProvider(name);
+                addProviderLocked(mockProviderManager);
+                mockProviderManager.attachLocked(new MockProvider(mockProviderManager, properties));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            addTestProviderLocked(name, properties);
         }
-        Binder.restoreCallingIdentity(identity);
-    }
-
-    private void addTestProviderLocked(String name, ProviderProperties properties) {
-        if (mProvidersByName.get(name) != null) {
-            throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
-        }
-
-        LocationProvider provider = new LocationProvider(name);
-        MockProvider mockProvider = new MockProvider(provider, properties);
-
-        addProviderLocked(provider);
-        mMockProviders.put(name, mockProvider);
-        mLastLocation.put(name, null);
-        mLastLocationCoarseInterval.put(name, null);
     }
 
     @Override
-    public void removeTestProvider(String provider, String opPackageName) {
+    public void removeTestProvider(String name, String opPackageName) {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
         synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.remove(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
-            }
-
             long identity = Binder.clearCallingIdentity();
             try {
-                removeProviderLocked(mProvidersByName.get(provider));
+                LocationProvider testProvider = getLocationProviderLocked(name);
+                if (testProvider == null || !testProvider.isMock()) {
+                    throw new IllegalArgumentException("Provider \"" + name + "\" unknown");
+                }
+
+                removeProviderLocked(testProvider);
 
                 // reinstate real provider if available
-                LocationProvider realProvider = mRealProviders.get(provider);
+                LocationProvider realProvider = null;
+                for (LocationProvider provider : mRealProviders) {
+                    if (name.equals(provider.getName())) {
+                        realProvider = provider;
+                        break;
+                    }
+                }
+
                 if (realProvider != null) {
                     addProviderLocked(realProvider);
                 }
-                mLastLocation.put(provider, null);
-                mLastLocationCoarseInterval.put(provider, null);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3407,78 +3285,60 @@
     }
 
     @Override
-    public void setTestProviderLocation(String provider, Location loc, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
-
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
-            }
-
-            // Ensure that the location is marked as being mock. There's some logic to do this in
-            // handleLocationChanged(), but it fails if loc has the wrong provider (bug 33091107).
-            Location mock = new Location(loc);
-            mock.setIsFromMockProvider(true);
-
-            if (!TextUtils.isEmpty(loc.getProvider()) && !provider.equals(loc.getProvider())) {
-                // The location has an explicit provider that is different from the mock provider
-                // name. The caller may be trying to fool us via bug 33091107.
-                EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
-                        provider + "!=" + loc.getProvider());
-            }
-
-            // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mockProvider.setLocation(mock);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
-    @Override
-    public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
-
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
-            }
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mockProvider.setEnabled(enabled);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
-    @Override
-    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime,
+    public void setTestProviderLocation(String providerName, Location location,
             String opPackageName) {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
         synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
+            LocationProvider testProvider = getLocationProviderLocked(providerName);
+            if (testProvider == null || !testProvider.isMock()) {
+                throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
             }
-            mockProvider.setStatus(status, extras, updateTime);
+
+            String locationProvider = location.getProvider();
+            if (!TextUtils.isEmpty(locationProvider) && !providerName.equals(locationProvider)) {
+                // The location has an explicit provider that is different from the mock
+                // provider name. The caller may be trying to fool us via b/33091107.
+                EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
+                        providerName + "!=" + location.getProvider());
+            }
+
+            ((MockLocationProvider) testProvider).setLocationLocked(location);
         }
     }
 
-    private void log(String log) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Slog.d(TAG, log);
+    @Override
+    public void setTestProviderEnabled(String providerName, boolean enabled, String opPackageName) {
+        if (!canCallerAccessMockLocation(opPackageName)) {
+            return;
+        }
+
+        synchronized (mLock) {
+            LocationProvider testProvider = getLocationProviderLocked(providerName);
+            if (testProvider == null || !testProvider.isMock()) {
+                throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
+            }
+
+            ((MockLocationProvider) testProvider).setEnabledLocked(enabled);
+        }
+    }
+
+    @Override
+    public void setTestProviderStatus(String providerName, int status, Bundle extras,
+            long updateTime, String opPackageName) {
+        if (!canCallerAccessMockLocation(opPackageName)) {
+            return;
+        }
+
+        synchronized (mLock) {
+            LocationProvider testProvider = getLocationProviderLocked(providerName);
+            if (testProvider == null || !testProvider.isMock()) {
+                throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
+            }
+
+            ((MockLocationProvider) testProvider).setStatusLocked(status, extras, updateTime);
         }
     }
 
@@ -3494,6 +3354,7 @@
                 return;
             }
             pw.println("Current Location Manager state:");
+            pw.println("  Location Mode: " + isLocationEnabled());
             pw.println("  Location Listeners:");
             for (Receiver receiver : mReceivers.values()) {
                 pw.println("    " + receiver);
@@ -3548,12 +3409,6 @@
 
             pw.append("  ");
             mBlacklist.dump(pw);
-            if (mMockProviders.size() > 0) {
-                pw.println("  Mock Providers:");
-                for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
-                    i.getValue().dump(fd, pw, args);
-                }
-            }
 
             if (mLocationControllerExtraPackage != null) {
                 pw.println(" Location controller extra package: " + mLocationControllerExtraPackage
@@ -3574,7 +3429,7 @@
                 return;
             }
             for (LocationProvider provider : mProviders) {
-                provider.dump(fd, pw, args);
+                provider.dumpLocked(fd, pw, args);
             }
             if (mGnssBatchingInProgress) {
                 pw.println("  GNSS batching in progress");
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index b0ca2df..aed0684 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -17,13 +17,11 @@
 package com.android.server;
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.SHUTDOWN;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
@@ -40,6 +38,7 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_TETHERING;
+
 import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
@@ -53,11 +52,9 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetd;
-import android.net.TetherStatsParcel;
 import android.net.INetworkManagementEventObserver;
 import android.net.ITetheringStatsProvider;
 import android.net.InterfaceConfiguration;
@@ -69,18 +66,15 @@
 import android.net.NetworkStats;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.TetherStatsParcel;
 import android.net.UidRange;
-import android.net.UidRangeParcel;
 import android.net.util.NetdService;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkActivityListener;
 import android.os.INetworkManagementService;
-import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
@@ -91,12 +85,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
-import android.telephony.PhoneStateListener;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -110,13 +99,11 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.Preconditions;
-import com.android.server.NativeDaemonConnector.Command;
-import com.android.server.NativeDaemonConnector.SensitiveArg;
+
 import com.google.android.collect.Maps;
 
 import java.io.BufferedReader;
 import java.io.DataInputStream;
-import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -124,15 +111,11 @@
 import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.StringTokenizer;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -2153,28 +2136,6 @@
     }
 
     @Override
-    public void startClatd(String interfaceName) throws IllegalStateException {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
-        try {
-            mNetdService.clatdStart(interfaceName);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void stopClatd(String interfaceName) throws IllegalStateException {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
-        try {
-            mNetdService.clatdStop(interfaceName);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
     public void registerNetworkActivityListener(INetworkActivityListener listener) {
         mNetworkActivityListeners.register(listener);
     }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index f27d373..8adc416 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Environment;
 import android.os.Handler;
@@ -29,6 +30,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
@@ -46,8 +48,10 @@
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Monitors the health of packages on the system and notifies interested observers when packages
@@ -58,7 +62,7 @@
     // Duration to count package failures before it resets to 0
     private static final int TRIGGER_DURATION_MS = 60000;
     // Number of package failures within the duration above before we notify observers
-    private static final int TRIGGER_FAILURE_COUNT = 5;
+    static final int TRIGGER_FAILURE_COUNT = 5;
     private static final int DB_VERSION = 1;
     private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog";
     private static final String TAG_PACKAGE = "package";
@@ -75,20 +79,13 @@
     // Handler to run package cleanup runnables
     private final Handler mTimerHandler;
     private final Handler mIoHandler;
-    // Contains (observer-name -> external-observer-handle) that have been registered during the
-    // current boot.
-    // It is populated when observers call #registerHealthObserver and it does not survive reboots.
-    @GuardedBy("mLock")
-    final ArrayMap<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
-    // Contains (observer-name -> internal-observer-handle) that have ever been registered from
+    // Contains (observer-name -> observer-handle) that have ever been registered from
     // previous boots. Observers with all packages expired are periodically pruned.
     // It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
     @GuardedBy("mLock")
-    final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
+    private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
     // File containing the XML data of monitored packages /data/system/package-watchdog.xml
-    private final AtomicFile mPolicyFile =
-            new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
-                           "package-watchdog.xml"));
+    private final AtomicFile mPolicyFile;
     // Runnable to prune monitored packages that have expired
     private final Runnable mPackageCleanup;
     // Last SystemClock#uptimeMillis a package clean up was executed.
@@ -98,14 +95,32 @@
     // 0 if mPackageCleanup not running.
     private long mDurationAtLastReschedule;
 
+    // TODO(zezeozue): Remove redundant context param
     private PackageWatchdog(Context context) {
         mContext = context;
+        mPolicyFile = new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
+                        "package-watchdog.xml"));
         mTimerHandler = new Handler(Looper.myLooper());
         mIoHandler = BackgroundThread.getHandler();
         mPackageCleanup = this::rescheduleCleanup;
         loadFromFile();
     }
 
+    /**
+     * Creates a PackageWatchdog for testing that uses the same {@code looper} for all handlers
+     * and creates package-watchdog.xml in an apps data directory.
+     */
+    @VisibleForTesting
+    PackageWatchdog(Context context, Looper looper) {
+        mContext = context;
+        mPolicyFile = new AtomicFile(new File(context.getFilesDir(), "package-watchdog.xml"));
+        mTimerHandler = new Handler(looper);
+        mIoHandler = mTimerHandler;
+        mPackageCleanup = this::rescheduleCleanup;
+        loadFromFile();
+    }
+
+
     /** Creates or gets singleton instance of PackageWatchdog. */
     public static PackageWatchdog getInstance(Context context) {
         synchronized (PackageWatchdog.class) {
@@ -124,7 +139,10 @@
      */
     public void registerHealthObserver(PackageHealthObserver observer) {
         synchronized (mLock) {
-            mRegisteredObservers.put(observer.getName(), observer);
+            ObserverInternal internalObserver = mAllObservers.get(observer.getName());
+            if (internalObserver != null) {
+                internalObserver.mRegisteredObserver = observer;
+            }
             if (mDurationAtLastReschedule == 0) {
                 // Nothing running, schedule
                 rescheduleCleanup();
@@ -143,7 +161,7 @@
      * or {@code durationMs} is less than 1
      */
     public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
-            int durationMs) {
+            long durationMs) {
         if (packageNames.isEmpty() || durationMs < 1) {
             throw new IllegalArgumentException("Observation not started, no packages specified"
                     + "or invalid duration");
@@ -180,11 +198,32 @@
     public void unregisterHealthObserver(PackageHealthObserver observer) {
         synchronized (mLock) {
             mAllObservers.remove(observer.getName());
-            mRegisteredObservers.remove(observer.getName());
         }
         saveToFileAsync();
     }
 
+    /**
+     * Returns packages observed by {@code observer}
+     *
+     * @return an empty set if {@code observer} has some packages observerd from a previous boot
+     * but has not registered itself in the current boot to receive notifications. Returns null
+     * if there are no active packages monitored from any boot.
+     */
+    @Nullable
+    public Set<String> getPackages(PackageHealthObserver observer) {
+        synchronized (mLock) {
+            for (int i = 0; i < mAllObservers.size(); i++) {
+                if (observer.getName().equals(mAllObservers.keyAt(i))) {
+                    if (observer.equals(mAllObservers.valueAt(i).mRegisteredObserver)) {
+                        return mAllObservers.valueAt(i).mPackages.keySet();
+                    }
+                    return Collections.emptySet();
+                }
+            }
+        }
+        return null;
+    }
+
     // TODO(zezeozue:) Accept current versionCodes of failing packages?
     /**
      * Called when a process fails either due to a crash or ANR.
@@ -198,24 +237,23 @@
     public void onPackageFailure(String[] packages) {
         ArrayMap<String, List<PackageHealthObserver>> packagesToReport = new ArrayMap<>();
         synchronized (mLock) {
-            if (mRegisteredObservers.isEmpty()) {
+            if (mAllObservers.isEmpty()) {
                 return;
             }
 
             for (int pIndex = 0; pIndex < packages.length; pIndex++) {
+                // Observers interested in receiving packageName failures
+                List<PackageHealthObserver> observersToNotify = new ArrayList<>();
                 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
-                    // Observers interested in receiving packageName failures
-                    List<PackageHealthObserver> observersToNotify = new ArrayList<>();
-                    PackageHealthObserver activeObserver =
-                            mRegisteredObservers.get(mAllObservers.valueAt(oIndex).mName);
-                    if (activeObserver != null) {
-                        observersToNotify.add(activeObserver);
+                    PackageHealthObserver registeredObserver =
+                            mAllObservers.valueAt(oIndex).mRegisteredObserver;
+                    if (registeredObserver != null) {
+                        observersToNotify.add(registeredObserver);
                     }
-
-                    // Save interested observers and notify them outside the lock
-                    if (!observersToNotify.isEmpty()) {
-                        packagesToReport.put(packages[pIndex], observersToNotify);
-                    }
+                }
+                // Save interested observers and notify them outside the lock
+                if (!observersToNotify.isEmpty()) {
+                    packagesToReport.put(packages[pIndex], observersToNotify);
                 }
             }
         }
@@ -223,8 +261,11 @@
         // Notify observers
         for (int pIndex = 0; pIndex < packagesToReport.size(); pIndex++) {
             List<PackageHealthObserver> observers = packagesToReport.valueAt(pIndex);
+            String packageName = packages[pIndex];
             for (int oIndex = 0; oIndex < observers.size(); oIndex++) {
-                if (observers.get(oIndex).onHealthCheckFailed(packages[pIndex])) {
+                PackageHealthObserver observer = observers.get(oIndex);
+                if (mAllObservers.get(observer.getName()).onPackageFailure(packageName)
+                        && observer.onHealthCheckFailed(packageName)) {
                     // Observer has handled, do not notify others
                     break;
                 }
@@ -275,10 +316,12 @@
             // O if mPackageCleanup not running
             long elapsedDurationMs = mUptimeAtLastRescheduleMs == 0
                     ? 0 : uptimeMs - mUptimeAtLastRescheduleMs;
-            // O if mPackageCleanup not running
+            // Less than O if mPackageCleanup unexpectedly didn't run yet even though
+            // and we are past the last duration scheduled to run
             long remainingDurationMs = mDurationAtLastReschedule - elapsedDurationMs;
-
-            if (mUptimeAtLastRescheduleMs == 0 || nextDurationToScheduleMs < remainingDurationMs) {
+            if (mUptimeAtLastRescheduleMs == 0
+                    || remainingDurationMs <= 0
+                    || nextDurationToScheduleMs < remainingDurationMs) {
                 // First schedule or an earlier reschedule
                 pruneObservers(elapsedDurationMs);
                 mTimerHandler.removeCallbacks(mPackageCleanup);
@@ -305,6 +348,7 @@
             }
         }
         Slog.v(TAG, "Earliest package time is " + shortestDurationMs);
+
         return shortestDurationMs;
     }
 
@@ -409,6 +453,8 @@
     static class ObserverInternal {
         public final String mName;
         public final ArrayMap<String, MonitoredPackage> mPackages;
+        @Nullable
+        public PackageHealthObserver mRegisteredObserver;
 
         ObserverInternal(String name, List<MonitoredPackage> packages) {
             mName = name;
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index a9f190c..62da3f8 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -34,9 +34,9 @@
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsLog;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.PackageManagerService;
 
 import java.io.File;
 
@@ -179,6 +179,7 @@
     }
 
     private static void executeRescueLevelInternal(Context context, int level) throws Exception {
+        StatsLog.write(StatsLog.RESCUE_PARTY_RESET_REPORTED, level);
         switch (level) {
             case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
                 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2a80644..db2a733 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -780,6 +780,13 @@
             });
         refreshZramSettings();
 
+        // Schedule zram writeback unless zram is disabled by persist.sys.zram_enabled
+        String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
+        if (!zramPropValue.equals("0")
+                && mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_zramWriteback)) {
+            ZramWriteback.scheduleZramWriteback(mContext);
+        }
         // Toggle isolated-enable system property in response to settings
         mContext.getContentResolver().registerContentObserver(
             Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE),
@@ -813,6 +820,12 @@
             // changing the property value. There's no race: we're the
             // sole writer.
             SystemProperties.set(ZRAM_ENABLED_PROPERTY, desiredPropertyValue);
+            // Schedule writeback only if zram is being enabled.
+            if (desiredPropertyValue.equals("1")
+                    && mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_zramWriteback)) {
+                ZramWriteback.scheduleZramWriteback(mContext);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
new file mode 100644
index 0000000..16b12f1
--- /dev/null
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+    "presubmit": [
+        {
+            "name": "FrameworksMockingServicesTests",
+            "options": [
+                {
+                  "include-annotation": "android.platform.test.annotations.Presubmit"
+                },
+                {
+                  "exclude-annotation": "androidx.test.filters.FlakyTest"
+                }
+            ]
+        }
+    ]
+}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d07cf78..cbf6d04 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -34,6 +34,7 @@
 import android.os.UserHandle;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
+import android.telephony.DataFailCause;
 import android.telephony.DisconnectCause;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
@@ -47,6 +48,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.telephony.emergency.EmergencyNumber;
 import android.util.LocalLog;
 import android.util.StatsLog;
@@ -1313,16 +1315,17 @@
         }
     }
 
-    public void notifyDataConnection(int state, boolean isDataAllowed,
-            String reason, String apn, String apnType, LinkProperties linkProperties,
-            NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
+    public void notifyDataConnection(int state, boolean isDataAllowed, String apn, String apnType,
+                                     LinkProperties linkProperties,
+                                     NetworkCapabilities networkCapabilities, int networkType,
+                                     boolean roaming) {
         notifyDataConnectionForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state,
-            isDataAllowed,reason, apn, apnType, linkProperties,
-            networkCapabilities, networkType, roaming);
+                isDataAllowed, apn, apnType, linkProperties,
+                networkCapabilities, networkType, roaming);
     }
 
-    public void notifyDataConnectionForSubscriber(int subId, int state,
-            boolean isDataAllowed, String reason, String apn, String apnType,
+    public void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataAllowed,
+                                                  String apn, String apnType,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int networkType, boolean roaming) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
@@ -1331,7 +1334,6 @@
         if (VDBG) {
             log("notifyDataConnectionForSubscriber: subId=" + subId
                 + " state=" + state + " isDataAllowed=" + isDataAllowed
-                + " reason='" + reason
                 + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
                 + " mRecords.size()=" + mRecords.size());
         }
@@ -1366,7 +1368,8 @@
                     mDataConnectionNetworkType[phoneId] = networkType;
                 }
                 mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType,
-                        apnType, apn, reason, linkProperties, "");
+                        ApnSetting.getApnTypesBitmaskFromString(apnType), apn,
+                        linkProperties, DataFailCause.NONE);
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1381,30 +1384,30 @@
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionStateChanged(state, isDataAllowed, reason, apn,
-                apnType, linkProperties, networkCapabilities, roaming, subId);
-        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
-                linkProperties, "");
+        broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
+                networkCapabilities, roaming, subId);
+        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn,
+                linkProperties, DataFailCause.NONE);
     }
 
-    public void notifyDataConnectionFailed(String reason, String apnType) {
+    public void notifyDataConnectionFailed(String apnType) {
          notifyDataConnectionFailedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                 reason, apnType);
+                 apnType);
     }
 
-    public void notifyDataConnectionFailedForSubscriber(int subId,
-            String reason, String apnType) {
+    public void notifyDataConnectionFailedForSubscriber(int subId, String apnType) {
         if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
             return;
         }
         if (VDBG) {
             log("notifyDataConnectionFailedForSubscriber: subId=" + subId
-                + " reason=" + reason + " apnType=" + apnType);
+                    + " apnType=" + apnType);
         }
         synchronized (mRecords) {
             mPreciseDataConnectionState = new PreciseDataConnectionState(
                     TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                    apnType, "", reason, null, "");
+                    ApnSetting.getApnTypesBitmaskFromString(apnType), "", null,
+                    DataFailCause.NONE);
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
                         PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1417,9 +1420,10 @@
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionFailed(reason, apnType, subId);
+        broadcastDataConnectionFailed(apnType, subId);
         broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", reason, null, "");
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", null,
+                DataFailCause.NONE);
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
@@ -1529,15 +1533,15 @@
         }
     }
 
-    public void notifyPreciseDataConnectionFailed(String reason, String apnType,
-            String apn, String failCause) {
+    public void notifyPreciseDataConnectionFailed(String apnType,
+            String apn, @DataFailCause.FailCause int failCause) {
         if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
             return;
         }
         synchronized (mRecords) {
             mPreciseDataConnectionState = new PreciseDataConnectionState(
                     TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                    apnType, apn, reason, null, failCause);
+                    ApnSetting.getApnTypesBitmaskFromString(apnType), apn, null, failCause);
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
                         PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1551,7 +1555,7 @@
             handleRemoveListLocked();
         }
         broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, reason, null, failCause);
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, null, failCause);
     }
 
     @Override
@@ -1881,10 +1885,10 @@
                         android.Manifest.permission.READ_CALL_LOG});
     }
 
-    private void broadcastDataConnectionStateChanged(int state,
-            boolean isDataAllowed,
-            String reason, String apn, String apnType, LinkProperties linkProperties,
-            NetworkCapabilities networkCapabilities, boolean roaming, int subId) {
+    private void broadcastDataConnectionStateChanged(int state, boolean isDataAllowed, String apn,
+                                                     String apnType, LinkProperties linkProperties,
+                                                     NetworkCapabilities networkCapabilities,
+                                                     boolean roaming, int subId) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
@@ -1894,9 +1898,6 @@
         if (!isDataAllowed) {
             intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true);
         }
-        if (reason != null) {
-            intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason);
-        }
         if (linkProperties != null) {
             intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
             String iface = linkProperties.getInterfaceName();
@@ -1915,17 +1916,15 @@
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastDataConnectionFailed(String reason, String apnType,
-            int subId) {
+    private void broadcastDataConnectionFailed(String apnType, int subId) {
         Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-        intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
-            int backgroundCallState) {
+                                                  int backgroundCallState) {
         Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
         intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
         intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
@@ -1935,18 +1934,17 @@
     }
 
     private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
-            String apnType, String apn, String reason, LinkProperties linkProperties,
-            String failCause) {
+            String apnType, String apn, LinkProperties linkProperties,
+            @DataFailCause.FailCause int failCause) {
         Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
         intent.putExtra(PhoneConstants.STATE_KEY, state);
         intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
-        if (reason != null) intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason);
         if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
         if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
         if (linkProperties != null) {
-            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY,linkProperties);
+            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
         }
-        if (failCause != null) intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
+        intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
 
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                 android.Manifest.permission.READ_PRECISE_PHONE_STATE);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 9f353a8..ea6d435 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -141,6 +141,7 @@
     private boolean mLowPowerMode;
     private int mHapticFeedbackIntensity;
     private int mNotificationIntensity;
+    private int mRingIntensity;
 
     native static boolean vibratorExists();
     native static void vibratorInit();
@@ -149,6 +150,8 @@
     native static boolean vibratorSupportsAmplitudeControl();
     native static void vibratorSetAmplitude(int amplitude);
     native static long vibratorPerformEffect(long effect, long strength);
+    static native boolean vibratorSupportsExternalControl();
+    static native void vibratorSetExternalControl(boolean enabled);
 
     private final IUidObserver mUidObserver = new IUidObserver.Stub() {
         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -426,6 +429,10 @@
                     Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
                     true, mSettingObserver, UserHandle.USER_ALL);
 
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY),
+                    true, mSettingObserver, UserHandle.USER_ALL);
+
             mContext.registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
@@ -532,12 +539,6 @@
                 return;
             }
             verifyIncomingUid(uid);
-            if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
-                    > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
-                Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
-                        + uid + " is background");
-                return;
-            }
             if (!verifyVibrationEffect(effect)) {
                 return;
             }
@@ -575,6 +576,13 @@
                 }
 
                 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
+                if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
+                        > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+                        && vib.isHapticFeedback()) {
+                    Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
+                            + uid + " is background");
+                    return;
+                }
                 linkVibration(vib);
                 long ident = Binder.clearCallingIdentity();
                 try {
@@ -745,7 +753,9 @@
     }
 
     private int getCurrentIntensityLocked(Vibration vib) {
-        if (vib.isNotification() || vib.isRingtone()){
+        if (vib.isRingtone()) {
+            return mRingIntensity;
+        } else if (vib.isNotification()) {
             return mNotificationIntensity;
         } else if (vib.isHapticFeedback()) {
             return mHapticFeedbackIntensity;
@@ -767,7 +777,9 @@
         }
 
         final int defaultIntensity;
-        if (vib.isNotification() || vib.isRingtone()) {
+        if (vib.isRingtone()) {
+            defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
+        } else if (vib.isNotification()) {
             defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
         } else if (vib.isHapticFeedback()) {
             defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
@@ -930,6 +942,9 @@
         mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
                 Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
+        mRingIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.RING_VIBRATION_INTENSITY,
+                mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
     }
 
     @Override
@@ -1278,6 +1293,7 @@
             pw.println("  mLowPowerMode=" + mLowPowerMode);
             pw.println("  mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
             pw.println("  mNotificationIntensity=" + mNotificationIntensity);
+            pw.println("  mRingIntensity=" + mRingIntensity);
             pw.println("");
             pw.println("  Previous vibrations:");
             for (VibrationInfo info : mPreviousVibrations) {
diff --git a/services/core/java/com/android/server/ZramWriteback.java b/services/core/java/com/android/server/ZramWriteback.java
new file mode 100644
index 0000000..3a4aff2
--- /dev/null
+++ b/services/core/java/com/android/server/ZramWriteback.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Schedules jobs for triggering zram writeback.
+ */
+public final class ZramWriteback extends JobService {
+    private static final String TAG = "ZramWriteback";
+    private static final boolean DEBUG = false;
+
+    private static final ComponentName sZramWriteback =
+            new ComponentName("android", ZramWriteback.class.getName());
+
+    private static final int MARK_IDLE_JOB_ID = 811;
+    private static final int WRITEBACK_IDLE_JOB_ID = 812;
+
+    private static final int MAX_ZRAM_DEVICES = 256;
+    private static int sZramDeviceId = 0;
+
+    private static final String IDLE_SYS = "/sys/block/zram%d/idle";
+    private static final String IDLE_SYS_ALL_PAGES = "all";
+
+    private static final String WB_SYS = "/sys/block/zram%d/writeback";
+    private static final String WB_SYS_IDLE_PAGES = "idle";
+
+    private static final String WB_STATS_SYS = "/sys/block/zram%d/bd_stat";
+    private static final int WB_STATS_MAX_FILE_SIZE = 128;
+
+    private static final String BDEV_SYS = "/sys/block/zram%d/backing_dev";
+
+    private static final String MARK_IDLE_DELAY_PROP = "ro.zram.mark_idle_delay_mins";
+    private static final String FIRST_WB_DELAY_PROP = "ro.zram.first_wb_delay_mins";
+    private static final String PERIODIC_WB_DELAY_PROP = "ro.zram.periodic_wb_delay_hours";
+
+    private void markPagesAsIdle() {
+        String idlePath = String.format(IDLE_SYS, sZramDeviceId);
+        try {
+            FileUtils.stringToFile(new File(idlePath), IDLE_SYS_ALL_PAGES);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write to " + idlePath);
+        }
+    }
+
+    private void flushIdlePages() {
+        if (DEBUG) Slog.d(TAG, "Start writing back idle pages to disk");
+        String wbPath = String.format(WB_SYS, sZramDeviceId);
+        try {
+            FileUtils.stringToFile(new File(wbPath), WB_SYS_IDLE_PAGES);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write to " + wbPath);
+        }
+        if (DEBUG) Slog.d(TAG, "Finished writeback back idle pages");
+    }
+
+    private int getWrittenPageCount() {
+        String wbStatsPath = String.format(WB_STATS_SYS, sZramDeviceId);
+        try {
+            String wbStats = FileUtils
+                    .readTextFile(new File(wbStatsPath), WB_STATS_MAX_FILE_SIZE, "");
+            return Integer.parseInt(wbStats.trim().split("\\s+")[2], 10);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read writeback stats from " + wbStatsPath);
+        }
+
+        return -1;
+    }
+
+    private void markAndFlushPages() {
+        int pageCount = getWrittenPageCount();
+
+        flushIdlePages();
+        markPagesAsIdle();
+
+        if (pageCount != -1) {
+            Slog.i(TAG, "Total pages written to disk is " + (getWrittenPageCount() - pageCount));
+        }
+    }
+
+    private static boolean isWritebackEnabled() {
+        try {
+            String backingDev = FileUtils
+                    .readTextFile(new File(String.format(BDEV_SYS, sZramDeviceId)), 128, "");
+            if (!"none".equals(backingDev.trim())) {
+                return true;
+            } else {
+                Slog.w(TAG, "Writeback device is not set");
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Writeback is not enabled on zram");
+        }
+        return false;
+    }
+
+    private static void schedNextWriteback(Context context) {
+        int nextWbDelay = SystemProperties.getInt(PERIODIC_WB_DELAY_PROP, 24);
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
+                        .setMinimumLatency(TimeUnit.HOURS.toMillis(nextWbDelay))
+                        .setRequiresDeviceIdle(true)
+                        .build());
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+
+        if (!isWritebackEnabled()) {
+            jobFinished(params, false);
+            return false;
+        }
+
+        if (params.getJobId() == MARK_IDLE_JOB_ID) {
+            markPagesAsIdle();
+            jobFinished(params, false);
+            return false;
+        } else {
+            new Thread("ZramWriteback_WritebackIdlePages") {
+                @Override
+                public void run() {
+                    markAndFlushPages();
+                    schedNextWriteback(ZramWriteback.this);
+                    jobFinished(params, false);
+                }
+            }.start();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        // The thread that triggers the writeback is non-interruptible
+        return false;
+    }
+
+    /**
+     * Schedule the zram writeback job to trigger a writeback when idle
+     */
+    public static void scheduleZramWriteback(Context context) {
+        int markIdleDelay = SystemProperties.getInt(MARK_IDLE_DELAY_PROP, 20);
+        int firstWbDelay = SystemProperties.getInt(FIRST_WB_DELAY_PROP, 180);
+
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        // Schedule a one time job to mark pages as idle. These pages will be written
+        // back at later point if they remain untouched.
+        js.schedule(new JobInfo.Builder(MARK_IDLE_JOB_ID, sZramWriteback)
+                        .setMinimumLatency(TimeUnit.MINUTES.toMillis(markIdleDelay))
+                        .build());
+
+        // Schedule a one time job to flush idle pages to disk.
+        // After the initial writeback, subsequent writebacks are done at interval set
+        // by ro.zram.periodic_wb_delay_hours.
+        js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
+                        .setMinimumLatency(TimeUnit.MINUTES.toMillis(firstWbDelay))
+                        .setRequiresDeviceIdle(true)
+                        .build());
+    }
+}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index e5ab8fc..c316915 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -31,6 +31,7 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.service.adb.AdbServiceDumpProto;
+import android.sysprop.AdbProperties;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -185,7 +186,7 @@
         mContext = context;
         mContentResolver = context.getContentResolver();
 
-        boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
+        boolean secureAdbEnabled = AdbProperties.secure().orElse(false);
         boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
         if (secureAdbEnabled && !dataEncrypted) {
             mDebuggingManager = new AdbDebuggingManager(context);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a94fa12..a96676e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -130,6 +130,9 @@
     // calling startForeground() before we ANR + stop it.
     static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
 
+    // For how long after a whitelisted service's start its process can start a background activity
+    private static final int SERVICE_BG_ACTIVITY_START_TIMEOUT_MS = 10*1000;
+
     final ActivityManagerService mAm;
 
     // Maximum number of services that we allow to start in the background
@@ -398,6 +401,14 @@
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
             int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
             throws TransactionTooLargeException {
+        return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
+                callingPackage, userId, false);
+    }
+
+    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
+            int callingPid, int callingUid, boolean fgRequired, String callingPackage,
+            final int userId, boolean allowBackgroundActivityStarts)
+            throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
 
@@ -505,7 +516,7 @@
                 }
                 // This app knows it is in the new model where this operation is not
                 // allowed, so tell it what has happened.
-                UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
+                UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
                 return new ComponentName("?", "app is in background uid " + uidRec);
             }
         }
@@ -622,10 +633,28 @@
             }
         }
 
+        if (allowBackgroundActivityStarts) {
+            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
+            if (proc != null) {
+                proc.addAllowBackgroundActivityStartsToken(r);
+                // schedule removal of the whitelisting token after the timeout
+                removeAllowBackgroundActivityStartsServiceToken(proc, r,
+                        SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
+            }
+        }
         ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
         return cmp;
     }
 
+    private void removeAllowBackgroundActivityStartsServiceToken(ProcessRecord proc,
+            ServiceRecord r, int delayMillis) {
+        mAm.mHandler.postDelayed(() -> {
+            if (proc != null) {
+                proc.removeAllowBackgroundActivityStartsToken(r);
+            }
+        }, delayMillis);
+    }
+
     private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
             String callingPackage, int callingUid, Intent service, boolean callerFg,
             final int userId) {
@@ -710,7 +739,7 @@
 
     private void stopServiceLocked(ServiceRecord service) {
         if (service.delayed) {
-            // If service isn't actually running, but is is being held in the
+            // If service isn't actually running, but is being held in the
             // delayed list, then we need to keep it started but note that it
             // should be stopped once no longer delayed.
             if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Delaying stop of pending: " + service);
@@ -752,6 +781,9 @@
             if (r.record != null) {
                 final long origId = Binder.clearCallingIdentity();
                 try {
+                    // immediately remove bg activity whitelisting token if there was one
+                    removeAllowBackgroundActivityStartsServiceToken(callerApp, r.record,
+                            0 /* delayMillis */);
                     stopServiceLocked(r.record);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
@@ -1654,6 +1686,10 @@
                 }
             }
 
+            if ((flags & Context.BIND_RESTRICT_ASSOCIATIONS) != 0) {
+                mAm.requireAllowedAssociationsLocked(s.appInfo.packageName);
+            }
+
             mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
                     callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
                     s.instanceName, s.processName);
@@ -2503,6 +2539,9 @@
                     && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                 hostingType = "webview_service";
             }
+            if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
+                hostingType = "app_zygote";
+            }
         }
 
         // Not running -- get it started, and enqueue this service record
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 1c04a94..dd2b33a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -70,6 +70,12 @@
     static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
     static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
     static final String KEY_USE_COMPACTION = "use_compaction";
+    static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
+    static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
+    static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
+    static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
+    static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
+    static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -101,6 +107,12 @@
     private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
     private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
     private static final boolean DEFAULT_USE_COMPACTION = false;
+    public static final int DEFAULT_COMPACT_ACTION_1 = 1;
+    public static final int DEFAULT_COMPACT_ACTION_2 = 3;
+    public static final long DEFAULT_COMPACT_THROTTLE_1 = 5000;
+    public static final long DEFAULT_COMPACT_THROTTLE_2 = 10000;
+    public static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
+    public static final long DEFAULT_COMPACT_THROTTLE_4 = 10000;
 
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -223,6 +235,20 @@
     // Use compaction for background apps.
     public boolean USE_COMPACTION = DEFAULT_USE_COMPACTION;
 
+    // Action for compactAppSome.
+    public int COMPACT_ACTION_1 = DEFAULT_COMPACT_ACTION_1;
+    // Action for compactAppFull;
+    public int COMPACT_ACTION_2 = DEFAULT_COMPACT_ACTION_2;
+
+    // How long we'll skip second compactAppSome after first compactAppSome
+    public long COMPACT_THROTTLE_1 = DEFAULT_COMPACT_THROTTLE_1;
+    // How long we'll skip compactAppSome after compactAppFull
+    public long COMPACT_THROTTLE_2 = DEFAULT_COMPACT_THROTTLE_2;
+    // How long we'll skip compactAppFull after compactAppSome
+    public long COMPACT_THROTTLE_3 = DEFAULT_COMPACT_THROTTLE_3;
+    // How long we'll skip second compactAppFull after first compactAppFull
+    public long COMPACT_THROTTLE_4 = DEFAULT_COMPACT_THROTTLE_4;
+
     // Indicates whether the activity starts logging is enabled.
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -381,6 +407,12 @@
             TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
                     DEFAULT_TOP_TO_FGS_GRACE_DURATION);
             USE_COMPACTION = mParser.getBoolean(KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
+            COMPACT_ACTION_1 = mParser.getInt(KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
+            COMPACT_ACTION_2 = mParser.getInt(KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
+            COMPACT_THROTTLE_1 = mParser.getLong(KEY_COMPACT_THROTTLE_1, DEFAULT_COMPACT_THROTTLE_1);
+            COMPACT_THROTTLE_2 = mParser.getLong(KEY_COMPACT_THROTTLE_2, DEFAULT_COMPACT_THROTTLE_2);
+            COMPACT_THROTTLE_3 = mParser.getLong(KEY_COMPACT_THROTTLE_3, DEFAULT_COMPACT_THROTTLE_3);
+            COMPACT_THROTTLE_4 = mParser.getLong(KEY_COMPACT_THROTTLE_4, DEFAULT_COMPACT_THROTTLE_4);
 
             updateMaxCachedProcesses();
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b053979..089847d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,8 +24,6 @@
 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_CACHED_EMPTY;
 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;
@@ -34,6 +32,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 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_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -50,6 +49,7 @@
 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.NETWORK_STACK_UID;
 import static android.os.Process.NFC_UID;
 import static android.os.Process.PHONE_UID;
 import static android.os.Process.PROC_CHAR;
@@ -58,16 +58,11 @@
 import static android.os.Process.PROC_SPACE_TERM;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SCHED_FIFO;
-import static android.os.Process.SCHED_OTHER;
 import static android.os.Process.SCHED_RESET_ON_FORK;
 import static android.os.Process.SE_UID;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SIGNAL_USR1;
 import static android.os.Process.SYSTEM_UID;
-import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
-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_FOREGROUND;
 import static android.os.Process.getTotalMemory;
 import static android.os.Process.isThreadInProcess;
@@ -78,7 +73,6 @@
 import static android.os.Process.readProcFile;
 import static android.os.Process.removeAllProcessGroups;
 import static android.os.Process.sendSignal;
-import static android.os.Process.setProcessGroup;
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
 import static android.os.Process.zygoteProcess;
@@ -95,11 +89,9 @@
 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_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ_REASON;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -108,7 +100,6 @@
 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_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;
@@ -235,6 +226,7 @@
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
+import android.os.AppZygote;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.BinderProxy;
@@ -327,7 +319,6 @@
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.AlarmManagerInternal;
-import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleController;
 import com.android.server.DisplayThread;
@@ -346,6 +337,7 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
@@ -402,7 +394,7 @@
     public static final int TOP_APP_PRIORITY_BOOST = -10;
 
     static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
-    private static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
+    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;
@@ -410,9 +402,9 @@
     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;
+    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;
+    static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
     static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
     private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
     static final String TAG_PSS = TAG + POSTFIX_PSS;
@@ -452,6 +444,9 @@
     // before we decide it must be hung.
     static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
 
+    // How long we wait to kill an application zygote, after the last process using
+    // it has gone away.
+    static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
     /**
      * How long we wait for an provider to be published. Should be longer than
      * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
@@ -536,6 +531,8 @@
 
     private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
 
+    final OomAdjuster mOomAdjuster;
+
     /** All system services */
     SystemServiceManager mSystemServiceManager;
 
@@ -550,7 +547,7 @@
     public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler();
 
     // Whether we should use SCHED_FIFO for UI and RenderThreads.
-    private boolean mUseFifoUiScheduling = false;
+    boolean mUseFifoUiScheduling = false;
 
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
@@ -649,11 +646,6 @@
     final ProcessStatsService mProcessStats;
 
     /**
-     * Service for compacting background apps.
-     */
-    final AppCompactor mAppCompact;
-
-    /**
      * Non-persistent appId whitelist for background restrictions
      */
     int[] mBackgroundAppIdWhitelist = new int[] {
@@ -667,9 +659,47 @@
 
     /**
      * When an app has restrictions on the other apps that can have associations with it,
-     * it appears here with a set of the allowed apps.
+     * it appears here with a set of the allowed apps and also track debuggability of the app.
      */
-    ArrayMap<String, ArraySet<String>> mAllowedAssociations;
+    ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
+
+    /**
+     * Tracks association information for a particular package along with debuggability.
+     * <p> Associations for a package A are allowed to package B if B is part of the
+     *     allowed associations for A or if A is debuggable.
+     */
+    private final class PackageAssociationInfo {
+        private final String mSourcePackage;
+        private final ArraySet<String> mAllowedPackageAssociations;
+        private boolean mIsDebuggable;
+
+        PackageAssociationInfo(String sourcePackage, ArraySet<String> allowedPackages,
+                boolean isDebuggable) {
+            mSourcePackage = sourcePackage;
+            mAllowedPackageAssociations = allowedPackages;
+            mIsDebuggable = isDebuggable;
+        }
+
+        /**
+         * Returns true if {@code mSourcePackage} is allowed association with
+         * {@code targetPackage}.
+         */
+        boolean isPackageAssociationAllowed(String targetPackage) {
+            return mIsDebuggable || mAllowedPackageAssociations.contains(targetPackage);
+        }
+
+        boolean isDebuggable() {
+            return mIsDebuggable;
+        }
+
+        void setDebuggable(boolean isDebuggable) {
+            mIsDebuggable = isDebuggable;
+        }
+
+        ArraySet<String> getAllowedPackageAssociations() {
+            return mAllowedPackageAssociations;
+        }
+    }
 
     /**
      * All of the processes we currently have running organized by pid.
@@ -816,8 +846,6 @@
      */
     boolean mFullPssPending = false;
 
-    /** Track all uids that have actively running processes. */
-    final ActiveUids mActiveUids;
 
     /**
      * This is for verifying the UID report flow.
@@ -931,8 +959,8 @@
     /**
      * Backup/restore process management
      */
-    String mBackupAppName = null;
-    BackupRecord mBackupTarget = null;
+    @GuardedBy("this")
+    final SparseArray<BackupRecord> mBackupTargets = new SparseArray<>();
 
     final ProviderMap mProviderMap;
 
@@ -1099,32 +1127,7 @@
     /**
      * State of external calls telling us if the device is awake or asleep.
      */
-    private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
-
-    /**
-     * Current sequence id for oom_adj computation traversal.
-     */
-    int mAdjSeq = 0;
-
-    /**
-     * Keep track of the non-cached/empty process we last found, to help
-     * determine how to distribute cached/empty processes next time.
-     */
-    int mNumNonCachedProcs = 0;
-
-    /**
-     * Keep track of the number of cached hidden procs, to balance oom adj
-     * distribution between those and empty procs.
-     */
-    int mNumCachedHiddenProcs = 0;
-
-    /**
-     * Keep track of the number of service processes we last found, to
-     * determine on the next iteration which should be B services.
-     */
-    int mNumServiceProcs = 0;
-    int mNewNumAServiceProcs = 0;
-    int mNewNumServiceProcs = 0;
+    int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
 
     /**
      * Allow the current computed overall memory level of the system to go down?
@@ -1251,10 +1254,6 @@
     String mTrackAllocationApp = null;
     String mNativeDebuggingApp = null;
 
-    final long[] mTmpLong = new long[3];
-
-    private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
-
     private final Injector mInjector;
 
     static final class ProcessChangeItem {
@@ -1329,6 +1328,7 @@
         }
     }
 
+    // TODO: Move below 4 members and code to ProcessList
     final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
     ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
 
@@ -1441,6 +1441,7 @@
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+    static final int KILL_APP_ZYGOTE_MSG = 71;
 
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
 
@@ -1644,6 +1645,12 @@
                             false, userId, reason);
                 }
             } break;
+                case KILL_APP_ZYGOTE_MSG: {
+                    synchronized (ActivityManagerService.this) {
+                        final AppZygote appZygote = (AppZygote) msg.obj;
+                        mProcessList.killAppZygoteIfNeededLocked(appZygote);
+                    }
+                } break;
             case CHECK_EXCESSIVE_POWER_USE_MSG: {
                 synchronized (ActivityManagerService.this) {
                     checkExcessivePowerUsageLocked();
@@ -1932,7 +1939,8 @@
             synchronized (this) {
                 ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
                         false,
-                        0);
+                        0,
+                        false);
                 app.setPersistent(true);
                 app.pid = MY_PID;
                 app.getWindowProcessController().setPid(MY_PID);
@@ -2212,12 +2220,15 @@
         mUiContext = null;
         mAppErrors = null;
         mPackageWatchdog = null;
-        mActiveUids = new ActiveUids(this, false /* postChangesToAtm */);
         mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
         mBatteryStatsService = null;
         mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
         mHandlerThread = handlerThread;
         mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+        final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
+        mProcessList.init(this, activeUids);
+        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
+
         mIntentFirewall = hasHandlerThread
                 ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
         mProcessCpuThread = null;
@@ -2233,7 +2244,6 @@
                 ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
         mProcStartHandlerThread = null;
         mProcStartHandler = null;
-        mAppCompact = null;
         mHiddenApiBlacklist = null;
         mFactoryTest = FACTORY_TEST_OFF;
     }
@@ -2263,8 +2273,9 @@
         mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
 
         mConstants = new ActivityManagerConstants(this, mHandler);
-
-        mProcessList.init(this);
+        final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
+        mProcessList.init(this, activeUids);
+        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
 
         mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "foreground", BROADCAST_FG_TIMEOUT, false);
@@ -2280,7 +2291,6 @@
         mProviderMap = new ProviderMap(this);
         mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
         mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
-        mActiveUids = new ActiveUids(this, true /* postChangesToAtm */);
 
         final File systemDir = SystemServiceManager.ensureSystemDir();
 
@@ -2317,8 +2327,6 @@
                 DisplayThread.get().getLooper());
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
 
-        mAppCompact = new AppCompactor(this);
-
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
             public void run() {
@@ -2365,7 +2373,8 @@
         try {
             Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
                     Process.THREAD_GROUP_SYSTEM);
-            Process.setThreadGroupAndCpuset(mAppCompact.mCompactionThread.getThreadId(),
+            Process.setThreadGroupAndCpuset(
+                    mOomAdjuster.mAppCompact.mCompactionThread.getThreadId(),
                     Process.THREAD_GROUP_SYSTEM);
         } catch (Exception e) {
             Slog.w(TAG, "Setting background thread cpuset failed");
@@ -2417,32 +2426,82 @@
         return mBackgroundLaunchBroadcasts;
     }
 
+    /**
+     * Ensures that the given package name has an explicit set of allowed associations.
+     * If it does not, give it an empty set.
+     */
+    void requireAllowedAssociationsLocked(String packageName) {
+        ensureAllowedAssociations();
+        if (mAllowedAssociations.get(packageName) == null) {
+            mAllowedAssociations.put(packageName, new PackageAssociationInfo(packageName,
+                    new ArraySet<>(), /* isDebuggable = */ false));
+        }
+    }
+
+    /**
+     * Returns true if the package {@code pkg1} running under user handle {@code uid1} is
+     * allowed association with the package {@code pkg2} running under user handle {@code uid2}.
+     * <p> If either of the packages are running as  part of the core system, then the
+     * association is implicitly allowed.
+     */
     boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
-        if (mAllowedAssociations == null) {
-            mAllowedAssociations = SystemConfig.getInstance().getAllowedAssociations();
-        }
+        ensureAllowedAssociations();
         // Interactions with the system uid are always allowed, since that is the core system
-        // that everyone needs to be able to interact with.
-        if (UserHandle.getAppId(uid1) == SYSTEM_UID) {
+        // that everyone needs to be able to interact with. Also allow reflexive associations
+        // within the same uid.
+        if (uid1 == uid2 || UserHandle.getAppId(uid1) == SYSTEM_UID
+                || UserHandle.getAppId(uid2) == SYSTEM_UID) {
             return true;
         }
-        if (UserHandle.getAppId(uid2) == SYSTEM_UID) {
-            return true;
+
+        // Check for association on both source and target packages.
+        PackageAssociationInfo pai = mAllowedAssociations.get(pkg1);
+        if (pai != null && !pai.isPackageAssociationAllowed(pkg2)) {
+            return false;
         }
-        // We won't allow this association if either pkg1 or pkg2 has a limit on the
-        // associations that are allowed with it, and the other package is not explicitly
-        // specified as one of those associations.
-        ArraySet<String> pkgs = mAllowedAssociations.get(pkg1);
-        if (pkgs != null) {
-            if (!pkgs.contains(pkg2)) {
-                return false;
+        pai = mAllowedAssociations.get(pkg2);
+        if (pai != null && !pai.isPackageAssociationAllowed(pkg1)) {
+            return false;
+        }
+        // If no explicit associations are provided in the manifest, then assume the app is
+        // allowed associations with any package.
+        return true;
+    }
+
+    /** Sets up allowed associations for system prebuilt packages from system config (if needed). */
+    private void ensureAllowedAssociations() {
+        if (mAllowedAssociations == null) {
+            ArrayMap<String, ArraySet<String>> allowedAssociations =
+                    SystemConfig.getInstance().getAllowedAssociations();
+            mAllowedAssociations = new ArrayMap<>(allowedAssociations.size());
+            PackageManagerInternal pm = getPackageManagerInternalLocked();
+            for (int i = 0; i < allowedAssociations.size(); i++) {
+                final String pkg = allowedAssociations.keyAt(i);
+                final ArraySet<String> asc = allowedAssociations.valueAt(i);
+
+                // Query latest debuggable flag from package-manager.
+                boolean isDebuggable = false;
+                try {
+                    ApplicationInfo ai = AppGlobals.getPackageManager()
+                            .getApplicationInfo(pkg, MATCH_ALL, 0);
+                    if (ai != null) {
+                        isDebuggable = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+                    }
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                mAllowedAssociations.put(pkg, new PackageAssociationInfo(pkg, asc, isDebuggable));
             }
         }
-        pkgs = mAllowedAssociations.get(pkg2);
-        if (pkgs != null) {
-            return pkgs.contains(pkg1);
+    }
+
+    /** Updates allowed associations for app info (specifically, based on debuggability).  */
+    private void updateAssociationForApp(ApplicationInfo appInfo) {
+        ensureAllowedAssociations();
+        PackageAssociationInfo pai = mAllowedAssociations.get(appInfo.packageName);
+        if (pai != null) {
+            pai.setDebuggable((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
         }
-        return true;
     }
 
     @Override
@@ -4373,6 +4432,7 @@
         mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
     }
 
+    @GuardedBy("this")
     private final void processStartTimedOutLocked(ProcessRecord app) {
         final int pid = app.pid;
         boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
@@ -4393,7 +4453,8 @@
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
             }
             removeLruProcessLocked(app);
-            if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
+            final BackupRecord backupTarget = mBackupTargets.get(app.userId);
+            if (backupTarget != null && backupTarget.app.pid == pid) {
                 Slog.w(TAG, "Unattached app died before backup, skipping");
                 mHandler.post(new Runnable() {
                 @Override
@@ -4401,7 +4462,7 @@
                         try {
                             IBackupManager bm = IBackupManager.Stub.asInterface(
                                     ServiceManager.getService(Context.BACKUP_SERVICE));
-                            bm.agentDisconnected(app.info.packageName);
+                            bm.agentDisconnectedForUser(app.userId, app.info.packageName);
                         } catch (RemoteException e) {
                             // Can't happen; the backup manager is local
                         }
@@ -4524,6 +4585,7 @@
         if (DEBUG_ALL) Slog.v(
             TAG, "New app record " + app
             + " thread=" + thread.asBinder() + " pid=" + pid);
+        final BackupRecord backupTarget = mBackupTargets.get(app.userId);
         try {
             int testMode = ApplicationThreadConstants.DEBUG_OFF;
             if (mDebugApp != null && mDebugApp.equals(processName)) {
@@ -4545,11 +4607,11 @@
 
             // If the app is being launched for restore or full backup, set it up specially
             boolean isRestrictedBackupMode = false;
-            if (mBackupTarget != null && mBackupAppName.equals(processName)) {
-                isRestrictedBackupMode = mBackupTarget.appInfo.uid >= FIRST_APPLICATION_UID
-                        && ((mBackupTarget.backupMode == BackupRecord.RESTORE)
-                                || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
-                                || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
+            if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
+                isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
+                        && ((backupTarget.backupMode == BackupRecord.RESTORE)
+                                || (backupTarget.backupMode == BackupRecord.RESTORE_FULL)
+                                || (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
             }
 
             final ActiveInstrumentation instr = app.getActiveInstrumentation();
@@ -4757,15 +4819,15 @@
         }
 
         // Check whether the next backup agent is in this process...
-        if (!badApp && mBackupTarget != null && mBackupTarget.app == app) {
+        if (!badApp && backupTarget != null && backupTarget.app == app) {
             if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
                     "New app is backup target, launching agent for " + app);
-            notifyPackageUse(mBackupTarget.appInfo.packageName,
+            notifyPackageUse(backupTarget.appInfo.packageName,
                              PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
             try {
-                thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
-                        compatibilityInfoForPackage(mBackupTarget.appInfo),
-                        mBackupTarget.backupMode);
+                thread.scheduleCreateBackupAgent(backupTarget.appInfo,
+                        compatibilityInfoForPackage(backupTarget.appInfo),
+                        backupTarget.backupMode);
             } catch (Exception e) {
                 Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
                 badApp = true;
@@ -5310,18 +5372,9 @@
         }
     }
 
-    @Override
-    public boolean isAppForeground(int uid) {
-        int callerUid = Binder.getCallingUid();
-        if (UserHandle.isCore(callerUid) || callerUid == uid) {
-            return isAppForegroundInternal(uid);
-        }
-        return false;
-    }
-
-    private boolean isAppForegroundInternal(int uid) {
+    private boolean isAppForeground(int uid) {
         synchronized (this) {
-            UidRecord uidRec = mActiveUids.get(uid);
+            UidRecord uidRec = mProcessList.mActiveUids.get(uid);
             if (uidRec == null || uidRec.idle) {
                 return false;
             }
@@ -5333,15 +5386,10 @@
     // be guarded by permission checking.
     int getUidState(int uid) {
         synchronized (this) {
-            return getUidStateLocked(uid);
+            return mProcessList.getUidProcStateLocked(uid);
         }
     }
 
-    int getUidStateLocked(int uid) {
-        UidRecord uidRec = mActiveUids.get(uid);
-        return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
-    }
-
     // =========================================================
     // PROCESS INFO
     // =========================================================
@@ -5633,7 +5681,7 @@
 
     int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
             int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
-        UidRecord uidRec = mActiveUids.get(uid);
+        UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
         if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
                 + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
                 + (uidRec != null ? uidRec.idle : false));
@@ -7396,7 +7444,7 @@
         }
 
         if (app == null) {
-            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0);
+            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0, false);
             mProcessList.updateLruProcessLocked(app, false, null);
             updateOomAdjLocked();
         }
@@ -7854,8 +7902,7 @@
         }
 
         synchronized (this) {
-            UidRecord uidRec = mActiveUids.get(uid);
-            return uidRec != null ? uidRec.getCurProcState() : PROCESS_STATE_NONEXISTENT;
+            return mProcessList.getUidProcStateLocked(uid);
         }
     }
 
@@ -7891,7 +7938,7 @@
     }
 
     boolean isUidActiveLocked(int uid) {
-        final UidRecord uidRecord = mActiveUids.get(uid);
+        final UidRecord uidRecord = mProcessList.getUidRecordLocked(uid);
         return uidRecord != null && !uidRecord.setIdle;
     }
 
@@ -8683,7 +8730,7 @@
 
                 if (mForceBackgroundCheck) {
                     // Stop background services for idle UIDs.
-                    doStopUidForIdleUidsLocked();
+                    mProcessList.doStopUidForIdleUidsLocked();
                 }
             }
         }
@@ -9532,7 +9579,8 @@
                     proto.end(broadcastToken);
 
                     long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
-                    mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
+                    mServices.writeToProto(proto,
+                            ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
                     proto.end(serviceToken);
 
                     long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
@@ -10084,11 +10132,13 @@
             }
         }
 
-        if (mActiveUids.size() > 0) {
-            if (dumpUids(pw, dumpPackage, dumpAppId, mActiveUids, "UID states:", needSep)) {
+        if (mProcessList.mActiveUids.size() > 0) {
+            if (dumpUids(pw, dumpPackage, dumpAppId, mProcessList.mActiveUids,
+                    "UID states:", needSep)) {
                 needSep = true;
             }
         }
+
         if (dumpAll) {
             if (mValidateUids.size() > 0) {
                 if (dumpUids(pw, dumpPackage, dumpAppId, mValidateUids, "UID validation:",
@@ -10351,12 +10401,8 @@
                 pw.print("  mLastPowerCheckUptime=");
                         TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
                         pw.println("");
-                pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
-                pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
-                        + " (" + mProcessList.getLruSizeLocked() + " total)"
-                        + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
-                        + " mNumServiceProcs=" + mNumServiceProcs
-                        + " mNewNumServiceProcs=" + mNewNumServiceProcs);
+                mOomAdjuster.dumpSequenceNumbersLocked(pw);
+                mOomAdjuster.dumpProcCountsLocked(pw);
                 pw.println("  mAllowLowerMemLevel=" + mAllowLowerMemLevel
                         + " mLastMemoryLevel=" + mLastMemoryLevel
                         + " mLastNumProcesses=" + mLastNumProcesses);
@@ -10408,7 +10454,8 @@
                 if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                     continue;
                 }
-                r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS, mProcessList.mLruProcesses.indexOf(r)
+                r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS,
+                        mProcessList.mLruProcesses.indexOf(r)
                 );
                 if (r.isPersistent()) {
                     numPers++;
@@ -10432,19 +10479,20 @@
                     && !ai.mTargetInfo.packageName.equals(dumpPackage)) {
                 continue;
             }
-            ai.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
+            ai.writeToProto(proto,
+                    ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
         }
 
         int whichAppId = getAppId(dumpPackage);
-        for (int i=0; i<mActiveUids.size(); i++) {
-            UidRecord uidRec = mActiveUids.valueAt(i);
+        for (int i = 0; i < mProcessList.mActiveUids.size(); i++) {
+            UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
             if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
                 continue;
             }
             uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
         }
 
-        for (int i=0; i<mValidateUids.size(); i++) {
+        for (int i = 0; i < mValidateUids.size(); i++) {
             UidRecord uidRec = mValidateUids.valueAt(i);
             if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
                 continue;
@@ -10519,12 +10567,15 @@
             r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ON_HOLD_PROCS);
         }
 
-        writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
-        mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
+        writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS,
+                dumpPackage);
+        mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
+                dumpPackage);
         mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, mTestPssMode);
 
         if (dumpPackage == null) {
-            mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
+            mUserController.writeToProto(proto,
+            ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
         }
 
         final int NI = mUidObservers.getRegisteredCallbackCount();
@@ -10636,11 +10687,7 @@
             proto.write(ActivityManagerServiceDumpProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
             proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
-            proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
-            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);
+            mOomAdjuster.dumpProcessListVariablesLocked(proto);
             proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
@@ -10648,7 +10695,6 @@
             ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
             proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
         }
-
     }
 
     void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
@@ -10930,14 +10976,14 @@
     void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
         boolean needSep = false;
-        boolean printedAnything = false;
 
         pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
         boolean printed = false;
         if (mAllowedAssociations != null) {
             for (int i = 0; i < mAllowedAssociations.size(); i++) {
                 final String pkg = mAllowedAssociations.keyAt(i);
-                final ArraySet<String> asc = mAllowedAssociations.valueAt(i);
+                final ArraySet<String> asc =
+                        mAllowedAssociations.valueAt(i).getAllowedPackageAssociations();
                 boolean printedHeader = false;
                 for (int j = 0; j < asc.size(); j++) {
                     if (dumpPackage == null || pkg.equals(dumpPackage)
@@ -10946,7 +10992,6 @@
                             pw.println("  Allowed associations (by restricted package):");
                             printed = true;
                             needSep = true;
-                            printedAnything = true;
                         }
                         if (!printedHeader) {
                             pw.print("  * ");
@@ -10958,6 +11003,9 @@
                         pw.println(asc.valueAt(j));
                     }
                 }
+                if (mAllowedAssociations.valueAt(i).isDebuggable()) {
+                    pw.println("      (debuggable)");
+                }
             }
         }
         if (!printed) {
@@ -13247,16 +13295,17 @@
         app.receivers.clear();
 
         // If the app is undergoing backup, tell the backup manager about it
-        if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
+        final BackupRecord backupTarget = mBackupTargets.get(app.userId);
+        if (backupTarget != null && app.pid == backupTarget.app.pid) {
             if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
-                    + mBackupTarget.appInfo + " died during backup");
+                    + backupTarget.appInfo + " died during backup");
             mHandler.post(new Runnable() {
                 @Override
                 public void run(){
                     try {
                         IBackupManager bm = IBackupManager.Stub.asInterface(
                                 ServiceManager.getService(Context.BACKUP_SERVICE));
-                        bm.agentDisconnected(app.info.packageName);
+                        bm.agentDisconnectedForUser(app.userId, app.info.packageName);
                     } catch (RemoteException e) {
                         // can't happen; backup manager is local
                     }
@@ -13596,7 +13645,11 @@
     // instantiated.  The backup agent will invoke backupAgentCreated() on the
     // activity manager to announce its creation.
     public boolean bindBackupAgent(String packageName, int backupMode, int userId) {
-        if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode);
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode="
+                    + backupMode + " userId=" + userId + " callingUid = " + Binder.getCallingUid()
+                    + " uid = " + Process.myUid());
+        }
         enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent");
 
         IPackageManager pm = AppGlobals.getPackageManager();
@@ -13648,10 +13701,10 @@
                 proc.inFullBackup = true;
             }
             r.app = proc;
-            oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
+            final BackupRecord backupTarget = mBackupTargets.get(userId);
+            oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
             newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1;
-            mBackupTarget = r;
-            mBackupAppName = app.packageName;
+            mBackupTargets.put(userId, r);
 
             // Try not to kill the process during backup
             updateOomAdjLocked(proc, true);
@@ -13686,14 +13739,14 @@
         return true;
     }
 
-    @Override
-    public void clearPendingBackup() {
-        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "clearPendingBackup");
-        enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup");
+    private void clearPendingBackup(int userId) {
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG_BACKUP, "clearPendingBackup: userId = " + userId + " callingUid = "
+                    + Binder.getCallingUid() + " uid = " + Process.myUid());
+        }
 
         synchronized (this) {
-            mBackupTarget = null;
-            mBackupAppName = null;
+            mBackupTargets.delete(userId);
         }
 
         JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
@@ -13701,12 +13754,19 @@
     }
 
     // A backup agent has just come up
+    @Override
     public void backupAgentCreated(String agentPackageName, IBinder agent) {
-        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName
-                + " = " + agent);
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName + " = " + agent
+                    + " callingUserId = " + callingUserId + " callingUid = "
+                    + Binder.getCallingUid() + " uid = " + Process.myUid());
+        }
 
         synchronized(this) {
-            if (!agentPackageName.equals(mBackupAppName)) {
+            final BackupRecord backupTarget = mBackupTargets.get(callingUserId);
+            String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName;
+            if (!agentPackageName.equals(backupAppName)) {
                 Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
                 return;
             }
@@ -13716,7 +13776,7 @@
         try {
             IBackupManager bm = IBackupManager.Stub.asInterface(
                     ServiceManager.getService(Context.BACKUP_SERVICE));
-            bm.agentConnected(agentPackageName, agent);
+            bm.agentConnectedForUser(callingUserId, agentPackageName, agent);
         } catch (RemoteException e) {
             // can't happen; the backup manager service is local
         } catch (Exception e) {
@@ -13729,7 +13789,12 @@
 
     // done with this agent
     public void unbindBackupAgent(ApplicationInfo appInfo) {
-        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo);
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo + " appInfo.uid = "
+                    + appInfo.uid + " callingUid = " + Binder.getCallingUid() + " uid = "
+                    + Process.myUid());
+        }
+
         enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "unbindBackupAgent");
         if (appInfo == null) {
             Slog.w(TAG, "unbind backup agent for null app");
@@ -13738,24 +13803,27 @@
 
         int oldBackupUid;
 
+        final int userId = UserHandle.getUserId(appInfo.uid);
         synchronized(this) {
+            final BackupRecord backupTarget = mBackupTargets.get(userId);
+            String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName;
             try {
-                if (mBackupAppName == null) {
+                if (backupAppName == null) {
                     Slog.w(TAG, "Unbinding backup agent with no active backup");
                     return;
                 }
 
-                if (!mBackupAppName.equals(appInfo.packageName)) {
+                if (!backupAppName.equals(appInfo.packageName)) {
                     Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
                     return;
                 }
 
                 // Not backing this app up any more; reset its OOM adjustment
-                final ProcessRecord proc = mBackupTarget.app;
+                final ProcessRecord proc = backupTarget.app;
                 updateOomAdjLocked(proc, true);
                 proc.inFullBackup = false;
 
-                oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
+                oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
 
                 // If the app crashed during backup, 'thread' will be null here
                 if (proc.thread != null) {
@@ -13768,8 +13836,7 @@
                     }
                 }
             } finally {
-                mBackupTarget = null;
-                mBackupAppName = null;
+                mBackupTargets.delete(userId);
             }
         }
 
@@ -14333,6 +14400,7 @@
             case BLUETOOTH_UID:
             case NFC_UID:
             case SE_UID:
+            case NETWORK_STACK_UID:
                 isCallerSystem = true;
                 break;
             default:
@@ -14519,6 +14587,7 @@
                                     + " ssp=" + ssp + " data=" + data);
                             return ActivityManager.BROADCAST_SUCCESS;
                         }
+                        updateAssociationForApp(aInfo);
                         mAtmInternal.onPackageReplaced(aInfo);
                         mServices.updateServiceApplicationInfoLocked(aInfo);
                         sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
@@ -14613,7 +14682,7 @@
                     Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                 final int uid = getUidFromIntent(intent);
                 if (uid != -1) {
-                    final UidRecord uidRec = mActiveUids.get(uid);
+                    final UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
                     if (uidRec != null) {
                         uidRec.updateHasInternetPermission();
                     }
@@ -15391,7 +15460,7 @@
     // Returns whether the app is receiving broadcast.
     // If receiving, fetch all broadcast queues which the app is
     // the current [or imminent] receiver on.
-    private boolean isReceivingBroadcastLocked(ProcessRecord app,
+    boolean isReceivingBroadcastLocked(ProcessRecord app,
             ArraySet<BroadcastQueue> receivingQueues) {
         final int N = app.curReceivers.size();
         if (N > 0) {
@@ -15511,1087 +15580,6 @@
         }
     }
 
-    private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
-            new ComputeOomAdjWindowCallback();
-
-    /** These methods are called inline during computeOomAdjLocked(), on the same thread */
-    private final class ComputeOomAdjWindowCallback
-            implements WindowProcessController.ComputeOomAdjCallback {
-
-        ProcessRecord app;
-        int adj;
-        boolean foregroundActivities;
-        int procState;
-        int schedGroup;
-        int appUid;
-        int logUid;
-        int processStateCurTop;
-
-        void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
-                int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
-            this.app = app;
-            this.adj = adj;
-            this.foregroundActivities = foregroundActivities;
-            this.procState = procState;
-            this.schedGroup = schedGroup;
-            this.appUid = appUid;
-            this.logUid = logUid;
-            this.processStateCurTop = processStateCurTop;
-        }
-
-        @Override
-        public void onVisibleActivity() {
-            // App has a visible activity; only upgrade adjustment.
-            if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                adj = ProcessList.VISIBLE_APP_ADJ;
-                app.adjType = "vis-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
-                }
-            }
-            if (procState > processStateCurTop) {
-                procState = processStateCurTop;
-                app.adjType = "vis-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to vis-activity (top): " + app);
-                }
-            }
-            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            }
-            app.cached = false;
-            app.empty = false;
-            foregroundActivities = true;
-        }
-
-        @Override
-        public void onPausedActivity() {
-            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "pause-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: "  + app);
-                }
-            }
-            if (procState > processStateCurTop) {
-                procState = processStateCurTop;
-                app.adjType = "pause-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to pause-activity (top): "  + app);
-                }
-            }
-            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            }
-            app.cached = false;
-            app.empty = false;
-            foregroundActivities = true;
-        }
-
-        @Override
-        public void onStoppingActivity(boolean finishing) {
-            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "stop-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise adj to stop-activity: "  + app);
-                }
-            }
-
-            // For the process state, we will at this point consider the process to be cached. It
-            // will be cached either as an activity or empty depending on whether the activity is
-            // finishing. We do this so that we can treat the process as cached for purposes of
-            // memory trimming (determining current memory level, trim command to send to process)
-            // since there can be an arbitrary number of stopping processes and they should soon all
-            // go into the cached state.
-            if (!finishing) {
-                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
-                    procState = PROCESS_STATE_LAST_ACTIVITY;
-                    app.adjType = "stop-activity";
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                "Raise procstate to stop-activity: " + app);
-                    }
-                }
-            }
-            app.cached = false;
-            app.empty = false;
-            foregroundActivities = true;
-        }
-
-        @Override
-        public void onOtherActivity() {
-            if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
-                procState = PROCESS_STATE_CACHED_ACTIVITY;
-                app.adjType = "cch-act";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to cached activity: " + app);
-                }
-            }
-        }
-    }
-
-    private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
-            boolean doingAll, long now, boolean cycleReEval) {
-        if (mAdjSeq == app.adjSeq) {
-            if (app.adjSeq == app.completedAdjSeq) {
-                // This adjustment has already been computed successfully.
-                return false;
-            } else {
-                // The process is being computed, so there is a cycle. We cannot
-                // rely on this process's state.
-                app.containsCycle = true;
-
-                return false;
-            }
-        }
-
-        if (app.thread == null) {
-            app.adjSeq = mAdjSeq;
-            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
-            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;
-        }
-
-        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
-        app.adjSource = null;
-        app.adjTarget = null;
-        app.empty = false;
-        app.cached = false;
-
-        final WindowProcessController wpc = app.getWindowProcessController();
-        final int appUid = app.info.uid;
-        final int logUid = mCurOomAdjUid;
-
-        int prevAppAdj = app.curAdj;
-        int prevProcState = app.getCurProcState();
-
-        if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
-            // The max adjustment doesn't allow this app to be anything
-            // below foreground, so it is not worth doing work for it.
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
-            }
-            app.adjType = "fixed";
-            app.adjSeq = mAdjSeq;
-            app.setCurRawAdj(app.maxAdj);
-            app.setHasForegroundActivities(false);
-            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
-            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
-            // is currently showing UI.
-            app.systemNoUi = true;
-            if (app == TOP_APP) {
-                app.systemNoUi = false;
-                app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
-                app.adjType = "pers-top-activity";
-            } else if (app.hasTopUi()) {
-                // sched group/proc state adjustment is below
-                app.systemNoUi = false;
-                app.adjType = "pers-top-ui";
-            } else if (wpc.hasVisibleActivities()) {
-                app.systemNoUi = false;
-            }
-            if (!app.systemNoUi) {
-                if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
-                    // screen on, promote UI
-                    app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
-                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
-                } else {
-                    // screen off, restrict UI scheduling
-                    app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
-                }
-            }
-            app.setCurRawProcState(app.getCurProcState());
-            app.curAdj = app.maxAdj;
-            app.completedAdjSeq = app.adjSeq;
-            // if curAdj is less than prevAppAdj, then this process was promoted
-            return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
-        }
-
-        app.systemNoUi = false;
-
-        final int PROCESS_STATE_CUR_TOP = mAtmInternal.getTopProcessState();
-
-        // Determine the importance of the process, starting with most
-        // important to least, and assign an appropriate OOM adjustment.
-        int adj;
-        int schedGroup;
-        int procState;
-        int cachedAdjSeq;
-
-        boolean foregroundActivities = false;
-        mTmpBroadcastQueue.clear();
-        if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
-            // The last app on the list is the foreground app.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
-            app.adjType = "top-activity";
-            foregroundActivities = true;
-            procState = PROCESS_STATE_CUR_TOP;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
-            }
-        } else if (app.runningRemoteAnimation) {
-            adj = ProcessList.VISIBLE_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
-            app.adjType = "running-remote-anim";
-            procState = PROCESS_STATE_CUR_TOP;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
-            }
-        } else if (app.getActiveInstrumentation() != null) {
-            // Don't want to kill running instrumentation.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            app.adjType = "instrumentation";
-            procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
-            }
-        } else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
-            // An app that is currently receiving a broadcast also
-            // counts as being in the foreground for OOM killer purposes.
-            // It's placed in a sched group based on the nature of the
-            // broadcast as reflected by which queue it's active in.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = (mTmpBroadcastQueue.contains(mFgBroadcastQueue))
-                    ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "broadcast";
-            procState = ActivityManager.PROCESS_STATE_RECEIVER;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
-            }
-        } else if (app.executingServices.size() > 0) {
-            // An app that is currently executing a service callback also
-            // counts as being in the foreground.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = app.execServicesFg ?
-                    ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "exec-service";
-            procState = ActivityManager.PROCESS_STATE_SERVICE;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
-            }
-            //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
-        } else if (app == TOP_APP) {
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "top-sleeping";
-            foregroundActivities = true;
-            procState = PROCESS_STATE_CUR_TOP;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app);
-            }
-        } else {
-            // As far as we know the process is empty.  We may change our mind later.
-            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-            // At this point we don't actually know the adjustment.  Use the cached adj
-            // value that the caller wants us to.
-            adj = cachedAdj;
-            procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-            app.cached = true;
-            app.empty = true;
-            app.adjType = "cch-empty";
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
-            }
-        }
-
-        // Examine all activities if not already foreground.
-        if (!foregroundActivities && wpc.hasActivities()) {
-            mTmpComputeOomAdjWindowCallback.initialize(app, adj, foregroundActivities, procState,
-                    schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP);
-            final int minLayer = wpc.computeOomAdjFromActivities(
-                    ProcessList.VISIBLE_APP_LAYER_MAX, mTmpComputeOomAdjWindowCallback);
-
-            adj = mTmpComputeOomAdjWindowCallback.adj;
-            foregroundActivities = mTmpComputeOomAdjWindowCallback.foregroundActivities;
-            procState = mTmpComputeOomAdjWindowCallback.procState;
-            schedGroup = mTmpComputeOomAdjWindowCallback.schedGroup;
-
-            if (adj == ProcessList.VISIBLE_APP_ADJ) {
-                adj += minLayer;
-            }
-        }
-
-        if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
-            procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
-            app.adjType = "cch-rec";
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
-            }
-        }
-
-        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
-                || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-            if (app.hasForegroundServices()) {
-                // The user is aware of this app, so make it visible.
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-                app.cached = false;
-                app.adjType = "fg-service";
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app);
-                }
-            } else if (app.hasOverlayUi()) {
-                // The process is display an overlay UI.
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-                app.cached = false;
-                app.adjType = "has-overlay-ui";
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app);
-                }
-            }
-        }
-
-        // If the app was recently in the foreground and moved to a foreground service status,
-        // allow it to get a higher rank in memory for some time, compared to other foreground
-        // services so that it can finish performing any persistence/processing of in-memory state.
-        if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
-                && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
-                    || app.setProcState <= ActivityManager.PROCESS_STATE_TOP)) {
-            adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
-            app.adjType = "fg-service-act";
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
-            }
-        }
-
-        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
-                || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
-            if (app.forcingToImportant != null) {
-                // This is currently used for toasts...  they are not interactive, and
-                // we don't want them to cause the app to become fully foreground (and
-                // thus out of background check), so we yes the best background level we can.
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
-                app.cached = false;
-                app.adjType = "force-imp";
-                app.adjSource = app.forcingToImportant;
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
-                }
-            }
-        }
-
-        if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
-            if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
-                // We don't want to kill the current heavy-weight process.
-                adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "heavy";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
-                }
-            }
-            if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
-                procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
-                app.adjType = "heavy";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
-                }
-            }
-        }
-
-        if (wpc.isHomeProcess()) {
-            if (adj > ProcessList.HOME_APP_ADJ) {
-                // This process is hosting what we currently consider to be the
-                // home app, so we don't want to let it go into the background.
-                adj = ProcessList.HOME_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "home";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
-                }
-            }
-            if (procState > ActivityManager.PROCESS_STATE_HOME) {
-                procState = ActivityManager.PROCESS_STATE_HOME;
-                app.adjType = "home";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
-                }
-            }
-        }
-
-        if (wpc.isPreviousProcess() && app.hasActivities()) {
-            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
-                // This was the previous process that showed UI to the user.
-                // We want to try to keep it around more aggressively, to give
-                // a good experience around switching between two apps.
-                adj = ProcessList.PREVIOUS_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "previous";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
-                }
-            }
-            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
-                procState = PROCESS_STATE_LAST_ACTIVITY;
-                app.adjType = "previous";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
-                }
-            }
-        }
-
-        if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
-                + " reason=" + app.adjType);
-
-        // By default, we use the computed adjustment.  It may be changed if
-        // 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. If we're re-evaluating due to cycles, use the previously computed
-        // values.
-        app.setCurRawAdj(!cycleReEval ? adj : Math.min(adj, app.getCurRawAdj()));
-        app.setCurRawProcState(!cycleReEval
-                ? procState
-                : Math.min(procState, app.getCurRawProcState()));
-
-        app.hasStartedServices = false;
-        app.adjSeq = mAdjSeq;
-
-        if (mBackupTarget != null && app == mBackupTarget.app) {
-            // If possible we want to avoid killing apps while they're being backed up
-            if (adj > ProcessList.BACKUP_APP_ADJ) {
-                if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
-                adj = ProcessList.BACKUP_APP_ADJ;
-                if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
-                    procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
-                }
-                app.adjType = "backup";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
-                }
-                app.cached = false;
-            }
-            if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
-                procState = ActivityManager.PROCESS_STATE_BACKUP;
-                app.adjType = "backup";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
-                }
-            }
-        }
-
-        boolean mayBeTop = false;
-        String mayBeTopType = null;
-        Object mayBeTopSource = null;
-        Object mayBeTopTarget = null;
-
-        for (int is = app.services.size()-1;
-                is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                        || procState > ActivityManager.PROCESS_STATE_TOP);
-                is--) {
-            ServiceRecord s = app.services.valueAt(is);
-            if (s.startRequested) {
-                app.hasStartedServices = true;
-                if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
-                    procState = ActivityManager.PROCESS_STATE_SERVICE;
-                    app.adjType = "started-services";
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                "Raise procstate to started service: " + app);
-                    }
-                }
-                if (app.hasShownUi && !wpc.isHomeProcess()) {
-                    // If this process has shown some UI, let it immediately
-                    // go to the LRU list because it may be pretty heavy with
-                    // UI stuff.  We'll tag it with a label just to help
-                    // debug and understand what is going on.
-                    if (adj > ProcessList.SERVICE_ADJ) {
-                        app.adjType = "cch-started-ui-services";
-                    }
-                } else {
-                    if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
-                        // This service has seen some activity within
-                        // recent memory, so we will keep its process ahead
-                        // of the background processes.
-                        if (adj > ProcessList.SERVICE_ADJ) {
-                            adj = ProcessList.SERVICE_ADJ;
-                            app.adjType = "started-services";
-                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                        "Raise adj to started service: " + app);
-                            }
-                            app.cached = false;
-                        }
-                    }
-                    // If we have let the service slide into the background
-                    // state, still have some text describing what it is doing
-                    // even though the service no longer has an impact.
-                    if (adj > ProcessList.SERVICE_ADJ) {
-                        app.adjType = "cch-started-services";
-                    }
-                }
-            }
-
-            for (int conni = s.connections.size()-1;
-                    conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                            || procState > ActivityManager.PROCESS_STATE_TOP);
-                    conni--) {
-                ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
-                for (int i = 0;
-                        i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
-                                || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                                || procState > ActivityManager.PROCESS_STATE_TOP);
-                        i++) {
-                    // XXX should compute this based on the max of
-                    // all connected clients.
-                    ConnectionRecord cr = clist.get(i);
-                    if (cr.binding.client == app) {
-                        // Binding to oneself is not interesting.
-                        continue;
-                    }
-
-                    boolean trackedProcState = false;
-                    if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
-                        ProcessRecord client = cr.binding.client;
-                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
-
-                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
-                            continue;
-                        }
-
-                        int clientAdj = client.getCurRawAdj();
-                        int clientProcState = client.getCurRawProcState();
-
-                        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
-                            // doesn't propagate except under certain conditions.
-                            clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                        }
-                        String adjType = null;
-                        if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
-                            // Not doing bind OOM management, so treat
-                            // this guy more like a started service.
-                            if (app.hasShownUi && !wpc.isHomeProcess()) {
-                                // If this process has shown some UI, let it immediately
-                                // go to the LRU list because it may be pretty heavy with
-                                // UI stuff.  We'll tag it with a label just to help
-                                // debug and understand what is going on.
-                                if (adj > clientAdj) {
-                                    adjType = "cch-bound-ui-services";
-                                }
-                                app.cached = false;
-                                clientAdj = adj;
-                                clientProcState = procState;
-                            } else {
-                                if (now >= (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
-                                    // This service has not seen activity within
-                                    // recent memory, so allow it to drop to the
-                                    // LRU list if there is no other reason to keep
-                                    // it around.  We'll also tag it with a label just
-                                    // to help debug and undertand what is going on.
-                                    if (adj > clientAdj) {
-                                        adjType = "cch-bound-services";
-                                    }
-                                    clientAdj = adj;
-                                }
-                            }
-                        }
-                        if (adj > clientAdj) {
-                            // If this process has recently shown UI, and
-                            // the process that is binding to it is less
-                            // important than being visible, then we don't
-                            // care about the binding as much as we care
-                            // about letting this process get into the LRU
-                            // list to be killed and restarted if needed for
-                            // memory.
-                            if (app.hasShownUi && !wpc.isHomeProcess()
-                                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
-                                    adjType = "cch-bound-ui-services";
-                                }
-                            } else {
-                                int newAdj;
-                                if ((cr.flags&(Context.BIND_ABOVE_CLIENT
-                                        |Context.BIND_IMPORTANT)) != 0) {
-                                    if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
-                                        newAdj = clientAdj;
-                                    } else {
-                                        // make this service persistent
-                                        newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
-                                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                                        procState = ActivityManager.PROCESS_STATE_PERSISTENT;
-                                        cr.trackProcState(procState, mAdjSeq, now);
-                                        trackedProcState = true;
-                                    }
-                                } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0
-                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ + 1) {
-                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ + 1;
-                                } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
-                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                                } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                    newAdj = clientAdj;
-                                } else {
-                                    if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                                        newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
-                                    } else {
-                                        newAdj = adj;
-                                    }
-                                }
-                                if (!client.cached) {
-                                    app.cached = false;
-                                }
-                                if (adj >  newAdj) {
-                                    adj = newAdj;
-                                    app.setCurRawAdj(adj);
-                                    adjType = "service";
-                                }
-                            }
-                        }
-                        if ((cr.flags & (Context.BIND_NOT_FOREGROUND
-                                | Context.BIND_IMPORTANT_BACKGROUND)) == 0) {
-                            // This will treat important bound services identically to
-                            // the top app, which may behave differently than generic
-                            // foreground work.
-                            final int curSchedGroup = client.getCurrentSchedulingGroup();
-                            if (curSchedGroup > schedGroup) {
-                                if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
-                                    schedGroup = curSchedGroup;
-                                } else {
-                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                                }
-                            }
-                            if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
-                                if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
-                                    // Special handling of clients who are in the top state.
-                                    // We *may* want to consider this process to be in the
-                                    // top state as well, but only if there is not another
-                                    // reason for it to be running.  Being on the top is a
-                                    // special state, meaning you are specifically running
-                                    // for the current top app.  If the process is already
-                                    // running in the background for some other reason, it
-                                    // is more important to continue considering it to be
-                                    // in the background state.
-                                    mayBeTop = true;
-                                    mayBeTopType = "service";
-                                    mayBeTopSource = cr.binding.client;
-                                    mayBeTopTarget = s.instanceName;
-                                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                                } else {
-                                    // Special handling for above-top states (persistent
-                                    // processes).  These should not bring the current process
-                                    // into the top state, since they are not on top.  Instead
-                                    // give them the best state after that.
-                                    if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
-                                        clientProcState =
-                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                                    } else if (mWakefulness
-                                                    == PowerManagerInternal.WAKEFULNESS_AWAKE &&
-                                            (cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
-                                                    != 0) {
-                                        clientProcState =
-                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                                    } else {
-                                        clientProcState =
-                                                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-                                    }
-                                }
-                            }
-                        } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
-                            if (clientProcState <
-                                    ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
-                                clientProcState =
-                                        ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
-                            }
-                        } else {
-                            if (clientProcState <
-                                    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
-                                clientProcState =
-                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
-                            }
-                        }
-                        if (!trackedProcState) {
-                            cr.trackProcState(clientProcState, mAdjSeq, now);
-                        }
-                        if (procState > clientProcState) {
-                            procState = clientProcState;
-                            app.setCurRawProcState(procState);
-                            if (adjType == null) {
-                                adjType = "service";
-                            }
-                        }
-                        if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                                && (cr.flags & Context.BIND_SHOWING_UI) != 0) {
-                            app.setPendingUiClean(true);
-                        }
-                        if (adjType != null) {
-                            app.adjType = adjType;
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = cr.binding.client;
-                            app.adjSourceProcState = clientProcState;
-                            app.adjTarget = s.instanceName;
-                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
-                                        + ": " + app + ", due to " + cr.binding.client
-                                        + " adj=" + adj + " procState="
-                                        + ProcessList.makeProcStateString(procState));
-                            }
-                        }
-                    }
-                    if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
-                        app.treatLikeActivity = true;
-                    }
-                    final ActivityServiceConnectionsHolder a = cr.activity;
-                    if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
-                        if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
-                                && a.isActivityVisible()) {
-                            adj = ProcessList.FOREGROUND_APP_ADJ;
-                            app.setCurRawAdj(adj);
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
-                                    schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
-                                } else {
-                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                                }
-                            }
-                            app.cached = false;
-                            app.adjType = "service";
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = a;
-                            app.adjSourceProcState = procState;
-                            app.adjTarget = s.instanceName;
-                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                        "Raise to service w/activity: " + app);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        for (int provi = app.pubProviders.size()-1;
-                provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                        || procState > ActivityManager.PROCESS_STATE_TOP);
-                provi--) {
-            ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
-            for (int i = cpr.connections.size()-1;
-                    i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                            || procState > ActivityManager.PROCESS_STATE_TOP);
-                    i--) {
-                ContentProviderConnection conn = cpr.connections.get(i);
-                ProcessRecord client = conn.client;
-                if (client == app) {
-                    // Being our own client is not interesting.
-                    continue;
-                }
-                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
-
-                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
-                    continue;
-                }
-
-                int clientAdj = client.getCurRawAdj();
-                int clientProcState = client.getCurRawProcState();
-
-                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.
-                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                }
-                String adjType = null;
-                if (adj > clientAdj) {
-                    if (app.hasShownUi && !wpc.isHomeProcess()
-                            && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                        adjType = "cch-ui-provider";
-                    } else {
-                        adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
-                                ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
-                        app.setCurRawAdj(adj);
-                        adjType = "provider";
-                    }
-                    app.cached &= client.cached;
-                }
-                if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
-                    if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
-                        // Special handling of clients who are in the top state.
-                        // We *may* want to consider this process to be in the
-                        // top state as well, but only if there is not another
-                        // reason for it to be running.  Being on the top is a
-                        // special state, meaning you are specifically running
-                        // for the current top app.  If the process is already
-                        // running in the background for some other reason, it
-                        // is more important to continue considering it to be
-                        // in the background state.
-                        mayBeTop = true;
-                        clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                        mayBeTopType = adjType = "provider-top";
-                        mayBeTopSource = client;
-                        mayBeTopTarget = cpr.name;
-                    } else {
-                        // Special handling for above-top states (persistent
-                        // processes).  These should not bring the current process
-                        // into the top state, since they are not on top.  Instead
-                        // give them the best state after that.
-                        clientProcState =
-                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                        if (adjType == null) {
-                            adjType = "provider";
-                        }
-                    }
-                }
-                conn.trackProcState(clientProcState, mAdjSeq, now);
-                if (procState > clientProcState) {
-                    procState = clientProcState;
-                    app.setCurRawProcState(procState);
-                }
-                if (client.getCurrentSchedulingGroup() > schedGroup) {
-                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                }
-                if (adjType != null) {
-                    app.adjType = adjType;
-                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                            .REASON_PROVIDER_IN_USE;
-                    app.adjSource = client;
-                    app.adjSourceProcState = clientProcState;
-                    app.adjTarget = cpr.name;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
-                                + ": " + app + ", due to " + client
-                                + " adj=" + adj + " procState="
-                                + ProcessList.makeProcStateString(procState));
-                    }
-                }
-            }
-            // If the provider has external (non-framework) process
-            // dependencies, ensure that its adjustment is at least
-            // FOREGROUND_APP_ADJ.
-            if (cpr.hasExternalProcessHandles()) {
-                if (adj > ProcessList.FOREGROUND_APP_ADJ) {
-                    adj = ProcessList.FOREGROUND_APP_ADJ;
-                    app.setCurRawAdj(adj);
-                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                    app.cached = false;
-                    app.adjType = "ext-provider";
-                    app.adjTarget = cpr.name;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                "Raise adj to external provider: " + app);
-                    }
-                }
-                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
-                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-                    app.setCurRawProcState(procState);
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                "Raise procstate to external provider: " + app);
-                    }
-                }
-            }
-        }
-
-        if (app.lastProviderTime > 0 &&
-                (app.lastProviderTime+mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
-            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
-                adj = ProcessList.PREVIOUS_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "recent-provider";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise adj to recent provider: " + app);
-                }
-            }
-            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
-                procState = PROCESS_STATE_LAST_ACTIVITY;
-                app.adjType = "recent-provider";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to recent provider: " + app);
-                }
-            }
-        }
-
-        if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
-            // A client of one of our services or providers is in the top state.  We
-            // *may* want to be in the top state, but not if we are already running in
-            // the background for some other reason.  For the decision here, we are going
-            // to pick out a few specific states that we want to remain in when a client
-            // is top (states that tend to be longer-term) and otherwise allow it to go
-            // to the top state.
-            switch (procState) {
-                case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
-                case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
-                    // Something else is keeping it at this level, just leave it.
-                    break;
-                case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
-                case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
-                case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
-                case ActivityManager.PROCESS_STATE_SERVICE:
-                    // These all are longer-term states, so pull them up to the top
-                    // of the background states, but not all the way to the top state.
-                    procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                    app.adjType = mayBeTopType;
-                    app.adjSource = mayBeTopSource;
-                    app.adjTarget = mayBeTopTarget;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
-                                + ": " + app + ", due to " + mayBeTopSource
-                                + " adj=" + adj + " procState="
-                                + ProcessList.makeProcStateString(procState));
-                    }
-                    break;
-                default:
-                    // Otherwise, top is a better choice, so take it.
-                    procState = ActivityManager.PROCESS_STATE_TOP;
-                    app.adjType = mayBeTopType;
-                    app.adjSource = mayBeTopSource;
-                    app.adjTarget = mayBeTopTarget;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
-                                + ": " + app + ", due to " + mayBeTopSource
-                                + " adj=" + adj + " procState="
-                                + ProcessList.makeProcStateString(procState));
-                    }
-                    break;
-            }
-        }
-
-        if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
-            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";
-            } else if (app.treatLikeActivity) {
-                // This is a cached process, but somebody wants us to treat it like it has
-                // an activity, okay!
-                procState = PROCESS_STATE_CACHED_ACTIVITY;
-                app.adjType = "cch-as-act";
-            }
-        }
-
-        if (adj == ProcessList.SERVICE_ADJ) {
-            if (doingAll) {
-                app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
-                mNewNumServiceProcs++;
-                //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
-                if (!app.serviceb) {
-                    // This service isn't far enough down on the LRU list to
-                    // normally be a B service, but if we are low on RAM and it
-                    // is large we want to force it down since we would prefer to
-                    // keep launcher over it.
-                    if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
-                            && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
-                        app.serviceHighRam = true;
-                        app.serviceb = true;
-                        //Slog.i(TAG, "ADJ " + app + " high ram!");
-                    } else {
-                        mNewNumAServiceProcs++;
-                        //Slog.i(TAG, "ADJ " + app + " not high ram!");
-                    }
-                } else {
-                    app.serviceHighRam = false;
-                }
-            }
-            if (app.serviceb) {
-                adj = ProcessList.SERVICE_B_ADJ;
-            }
-        }
-
-        app.setCurRawAdj(adj);
-
-        //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
-        //      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
-        if (adj > app.maxAdj) {
-            adj = app.maxAdj;
-            if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            }
-        }
-
-        // Put bound foreground services in a special sched group for additional
-        // restrictions on screen off
-        if (procState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE &&
-            mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
-            if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
-                schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
-            }
-        }
-
-        // Do final modification to adj.  Everything we do between here and applying
-        // the final setAdj must be done in this function, because we will also use
-        // it when computing the final cached adj later.  Note that we don't need to
-        // worry about this for max adj above, since max adj will always be used to
-        // keep it out of the cached vaues.
-        app.curAdj = app.modifyRawOomAdj(adj);
-        app.setCurrentSchedulingGroup(schedGroup);
-        app.setCurProcState(procState);
-        app.setCurRawProcState(procState);
-        app.setHasForegroundActivities(foregroundActivities);
-        app.completedAdjSeq = mAdjSeq;
-
-        // if curAdj or curProcState improved, then this process was promoted
-        return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
-    }
-
-    /**
-     * Checks if for the given app and client, there's a cycle that should skip over the client
-     * for now or use partial values to evaluate the effect of the client binding.
-     * @param app
-     * @param client
-     * @param procState procstate evaluated so far for this app
-     * @param adj oom_adj evaluated so far for this app
-     * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
-     *                    evaluation.
-     * @return whether to skip using the client connection at this time
-     */
-    private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
-            int procState, int adj, boolean cycleReEval) {
-        if (client.containsCycle) {
-            // We've detected a cycle. We should retry computeOomAdjLocked later in
-            // case a later-checked connection from a client  would raise its
-            // priority legitimately.
-            app.containsCycle = true;
-            // If the client has not been completely evaluated, check if it's worth
-            // using the partial values.
-            if (client.completedAdjSeq < mAdjSeq) {
-                if (cycleReEval) {
-                    // If the partial values are no better, skip until the next
-                    // attempt
-                    if (client.getCurRawProcState() >= procState
-                            && client.getCurRawAdj() >= adj) {
-                        return true;
-                    }
-                    // Else use the client's partial procstate and adj to adjust the
-                    // effect of the binding
-                } else {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     private static final class RecordPssRunnable implements Runnable {
         private final ActivityManagerService mService;
         private final ProcessRecord mProc;
@@ -16989,287 +15977,6 @@
         }
     }
 
-    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
-            long nowElapsed) {
-        boolean success = true;
-
-        if (app.getCurRawAdj() != app.setRawAdj) {
-            app.setRawAdj = app.getCurRawAdj();
-        }
-
-        int changes = 0;
-
-        if (app.curAdj != app.setAdj) {
-            // don't compact during bootup
-            if (mConstants.USE_COMPACTION && mBooted) {
-                // Perform a minor compaction when a perceptible app becomes the prev/home app
-                // Perform a major compaction when any app enters cached
-                // reminder: here, setAdj is previous state, curAdj is upcoming state
-                if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
-                    (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
-                     app.curAdj == ProcessList.HOME_APP_ADJ)) {
-                    mAppCompact.compactAppSome(app);
-                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
-                           app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
-                    mAppCompact.compactAppFull(app);
-                }
-            }
-            ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
-            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.info.uid) {
-                String msg = "Set " + app.pid + " " + app.processName + " adj "
-                        + app.curAdj + ": " + app.adjType;
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
-            }
-            app.setAdj = app.curAdj;
-            app.verifiedAdj = ProcessList.INVALID_ADJ;
-        }
-
-        final int curSchedGroup = app.getCurrentSchedulingGroup();
-        if (app.setSchedGroup != curSchedGroup) {
-            int oldSchedGroup = app.setSchedGroup;
-            app.setSchedGroup = curSchedGroup;
-            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
-                String msg = "Setting sched group of " + app.processName
-                        + " to " + curSchedGroup + ": " + app.adjType;
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
-            }
-            if (app.waitingToKill != null && app.curReceivers.isEmpty()
-                    && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
-                app.kill(app.waitingToKill, true);
-                success = false;
-            } else {
-                int processGroup;
-                switch (curSchedGroup) {
-                    case ProcessList.SCHED_GROUP_BACKGROUND:
-                        processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
-                        break;
-                    case ProcessList.SCHED_GROUP_TOP_APP:
-                    case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
-                        processGroup = THREAD_GROUP_TOP_APP;
-                        break;
-                    case ProcessList.SCHED_GROUP_RESTRICTED:
-                        processGroup = THREAD_GROUP_RESTRICTED;
-                        break;
-                    default:
-                        processGroup = THREAD_GROUP_DEFAULT;
-                        break;
-                }
-                long oldId = Binder.clearCallingIdentity();
-                try {
-                    setProcessGroup(app.pid, processGroup);
-                    if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
-                        // do nothing if we already switched to RT
-                        if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                            app.getWindowProcessController().onTopProcChanged();
-                            if (mUseFifoUiScheduling) {
-                                // Switch UI pipeline for app to SCHED_FIFO
-                                app.savedPriority = Process.getThreadPriority(app.pid);
-                                scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
-                                if (app.renderThreadTid != 0) {
-                                    scheduleAsFifoPriority(app.renderThreadTid,
-                                        /* suppressLogs */true);
-                                    if (DEBUG_OOM_ADJ) {
-                                        Slog.d("UI_FIFO", "Set RenderThread (TID " +
-                                            app.renderThreadTid + ") to FIFO");
-                                    }
-                                } else {
-                                    if (DEBUG_OOM_ADJ) {
-                                        Slog.d("UI_FIFO", "Not setting RenderThread TID");
-                                    }
-                                }
-                            } else {
-                                // Boost priority for top app UI and render threads
-                                setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
-                                if (app.renderThreadTid != 0) {
-                                    try {
-                                        setThreadPriority(app.renderThreadTid,
-                                                TOP_APP_PRIORITY_BOOST);
-                                    } catch (IllegalArgumentException e) {
-                                        // thread died, ignore
-                                    }
-                                }
-                            }
-                        }
-                    } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
-                            curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                        app.getWindowProcessController().onTopProcChanged();
-                        if (mUseFifoUiScheduling) {
-                            try {
-                                // Reset UI pipeline to SCHED_OTHER
-                                setThreadScheduler(app.pid, SCHED_OTHER, 0);
-                                setThreadPriority(app.pid, app.savedPriority);
-                                if (app.renderThreadTid != 0) {
-                                    setThreadScheduler(app.renderThreadTid,
-                                        SCHED_OTHER, 0);
-                                    setThreadPriority(app.renderThreadTid, -4);
-                                }
-                            } catch (IllegalArgumentException e) {
-                                Slog.w(TAG,
-                                        "Failed to set scheduling policy, thread does not exist:\n"
-                                                + e);
-                            } catch (SecurityException e) {
-                                Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
-                            }
-                        } else {
-                            // Reset priority for top app UI and render threads
-                            setThreadPriority(app.pid, 0);
-                            if (app.renderThreadTid != 0) {
-                                setThreadPriority(app.renderThreadTid, 0);
-                            }
-                        }
-                    }
-                } catch (Exception e) {
-                    if (false) {
-                        Slog.w(TAG, "Failed setting process group of " + app.pid
-                                + " to " + app.getCurrentSchedulingGroup());
-                        Slog.w(TAG, "at location", e);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(oldId);
-                }
-            }
-        }
-        if (app.repForegroundActivities != app.hasForegroundActivities()) {
-            app.repForegroundActivities = app.hasForegroundActivities();
-            changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
-        }
-        if (app.getReportedProcState() != app.getCurProcState()) {
-            app.setReportedProcState(app.getCurProcState());
-            if (app.thread != null) {
-                try {
-                    if (false) {
-                        //RuntimeException h = new RuntimeException("here");
-                        Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
-                                + " to " + app /*, h*/);
-                    }
-                    app.thread.setProcessState(app.getReportedProcState());
-                } catch (RemoteException e) {
-                }
-            }
-        }
-        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
-                // the data right when a process is transitioning between process
-                // states, which will tend to give noisy data.
-                long start = SystemClock.uptimeMillis();
-                long startTime = SystemClock.currentThreadTimeMillis();
-                long pss = Debug.getPss(app.pid, mTmpLong, null);
-                long endTime = SystemClock.currentThreadTimeMillis();
-                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.getCurProcState() + ": "
-                        + (SystemClock.uptimeMillis()-start) + "ms");
-            }
-            app.lastStateTime = now;
-            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.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.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.getCurProcState()) {
-            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
-                String msg = "Proc state change of " + app.processName
-                        + " 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.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.setWhenUnimportant(now);
-                app.lastCpuTime = 0;
-            }
-            // Inform UsageStats of important process state change
-            // Must be called before updating setProcState
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
-
-            maybeUpdateLastTopTime(app, now);
-
-            app.setProcState = app.getCurProcState();
-            if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
-                app.notCachedSinceIdle = false;
-            }
-            if (!doingAll) {
-                setProcessTrackerStateLocked(app, mProcessStats.getMemFactorLocked(), now);
-            } else {
-                app.procStateChanged = true;
-            }
-        } 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.
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
-        }
-
-        if (changes != 0) {
-            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                    "Changes in " + app + ": " + changes);
-            int i = mPendingProcessChanges.size()-1;
-            ProcessChangeItem item = null;
-            while (i >= 0) {
-                item = mPendingProcessChanges.get(i);
-                if (item.pid == app.pid) {
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "Re-using existing item: " + item);
-                    break;
-                }
-                i--;
-            }
-            if (i < 0) {
-                // No existing item in pending changes; need a new one.
-                final int NA = mAvailProcessChanges.size();
-                if (NA > 0) {
-                    item = mAvailProcessChanges.remove(NA-1);
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "Retrieving available item: " + item);
-                } else {
-                    item = new ProcessChangeItem();
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "Allocating new item: " + item);
-                }
-                item.changes = 0;
-                item.pid = app.pid;
-                item.uid = app.info.uid;
-                if (mPendingProcessChanges.size() == 0) {
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "*** Enqueueing dispatch processes changed!");
-                    mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG).sendToTarget();
-                }
-                mPendingProcessChanges.add(item);
-            }
-            item.changes |= changes;
-            item.foregroundActivities = app.repForegroundActivities;
-            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                    "Item " + Integer.toHexString(System.identityHashCode(item))
-                    + " " + app.toShortString() + ": changes=" + item.changes
-                    + " foreground=" + item.foregroundActivities
-                    + " type=" + app.adjType + " source=" + app.adjSource
-                    + " target=" + app.adjTarget);
-        }
-
-        return success;
-    }
-
     private boolean isEphemeralLocked(int uid) {
         String packages[] = mContext.getPackageManager().getPackagesForUid(uid);
         if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
@@ -17383,78 +16090,13 @@
         }
     }
 
-    private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
-        if (DEBUG_USAGE_STATS) {
-            Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
-                    + "] state changes: old = " + app.setProcState + ", new = "
-                    + app.getCurProcState());
-        }
-        if (mUsageStatsService == null) {
-            return;
-        }
-        boolean isInteraction;
-        // 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.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
-            isInteraction = true;
-            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.getFgInteractionTime()
-                        + mConstants.SERVICE_USAGE_INTERACTION_TIME;
-            }
-        } else {
-            isInteraction =
-                    app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-            app.setFgInteractionTime(0);
-        }
-        if (isInteraction
-                && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
-                > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
-            app.setInteractionEventTime(nowElapsed);
-            String[] packages = app.getPackageList();
-            if (packages != null) {
-                for (int i = 0; i < packages.length; i++) {
-                    mUsageStatsService.reportEvent(packages[i], app.userId,
-                            UsageEvents.Event.SYSTEM_INTERACTION);
-                }
-            }
-        }
-        app.reportedInteraction = isInteraction;
-        if (!isInteraction) {
-            app.setInteractionEventTime(0);
-        }
-    }
-
-    private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
-        if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
-                && app.getCurProcState() > ActivityManager.PROCESS_STATE_TOP) {
-            app.lastTopTime = nowUptime;
-        }
-    }
-
-    private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
+    final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
         if (proc.thread != null && proc.baseProcessTracker != null) {
             proc.baseProcessTracker.setState(
                     proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
         }
     }
 
-    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
-            ProcessRecord TOP_APP, boolean doingAll, long now) {
-        if (app.thread == null) {
-            return false;
-        }
-
-        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
-
-        return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
-    }
-
     @GuardedBy("this")
     final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
             boolean oomAdj) {
@@ -17537,29 +16179,10 @@
      */
     @GuardedBy("this")
     final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
-        final ProcessRecord TOP_APP = getTopAppLocked();
-        final boolean wasCached = app.cached;
-
-        mAdjSeq++;
-
-        // This is the desired cached adjusment we want to tell it to use.
-        // 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.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.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
-            // Changed to/from cached state, so apps after it in the LRU
-            // list may also be changed.
-            updateOomAdjLocked();
-        }
-        return success;
+        return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll);
     }
 
-    private static final class ProcStatsRunnable implements Runnable {
+    static final class ProcStatsRunnable implements Runnable {
         private final ActivityManagerService mService;
         private final ProcessStatsService mProcessStats;
 
@@ -17750,387 +16373,7 @@
 
     @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 = mProcessList.getLruSizeLocked();
-
-        // Reset state in all uid records.
-        for (int i=mActiveUids.size()-1; i>=0; i--) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                    "Starting update of " + uidRec);
-            uidRec.reset();
-        }
-
-        if (mAtmInternal != null) {
-            mAtmInternal.rankTaskLayersIfNeeded();
-        }
-
-        mAdjSeq++;
-        mNewNumServiceProcs = 0;
-        mNewNumAServiceProcs = 0;
-
-        final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
-        final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit;
-
-        // Let's determine how many processes we have running vs.
-        // how many slots we have for background processes; we may want
-        // to put multiple processes in a slot of there are enough of
-        // them.
-        final int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
-                - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2
-                / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
-        if (numEmptyProcs > cachedProcessLimit) {
-            // If there are more empty processes than our limit on cached
-            // processes, then use the cached process limit for the factor.
-            // This ensures that the really old empty processes get pushed
-            // down to the bottom, so if we are running low on memory we will
-            // have a better chance at keeping around more cached processes
-            // instead of a gazillion empty processes.
-            numEmptyProcs = cachedProcessLimit;
-        }
-        int emptyFactor = (numEmptyProcs + numSlots - 1) / numSlots;
-        if (emptyFactor < 1) emptyFactor = 1;
-        int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + numSlots - 1) : 1)
-                / numSlots;
-        if (cachedFactor < 1) cachedFactor = 1;
-        int stepCached = -1;
-        int stepEmpty = -1;
-        int numCached = 0;
-        int numCachedExtraGroup = 0;
-        int numEmpty = 0;
-        int numTrimming = 0;
-        int lastCachedGroup = 0;
-        int lastCachedGroupImportance = 0;
-        int lastCachedGroupUid = 0;
-
-        mNumNonCachedProcs = 0;
-        mNumCachedHiddenProcs = 0;
-
-        // First update the OOM adjustment for each of the
-        // application processes based on their current state.
-        int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
-        int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
-        int curCachedImpAdj = 0;
-        int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
-
-        boolean retryCycles = false;
-
-        // need to reset cycle state before calling computeOomAdjLocked because of service connections
-        for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mProcessList.mLruProcesses.get(i);
-            app.containsCycle = false;
-            app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
-            app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
-        }
-        for (int i=N-1; i>=0; 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, false);
-
-                // if any app encountered a cycle, we need to perform an additional loop later
-                retryCycles |= app.containsCycle;
-
-                // If we haven't yet assigned the final cached adj
-                // to the process, do that now.
-                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
-                    switch (app.getCurProcState()) {
-                        case PROCESS_STATE_CACHED_ACTIVITY:
-                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-                        case ActivityManager.PROCESS_STATE_CACHED_RECENT:
-                            // Figure out the next cached level, taking into account groups.
-                            boolean inGroup = false;
-                            if (app.connectionGroup != 0) {
-                                if (lastCachedGroupUid == app.uid
-                                        && lastCachedGroup == app.connectionGroup) {
-                                    // This is in the same group as the last process, just tweak
-                                    // adjustment by importance.
-                                    if (app.connectionImportance > lastCachedGroupImportance) {
-                                        lastCachedGroupImportance = app.connectionImportance;
-                                        if (curCachedAdj < nextCachedAdj
-                                                && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
-                                            curCachedImpAdj++;
-                                        }
-                                    }
-                                    inGroup = true;
-                                } else {
-                                    lastCachedGroupUid = app.uid;
-                                    lastCachedGroup = app.connectionGroup;
-                                    lastCachedGroupImportance = app.connectionImportance;
-                                }
-                            }
-                            if (!inGroup && curCachedAdj != nextCachedAdj) {
-                                stepCached++;
-                                curCachedImpAdj = 0;
-                                if (stepCached >= cachedFactor) {
-                                    stepCached = 0;
-                                    curCachedAdj = nextCachedAdj;
-                                    nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
-                                    if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                        nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                                    }
-                                }
-                            }
-                            // This process is a cached process holding activities...
-                            // assign it the next cached value for that type, and then
-                            // step that cached level.
-                            app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
-                            app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
-                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
-                                    + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
-                                    + " curCachedImpAdj=" + curCachedImpAdj + ")");
-                            break;
-                        default:
-                            // Figure out the next cached level.
-                            if (curEmptyAdj != nextEmptyAdj) {
-                                stepEmpty++;
-                                if (stepEmpty >= emptyFactor) {
-                                    stepEmpty = 0;
-                                    curEmptyAdj = nextEmptyAdj;
-                                    nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
-                                    if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                        nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                                    }
-                                }
-                            }
-                            // For everything else, assign next empty cached process
-                            // level and bump that up.  Note that this means that
-                            // 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.setCurRawAdj(curEmptyAdj);
-                            app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
-                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
-                                    + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
-                                    + ")");
-                            break;
-                    }
-                }
-            }
-        }
-
-        // Cycle strategy:
-        // - Retry computing any process that has encountered a cycle.
-        // - Continue retrying until no process was promoted.
-        // - Iterate from least important to most important.
-        int cycleCount = 0;
-        while (retryCycles && cycleCount < 10) {
-            cycleCount++;
-            retryCycles = false;
-
-            for (int i=0; i<N; i++) {
-                ProcessRecord app = mProcessList.mLruProcesses.get(i);
-                if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-                    app.adjSeq--;
-                    app.completedAdjSeq--;
-                }
-            }
-
-            for (int i=0; i<N; 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,
-                            true)) {
-                        retryCycles = true;
-                    }
-                }
-            }
-        }
-
-        lastCachedGroup = lastCachedGroupUid = 0;
-
-        for (int i=N-1; i>=0; 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.getCurProcState()) {
-                    case PROCESS_STATE_CACHED_ACTIVITY:
-                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-                        mNumCachedHiddenProcs++;
-                        numCached++;
-                        if (app.connectionGroup != 0) {
-                            if (lastCachedGroupUid == app.uid
-                                    && lastCachedGroup == app.connectionGroup) {
-                                // If this process is the next in the same group, we don't
-                                // want it to count against our limit of the number of cached
-                                // processes, so bump up the group count to account for it.
-                                numCachedExtraGroup++;
-                            } else {
-                                lastCachedGroupUid = app.uid;
-                                lastCachedGroup = app.connectionGroup;
-                            }
-                        } else {
-                            lastCachedGroupUid = lastCachedGroup = 0;
-                        }
-                        if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
-                            app.kill("cached #" + numCached, true);
-                        }
-                        break;
-                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
-                        if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
-                                && app.lastActivityTime < oldTime) {
-                            app.kill("empty for "
-                                    + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
-                                    / 1000) + "s", true);
-                        } else {
-                            numEmpty++;
-                            if (numEmpty > emptyProcessLimit) {
-                                app.kill("empty #" + numEmpty, true);
-                            }
-                        }
-                        break;
-                    default:
-                        mNumNonCachedProcs++;
-                        break;
-                }
-
-                if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) {
-                    // If this is an isolated process, there are no services
-                    // running in it, and it's not a special process with a
-                    // custom entry point, then the process is no longer
-                    // needed.  We agressively kill these because we can by
-                    // definition not re-use the same process again, and it is
-                    // good to avoid having whatever code was running in them
-                    // left sitting around after no longer needed.
-                    app.kill("isolated not needed", true);
-                } else {
-                    // Keeping this process, update its uid.
-                    final UidRecord uidRec = app.uidRecord;
-                    if (uidRec != null) {
-                        uidRec.ephemeral = app.info.isInstantApp();
-                        if (uidRec.getCurProcState() > app.getCurProcState()) {
-                            uidRec.setCurProcState(app.getCurProcState());
-                        }
-                        if (app.hasForegroundServices()) {
-                            uidRec.foregroundServices = true;
-                        }
-                    }
-                }
-
-                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
-                        && !app.killedByAm) {
-                    numTrimming++;
-                }
-            }
-        }
-
-        incrementProcStateSeqAndNotifyAppsLocked();
-
-        mNumServiceProcs = mNewNumServiceProcs;
-
-        boolean allChanged = updateLowMemStateLocked(numCached, numEmpty, numTrimming);
-
-        if (mAlwaysFinishActivities) {
-            // Need to do this on its own message because the stack may not
-            // be in a consistent state at this point.
-            mAtmInternal.scheduleDestroyAllActivities("always-finish");
-        }
-
-        if (allChanged) {
-            requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());
-        }
-
-        ArrayList<UidRecord> becameIdle = null;
-
-        // Update from any uid changes.
-        if (mLocalPowerManager != null) {
-            mLocalPowerManager.startUidChanges();
-        }
-        for (int i=mActiveUids.size()-1; i>=0; i--) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            int uidChange = UidRecord.CHANGE_PROCSTATE;
-            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.getCurProcState() + ", whitelist from " + uidRec.setWhitelist
-                        + " to " + uidRec.curWhitelist);
-                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)?
-                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
-                            || uidRec.setWhitelist) {
-                        uidRec.lastBackgroundTime = nowElapsed;
-                        if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {
-                            // Note: the background settle time is in elapsed realtime, while
-                            // the handler time base is uptime.  All this means is that we may
-                            // stop background uids later than we had intended, but that only
-                            // happens because the device was sleeping so we are okay anyway.
-                            mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
-                                    mConstants.BACKGROUND_SETTLE_TIME);
-                        }
-                    }
-                    if (uidRec.idle && !uidRec.setIdle) {
-                        uidChange = UidRecord.CHANGE_IDLE;
-                        if (becameIdle == null) {
-                            becameIdle = new ArrayList<>();
-                        }
-                        becameIdle.add(uidRec);
-                    }
-                } else {
-                    if (uidRec.idle) {
-                        uidChange = UidRecord.CHANGE_ACTIVE;
-                        EventLogTags.writeAmUidActive(uidRec.uid);
-                        uidRec.idle = false;
-                    }
-                    uidRec.lastBackgroundTime = 0;
-                }
-                final boolean wasCached = uidRec.setProcState
-                        > ActivityManager.PROCESS_STATE_RECEIVER;
-                final boolean isCached = uidRec.getCurProcState()
-                        > ActivityManager.PROCESS_STATE_RECEIVER;
-                if (wasCached != isCached || uidRec.setProcState == PROCESS_STATE_NONEXISTENT) {
-                    uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
-                }
-                uidRec.setProcState = uidRec.getCurProcState();
-                uidRec.setWhitelist = uidRec.curWhitelist;
-                uidRec.setIdle = uidRec.idle;
-                enqueueUidChangeLocked(uidRec, -1, uidChange);
-                noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
-                if (uidRec.foregroundServices) {
-                    mServices.foregroundServiceProcStateChangedLocked(uidRec);
-                }
-            }
-        }
-        if (mLocalPowerManager != null) {
-            mLocalPowerManager.finishUidChanges();
-        }
-
-        if (becameIdle != null) {
-            // If we have any new uids that became idle this time, we need to make sure
-            // they aren't left with running services.
-            for (int i = becameIdle.size() - 1; i >= 0; i--) {
-                mServices.stopInBackgroundLocked(becameIdle.get(i).uid);
-            }
-        }
-
-        if (mProcessStats.shouldWriteNowLocked(now)) {
-            mHandler.post(new ProcStatsRunnable(ActivityManagerService.this, mProcessStats));
-        }
-
-        // Run this after making sure all procstates are updated.
-        mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
-
-        if (DEBUG_OOM_ADJ) {
-            final long duration = SystemClock.uptimeMillis() - now;
-            if (false) {
-                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
-                        new RuntimeException("here").fillInStackTrace());
-            } else {
-                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
-            }
-        }
-        mOomAdjProfiler.oomAdjEnded();
+        mOomAdjuster.updateOomAdjLocked();
     }
 
     @Override
@@ -18165,9 +16408,9 @@
                     mLocalPowerManager.startUidChanges();
                 }
                 final int appId = UserHandle.getAppId(pkgUid);
-                final int N = mActiveUids.size();
-                for (int i=N-1; i>=0; i--) {
-                    final UidRecord uidRec = mActiveUids.valueAt(i);
+                final int N = mProcessList.mActiveUids.size();
+                for (int i = N - 1; i >= 0; i--) {
+                    final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
                     final long bgTime = uidRec.lastBackgroundTime;
                     if (bgTime > 0 && !uidRec.idle) {
                         if (UserHandle.getAppId(uidRec.uid) == appId) {
@@ -18192,49 +16435,17 @@
         }
     }
 
+    /** Make the currently active UIDs idle after a certain grace period. */
     final void idleUids() {
         synchronized (this) {
-            final int N = mActiveUids.size();
-            if (N <= 0) {
-                return;
-            }
-            final long nowElapsed = SystemClock.elapsedRealtime();
-            final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
-            long nextTime = 0;
-            if (mLocalPowerManager != null) {
-                mLocalPowerManager.startUidChanges();
-            }
-            for (int i=N-1; i>=0; i--) {
-                final UidRecord uidRec = mActiveUids.valueAt(i);
-                final long bgTime = uidRec.lastBackgroundTime;
-                if (bgTime > 0 && !uidRec.idle) {
-                    if (bgTime <= maxBgTime) {
-                        EventLogTags.writeAmUidIdle(uidRec.uid);
-                        uidRec.idle = true;
-                        uidRec.setIdle = true;
-                        doStopUidLocked(uidRec.uid, uidRec);
-                    } else {
-                        if (nextTime == 0 || nextTime > bgTime) {
-                            nextTime = bgTime;
-                        }
-                    }
-                }
-            }
-            if (mLocalPowerManager != null) {
-                mLocalPowerManager.finishUidChanges();
-            }
-            if (nextTime > 0) {
-                mHandler.removeMessages(IDLE_UIDS_MSG);
-                mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
-                        nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
-            }
+            mOomAdjuster.idleUidsLocked();
         }
     }
 
     /**
      * Checks if any uid is coming from background to foreground or vice versa and if so, increments
      * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter
-     * {@link #mProcStateSeqCounter} and notifies the app if it needs to block.
+     * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
      */
     @VisibleForTesting
     @GuardedBy("this")
@@ -18244,8 +16455,8 @@
         }
         // Used for identifying which uids need to block for network.
         ArrayList<Integer> blockingUids = null;
-        for (int i = mActiveUids.size() - 1; i >= 0; --i) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
+        for (int i = mProcessList.mActiveUids.size() - 1; i >= 0; --i) {
+            final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
             // If the network is not restricted for uid, then nothing to do here.
             if (!mInjector.isNetworkRestrictedForUid(uidRec.uid)) {
                 continue;
@@ -18293,13 +16504,15 @@
                 continue;
             }
             if (!app.killedByAm && app.thread != null) {
-                final UidRecord uidRec = mActiveUids.get(app.uid);
+                final UidRecord uidRec = mProcessList.getUidRecordLocked(app.uid);
                 try {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: "
                                 + uidRec);
                     }
-                    app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
+                    if (uidRec != null) {
+                        app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
@@ -18340,7 +16553,7 @@
 
     final void runInBackgroundDisabled(int uid) {
         synchronized (this) {
-            UidRecord uidRec = mActiveUids.get(uid);
+            UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
             if (uidRec != null) {
                 // This uid is actually running...  should it be considered background now?
                 if (uidRec.idle) {
@@ -18353,24 +16566,7 @@
         }
     }
 
-    /**
-     * Call {@link #doStopUidLocked} (which will also stop background services) for all idle UIDs.
-     */
-    void doStopUidForIdleUidsLocked() {
-        final int size = mActiveUids.size();
-        for (int i = 0; i < size; i++) {
-            final int uid = mActiveUids.keyAt(i);
-            if (UserHandle.isCore(uid)) {
-                continue;
-            }
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (!uidRec.idle) {
-                continue;
-            }
-            doStopUidLocked(uidRec.uid, uidRec);
-        }
-    }
-
+    @GuardedBy("this")
     final void doStopUidLocked(int uid, final UidRecord uidRec) {
         mServices.stopInBackgroundLocked(uid);
         enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
@@ -18454,27 +16650,12 @@
 
     @GuardedBy("this")
     final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
-        boolean changed = false;
-        for (int i=mActiveUids.size()-1; i>=0; i--) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) {
-                uidRec.curWhitelist = onWhitelist;
-                changed = true;
-            }
-        }
-        if (changed) {
-            updateOomAdjLocked();
-        }
+        mOomAdjuster.setAppIdTempWhitelistStateLocked(appId, onWhitelist);
     }
 
     @GuardedBy("this")
     final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
-        boolean changed = false;
-        final UidRecord uidRec = mActiveUids.get(uid);
-        if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
-            uidRec.curWhitelist = onWhitelist;
-            updateOomAdjLocked();
-        }
+        mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist);
     }
 
     final void trimApplications() {
@@ -19137,7 +17318,7 @@
             }
             UidRecord record;
             synchronized (ActivityManagerService.this) {
-                record = mActiveUids.get(uid);
+                record = mProcessList.getUidRecordLocked(uid);
                 if (record == null) {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "No active uidRecord for uid: " + uid
@@ -19468,8 +17649,8 @@
 
         @Override
         public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
-                boolean fgRequired, String callingPackage, int userId)
-                throws TransactionTooLargeException {
+                boolean fgRequired, String callingPackage, int userId,
+                boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
             synchronized(ActivityManagerService.this) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                         "startServiceInPackage: " + service + " type=" + resolvedType);
@@ -19477,7 +17658,8 @@
                 ComponentName res;
                 try {
                     res = mServices.startServiceLocked(null, service,
-                            resolvedType, -1, uid, fgRequired, callingPackage, userId);
+                            resolvedType, -1, uid, fgRequired, callingPackage, userId,
+                            allowBackgroundActivityStarts);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
@@ -19675,6 +17857,25 @@
                 return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.mountMode;
             }
         }
+
+        @Override
+        public boolean isAppForeground(int uid) {
+            return ActivityManagerService.this.isAppForeground(uid);
+        }
+
+        @Override
+        public void clearPendingBackup(int userId) {
+            ActivityManagerService.this.clearPendingBackup(userId);
+        }
+
+        /**
+         * When power button is very long pressed, call this interface to do some pre-shutdown work
+         * like persisting database etc.
+         */
+        @Override
+        public void prepareForPossibleShutdown() {
+            ActivityManagerService.this.prepareForPossibleShutdown();
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -19750,7 +17951,7 @@
         }
         UidRecord record;
         synchronized (this) {
-            record = mActiveUids.get(callingUid);
+            record = mProcessList.getUidRecordLocked(callingUid);
             if (record == null) {
                 return;
             }
@@ -19931,6 +18132,18 @@
         }
     }
 
+    /**
+     * When power button is very long pressed, call this interface to do some pre-shutdown work
+     * like persisting database etc.
+     */
+    public void prepareForPossibleShutdown() {
+        synchronized (this) {
+            if (mUsageStatsService != null) {
+                mUsageStatsService.prepareForPossibleShutdown();
+            }
+        }
+    }
+
     @VisibleForTesting
     public static class Injector {
         private NetworkManagementInternal mNmi;
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index aee16c3..fe27c49 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -44,7 +44,11 @@
      */
     final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>();
 
-    /*
+    static final int COMPACT_PROCESS_SOME = 1;
+    static final int COMPACT_PROCESS_FULL = 2;
+    static final int COMPACT_PROCESS_MSG = 1;
+
+    /**
      * This thread must be moved to the system background cpuset.
      * If that doesn't happen, it's probably going to draw a lot of power.
      * However, this has to happen after the first updateOomAdjLocked, because
@@ -53,13 +57,22 @@
      */
     final ServiceThread mCompactionThread;
 
-    static final int COMPACT_PROCESS_SOME = 1;
-    static final int COMPACT_PROCESS_FULL = 2;
-    static final int COMPACT_PROCESS_MSG = 1;
-    final Handler mCompactionHandler;
+    final private Handler mCompactionHandler;
 
-    final ActivityManagerService mAm;
-    final ActivityManagerConstants mConstants;
+    final private ActivityManagerService mAm;
+    final private ActivityManagerConstants mConstants;
+
+    final private String COMPACT_ACTION_FILE = "file";
+    final private String COMPACT_ACTION_ANON = "anon";
+    final private String COMPACT_ACTION_FULL = "full";
+
+    final private String compactActionSome;
+    final private String compactActionFull;
+
+    final private long throttleSomeSome;
+    final private long throttleSomeFull;
+    final private long throttleFullSome;
+    final private long throttleFullFull;
 
     public AppCompactor(ActivityManagerService am) {
         mAm = am;
@@ -69,6 +82,41 @@
                 THREAD_PRIORITY_FOREGROUND, true);
         mCompactionThread.start();
         mCompactionHandler = new MemCompactionHandler(this);
+
+        switch(mConstants.COMPACT_ACTION_1) {
+            case 1:
+                compactActionSome = COMPACT_ACTION_FILE;
+                break;
+            case 2:
+                compactActionSome = COMPACT_ACTION_ANON;
+                break;
+            case 3:
+                compactActionSome = COMPACT_ACTION_FULL;
+                break;
+            default:
+                compactActionSome = COMPACT_ACTION_FILE;
+                break;
+        }
+
+        switch(mConstants.COMPACT_ACTION_2) {
+            case 1:
+                compactActionFull = COMPACT_ACTION_FILE;
+                break;
+            case 2:
+                compactActionFull = COMPACT_ACTION_ANON;
+                break;
+            case 3:
+                compactActionFull = COMPACT_ACTION_FULL;
+                break;
+            default:
+                compactActionFull = COMPACT_ACTION_FULL;
+                break;
+        }
+
+        throttleSomeSome = mConstants.COMPACT_THROTTLE_1;
+        throttleSomeFull = mConstants.COMPACT_THROTTLE_2;
+        throttleFullSome = mConstants.COMPACT_THROTTLE_3;
+        throttleFullFull = mConstants.COMPACT_THROTTLE_4;
     }
 
     // Must be called while holding AMS lock.
@@ -128,18 +176,16 @@
                 }
 
                 // basic throttling
+                // use the ActivityManagerConstants knobs to determine whether current/prevous
+                // compaction combo should be throtted or not
                 if (pendingAction == COMPACT_PROCESS_SOME) {
-                    // if we're compacting some, then compact if >10s after last full
-                    // or >5s after last some
-                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) ||
-                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleSomeSome)) ||
+                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleSomeFull))) {
                         return;
                     }
                 } else {
-                    // if we're compacting full, then compact if >10s after last full
-                    // or >.5s after last some
-                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) ||
-                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleFullSome)) ||
+                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleFullFull))) {
                         return;
                     }
                 }
@@ -151,9 +197,9 @@
                     long[] rssBefore = Process.getRss(pid);
                     FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
                     if (pendingAction == COMPACT_PROCESS_SOME) {
-                        action = "file";
+                        action = compactActionSome;
                     } else {
-                        action = "all";
+                        action = compactActionFull;
                     }
                     fos.write(action.getBytes());
                     fos.close();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index f9a77af..d3953b5 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -67,8 +67,10 @@
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_ALL_APPS, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class);
-        sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_OUT_APPS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACKLIST, String.class);
         // add other global settings here...
     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
new file mode 100644
index 0000000..dc03369
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -0,0 +1,2104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.os.Process.SCHED_OTHER;
+import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
+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.setProcessGroup;
+import static android.os.Process.setThreadPriority;
+import static android.os.Process.setThreadScheduler;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ_REASON;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
+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.DEBUG_USAGE_STATS;
+import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG;
+import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESSES_CHANGED_UI_MSG;
+import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
+import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
+import static com.android.server.am.ActivityManagerService.TAG_LRU;
+import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerService.TAG_PROCESS_OBSERVERS;
+import static com.android.server.am.ActivityManagerService.TAG_PSS;
+import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerService.TOP_APP_PRIORITY_BOOST;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+
+import android.app.ActivityManager;
+import android.app.usage.UsageEvents;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.PowerManagerInternal;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityServiceConnectionsHolder;
+import com.android.server.wm.WindowProcessController;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * All of the code required to compute proc states and oom_adj values.
+ */
+public final class OomAdjuster {
+    private static final String TAG = "OomAdjuster";
+
+    /**
+     * For some direct access we need to power manager.
+     */
+    PowerManagerInternal mLocalPowerManager;
+
+    /**
+     * Service for compacting background apps.
+     */
+    AppCompactor mAppCompact;
+
+    ActivityManagerConstants mConstants;
+
+    final long[] mTmpLong = new long[3];
+
+    /**
+     * Current sequence id for oom_adj computation traversal.
+     */
+    int mAdjSeq = 0;
+
+    /**
+     * Keep track of the number of service processes we last found, to
+     * determine on the next iteration which should be B services.
+     */
+    int mNumServiceProcs = 0;
+    int mNewNumAServiceProcs = 0;
+    int mNewNumServiceProcs = 0;
+
+    /**
+     * Keep track of the non-cached/empty process we last found, to help
+     * determine how to distribute cached/empty processes next time.
+     */
+    int mNumNonCachedProcs = 0;
+
+    /**
+     * Keep track of the number of cached hidden procs, to balance oom adj
+     * distribution between those and empty procs.
+     */
+    int mNumCachedHiddenProcs = 0;
+
+    /** Track all uids that have actively running processes. */
+    ActiveUids mActiveUids;
+
+    private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
+
+    private final ActivityManagerService mService;
+    private final ProcessList mProcessList;
+
+    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
+        mService = service;
+        mProcessList = processList;
+        mActiveUids = activeUids;
+
+        mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
+        mConstants = mService.mConstants;
+        // mConstants can be null under test, which causes AppCompactor to crash
+        if (mConstants != null) {
+            mAppCompact = new AppCompactor(mService);
+        }
+    }
+
+    /**
+     * Update OomAdj for a specific process.
+     * @param app The process to update
+     * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps
+     *                  if necessary, or skip.
+     * @return whether updateOomAdjLocked(app) was successful.
+     */
+    @GuardedBy("mService")
+    final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
+        final ProcessRecord TOP_APP = mService.getTopAppLocked();
+        final boolean wasCached = app.cached;
+
+        mAdjSeq++;
+
+        // This is the desired cached adjusment we want to tell it to use.
+        // 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.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.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
+            // Changed to/from cached state, so apps after it in the LRU
+            // list may also be changed.
+            updateOomAdjLocked();
+        }
+        return success;
+    }
+
+    @GuardedBy("mService")
+    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+            ProcessRecord TOP_APP, boolean doingAll, long now) {
+        if (app.thread == null) {
+            return false;
+        }
+
+        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
+
+        return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
+    }
+
+    @GuardedBy("mService")
+    final void updateOomAdjLocked() {
+        mService.mOomAdjProfiler.oomAdjStarted();
+        final ProcessRecord TOP_APP = mService.getTopAppLocked();
+        final long now = SystemClock.uptimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
+        final int N = mProcessList.getLruSizeLocked();
+
+        // Reset state in all uid records.
+        for (int  i = mActiveUids.size() - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+                    "Starting update of " + uidRec);
+            uidRec.reset();
+        }
+
+        if (mService.mAtmInternal != null) {
+            mService.mAtmInternal.rankTaskLayersIfNeeded();
+        }
+
+        mAdjSeq++;
+        mNewNumServiceProcs = 0;
+        mNewNumAServiceProcs = 0;
+
+        final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
+        final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
+                - emptyProcessLimit;
+
+        // Let's determine how many processes we have running vs.
+        // how many slots we have for background processes; we may want
+        // to put multiple processes in a slot of there are enough of
+        // them.
+        final int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
+                - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2
+                / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
+        if (numEmptyProcs > cachedProcessLimit) {
+            // If there are more empty processes than our limit on cached
+            // processes, then use the cached process limit for the factor.
+            // This ensures that the really old empty processes get pushed
+            // down to the bottom, so if we are running low on memory we will
+            // have a better chance at keeping around more cached processes
+            // instead of a gazillion empty processes.
+            numEmptyProcs = cachedProcessLimit;
+        }
+        int emptyFactor = (numEmptyProcs + numSlots - 1) / numSlots;
+        if (emptyFactor < 1) emptyFactor = 1;
+        int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + numSlots - 1) : 1)
+                / numSlots;
+        if (cachedFactor < 1) cachedFactor = 1;
+        int stepCached = -1;
+        int stepEmpty = -1;
+        int numCached = 0;
+        int numCachedExtraGroup = 0;
+        int numEmpty = 0;
+        int numTrimming = 0;
+        int lastCachedGroup = 0;
+        int lastCachedGroupImportance = 0;
+        int lastCachedGroupUid = 0;
+
+        mNumNonCachedProcs = 0;
+        mNumCachedHiddenProcs = 0;
+
+        // First update the OOM adjustment for each of the
+        // application processes based on their current state.
+        int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
+        int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+        int curCachedImpAdj = 0;
+        int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+        int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+
+        boolean retryCycles = false;
+
+        // need to reset cycle state before calling computeOomAdjLocked because of service conns
+        for (int i = N - 1; i >= 0; i--) {
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
+            app.containsCycle = false;
+            app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+            app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
+        }
+        for (int i = N - 1; i >= 0; 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, false);
+
+                // if any app encountered a cycle, we need to perform an additional loop later
+                retryCycles |= app.containsCycle;
+
+                // If we haven't yet assigned the final cached adj
+                // to the process, do that now.
+                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
+                    switch (app.getCurProcState()) {
+                        case PROCESS_STATE_CACHED_ACTIVITY:
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                        case ActivityManager.PROCESS_STATE_CACHED_RECENT:
+                            // Figure out the next cached level, taking into account groups.
+                            boolean inGroup = false;
+                            if (app.connectionGroup != 0) {
+                                if (lastCachedGroupUid == app.uid
+                                        && lastCachedGroup == app.connectionGroup) {
+                                    // This is in the same group as the last process, just tweak
+                                    // adjustment by importance.
+                                    if (app.connectionImportance > lastCachedGroupImportance) {
+                                        lastCachedGroupImportance = app.connectionImportance;
+                                        if (curCachedAdj < nextCachedAdj
+                                                && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
+                                            curCachedImpAdj++;
+                                        }
+                                    }
+                                    inGroup = true;
+                                } else {
+                                    lastCachedGroupUid = app.uid;
+                                    lastCachedGroup = app.connectionGroup;
+                                    lastCachedGroupImportance = app.connectionImportance;
+                                }
+                            }
+                            if (!inGroup && curCachedAdj != nextCachedAdj) {
+                                stepCached++;
+                                curCachedImpAdj = 0;
+                                if (stepCached >= cachedFactor) {
+                                    stepCached = 0;
+                                    curCachedAdj = nextCachedAdj;
+                                    nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+                                    if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
+                                }
+                            }
+                            // This process is a cached process holding activities...
+                            // assign it the next cached value for that type, and then
+                            // step that cached level.
+                            app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+                            app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
+                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+                                    + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+                                    + " curCachedImpAdj=" + curCachedImpAdj + ")");
+                            break;
+                        default:
+                            // Figure out the next cached level.
+                            if (curEmptyAdj != nextEmptyAdj) {
+                                stepEmpty++;
+                                if (stepEmpty >= emptyFactor) {
+                                    stepEmpty = 0;
+                                    curEmptyAdj = nextEmptyAdj;
+                                    nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+                                    if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
+                                }
+                            }
+                            // For everything else, assign next empty cached process
+                            // level and bump that up.  Note that this means that
+                            // 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.setCurRawAdj(curEmptyAdj);
+                            app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+                                    + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+                                    + ")");
+                            break;
+                    }
+                }
+            }
+        }
+
+        // Cycle strategy:
+        // - Retry computing any process that has encountered a cycle.
+        // - Continue retrying until no process was promoted.
+        // - Iterate from least important to most important.
+        int cycleCount = 0;
+        while (retryCycles && cycleCount < 10) {
+            cycleCount++;
+            retryCycles = false;
+
+            for (int i = 0; i < N; i++) {
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
+                if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
+                    app.adjSeq--;
+                    app.completedAdjSeq--;
+                }
+            }
+
+            for (int i = 0; i < N; 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,
+                            true)) {
+                        retryCycles = true;
+                    }
+                }
+            }
+        }
+
+        lastCachedGroup = lastCachedGroupUid = 0;
+
+        for (int i = N - 1; i >= 0; 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.getCurProcState()) {
+                    case PROCESS_STATE_CACHED_ACTIVITY:
+                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                        mNumCachedHiddenProcs++;
+                        numCached++;
+                        if (app.connectionGroup != 0) {
+                            if (lastCachedGroupUid == app.uid
+                                    && lastCachedGroup == app.connectionGroup) {
+                                // If this process is the next in the same group, we don't
+                                // want it to count against our limit of the number of cached
+                                // processes, so bump up the group count to account for it.
+                                numCachedExtraGroup++;
+                            } else {
+                                lastCachedGroupUid = app.uid;
+                                lastCachedGroup = app.connectionGroup;
+                            }
+                        } else {
+                            lastCachedGroupUid = lastCachedGroup = 0;
+                        }
+                        if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
+                            app.kill("cached #" + numCached, true);
+                        }
+                        break;
+                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+                        if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
+                                && app.lastActivityTime < oldTime) {
+                            app.kill("empty for "
+                                    + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
+                                    / 1000) + "s", true);
+                        } else {
+                            numEmpty++;
+                            if (numEmpty > emptyProcessLimit) {
+                                app.kill("empty #" + numEmpty, true);
+                            }
+                        }
+                        break;
+                    default:
+                        mNumNonCachedProcs++;
+                        break;
+                }
+
+                if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) {
+                    // If this is an isolated process, there are no services
+                    // running in it, and it's not a special process with a
+                    // custom entry point, then the process is no longer
+                    // needed.  We agressively kill these because we can by
+                    // definition not re-use the same process again, and it is
+                    // good to avoid having whatever code was running in them
+                    // left sitting around after no longer needed.
+                    app.kill("isolated not needed", true);
+                } else {
+                    // Keeping this process, update its uid.
+                    final UidRecord uidRec = app.uidRecord;
+                    if (uidRec != null) {
+                        uidRec.ephemeral = app.info.isInstantApp();
+                        if (uidRec.getCurProcState() > app.getCurProcState()) {
+                            uidRec.setCurProcState(app.getCurProcState());
+                        }
+                        if (app.hasForegroundServices()) {
+                            uidRec.foregroundServices = true;
+                        }
+                    }
+                }
+
+                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
+                        && !app.killedByAm) {
+                    numTrimming++;
+                }
+            }
+        }
+
+        mService.incrementProcStateSeqAndNotifyAppsLocked();
+
+        mNumServiceProcs = mNewNumServiceProcs;
+
+        boolean allChanged = mService.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+
+        if (mService.mAlwaysFinishActivities) {
+            // Need to do this on its own message because the stack may not
+            // be in a consistent state at this point.
+            mService.mAtmInternal.scheduleDestroyAllActivities("always-finish");
+        }
+
+        if (allChanged) {
+            mService.requestPssAllProcsLocked(now, false,
+                    mService.mProcessStats.isMemFactorLowered());
+        }
+
+        ArrayList<UidRecord> becameIdle = null;
+
+        // Update from any uid changes.
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.startUidChanges();
+        }
+        for (int i = mActiveUids.size() - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            int uidChange = UidRecord.CHANGE_PROCSTATE;
+            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.getCurProcState() + ", whitelist from " + uidRec.setWhitelist
+                        + " to " + uidRec.curWhitelist);
+                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)?
+                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
+                            || uidRec.setWhitelist) {
+                        uidRec.lastBackgroundTime = nowElapsed;
+                        if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
+                            // Note: the background settle time is in elapsed realtime, while
+                            // the handler time base is uptime.  All this means is that we may
+                            // stop background uids later than we had intended, but that only
+                            // happens because the device was sleeping so we are okay anyway.
+                            mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+                                    mConstants.BACKGROUND_SETTLE_TIME);
+                        }
+                    }
+                    if (uidRec.idle && !uidRec.setIdle) {
+                        uidChange = UidRecord.CHANGE_IDLE;
+                        if (becameIdle == null) {
+                            becameIdle = new ArrayList<>();
+                        }
+                        becameIdle.add(uidRec);
+                    }
+                } else {
+                    if (uidRec.idle) {
+                        uidChange = UidRecord.CHANGE_ACTIVE;
+                        EventLogTags.writeAmUidActive(uidRec.uid);
+                        uidRec.idle = false;
+                    }
+                    uidRec.lastBackgroundTime = 0;
+                }
+                final boolean wasCached = uidRec.setProcState
+                        > ActivityManager.PROCESS_STATE_RECEIVER;
+                final boolean isCached = uidRec.getCurProcState()
+                        > ActivityManager.PROCESS_STATE_RECEIVER;
+                if (wasCached != isCached || uidRec.setProcState == PROCESS_STATE_NONEXISTENT) {
+                    uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
+                }
+                uidRec.setProcState = uidRec.getCurProcState();
+                uidRec.setWhitelist = uidRec.curWhitelist;
+                uidRec.setIdle = uidRec.idle;
+                mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
+                mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
+                if (uidRec.foregroundServices) {
+                    mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
+                }
+            }
+        }
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.finishUidChanges();
+        }
+
+        if (becameIdle != null) {
+            // If we have any new uids that became idle this time, we need to make sure
+            // they aren't left with running services.
+            for (int i = becameIdle.size() - 1; i >= 0; i--) {
+                mService.mServices.stopInBackgroundLocked(becameIdle.get(i).uid);
+            }
+        }
+
+        if (mService.mProcessStats.shouldWriteNowLocked(now)) {
+            mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
+                    mService.mProcessStats));
+        }
+
+        // Run this after making sure all procstates are updated.
+        mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+
+        if (DEBUG_OOM_ADJ) {
+            final long duration = SystemClock.uptimeMillis() - now;
+            if (false) {
+                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
+                        new RuntimeException("here").fillInStackTrace());
+            } else {
+                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
+            }
+        }
+        mService.mOomAdjProfiler.oomAdjEnded();
+    }
+
+    private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
+            new ComputeOomAdjWindowCallback();
+
+    /** These methods are called inline during computeOomAdjLocked(), on the same thread */
+    private final class ComputeOomAdjWindowCallback
+            implements WindowProcessController.ComputeOomAdjCallback {
+
+        ProcessRecord app;
+        int adj;
+        boolean foregroundActivities;
+        int procState;
+        int schedGroup;
+        int appUid;
+        int logUid;
+        int processStateCurTop;
+
+        void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
+                int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
+            this.app = app;
+            this.adj = adj;
+            this.foregroundActivities = foregroundActivities;
+            this.procState = procState;
+            this.schedGroup = schedGroup;
+            this.appUid = appUid;
+            this.logUid = logUid;
+            this.processStateCurTop = processStateCurTop;
+        }
+
+        @Override
+        public void onVisibleActivity() {
+            // App has a visible activity; only upgrade adjustment.
+            if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                adj = ProcessList.VISIBLE_APP_ADJ;
+                app.adjType = "vis-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
+                }
+            }
+            if (procState > processStateCurTop) {
+                procState = processStateCurTop;
+                app.adjType = "vis-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to vis-activity (top): " + app);
+                }
+            }
+            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onPausedActivity() {
+            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                app.adjType = "pause-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: "  + app);
+                }
+            }
+            if (procState > processStateCurTop) {
+                procState = processStateCurTop;
+                app.adjType = "pause-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to pause-activity (top): "  + app);
+                }
+            }
+            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onStoppingActivity(boolean finishing) {
+            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                app.adjType = "stop-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise adj to stop-activity: "  + app);
+                }
+            }
+
+            // For the process state, we will at this point consider the process to be cached. It
+            // will be cached either as an activity or empty depending on whether the activity is
+            // finishing. We do this so that we can treat the process as cached for purposes of
+            // memory trimming (determining current memory level, trim command to send to process)
+            // since there can be an arbitrary number of stopping processes and they should soon all
+            // go into the cached state.
+            if (!finishing) {
+                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                    procState = PROCESS_STATE_LAST_ACTIVITY;
+                    app.adjType = "stop-activity";
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise procstate to stop-activity: " + app);
+                    }
+                }
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onOtherActivity() {
+            if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
+                procState = PROCESS_STATE_CACHED_ACTIVITY;
+                app.adjType = "cch-act";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to cached activity: " + app);
+                }
+            }
+        }
+    }
+
+    private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
+            ProcessRecord TOP_APP, boolean doingAll, long now, boolean cycleReEval) {
+        if (mAdjSeq == app.adjSeq) {
+            if (app.adjSeq == app.completedAdjSeq) {
+                // This adjustment has already been computed successfully.
+                return false;
+            } else {
+                // The process is being computed, so there is a cycle. We cannot
+                // rely on this process's state.
+                app.containsCycle = true;
+
+                return false;
+            }
+        }
+
+        if (app.thread == null) {
+            app.adjSeq = mAdjSeq;
+            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
+            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;
+        }
+
+        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
+        app.adjSource = null;
+        app.adjTarget = null;
+        app.empty = false;
+        app.cached = false;
+
+        final WindowProcessController wpc = app.getWindowProcessController();
+        final int appUid = app.info.uid;
+        final int logUid = mService.mCurOomAdjUid;
+
+        int prevAppAdj = app.curAdj;
+        int prevProcState = app.getCurProcState();
+
+        if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
+            // The max adjustment doesn't allow this app to be anything
+            // below foreground, so it is not worth doing work for it.
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                mService.reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
+            }
+            app.adjType = "fixed";
+            app.adjSeq = mAdjSeq;
+            app.setCurRawAdj(app.maxAdj);
+            app.setHasForegroundActivities(false);
+            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+            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
+            // is currently showing UI.
+            app.systemNoUi = true;
+            if (app == TOP_APP) {
+                app.systemNoUi = false;
+                app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                app.adjType = "pers-top-activity";
+            } else if (app.hasTopUi()) {
+                // sched group/proc state adjustment is below
+                app.systemNoUi = false;
+                app.adjType = "pers-top-ui";
+            } else if (wpc.hasVisibleActivities()) {
+                app.systemNoUi = false;
+            }
+            if (!app.systemNoUi) {
+                if (mService.mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+                    // screen on, promote UI
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                } else {
+                    // screen off, restrict UI scheduling
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+                }
+            }
+            app.setCurRawProcState(app.getCurProcState());
+            app.curAdj = app.maxAdj;
+            app.completedAdjSeq = app.adjSeq;
+            // if curAdj is less than prevAppAdj, then this process was promoted
+            return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
+        }
+
+        app.systemNoUi = false;
+
+        final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState();
+
+        // Determine the importance of the process, starting with most
+        // important to least, and assign an appropriate OOM adjustment.
+        int adj;
+        int schedGroup;
+        int procState;
+        int cachedAdjSeq;
+
+        boolean foregroundActivities = false;
+        mTmpBroadcastQueue.clear();
+        if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
+            // The last app on the list is the foreground app.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+            app.adjType = "top-activity";
+            foregroundActivities = true;
+            procState = PROCESS_STATE_CUR_TOP;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
+            }
+        } else if (app.runningRemoteAnimation) {
+            adj = ProcessList.VISIBLE_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+            app.adjType = "running-remote-anim";
+            procState = PROCESS_STATE_CUR_TOP;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
+            }
+        } else if (app.getActiveInstrumentation() != null) {
+            // Don't want to kill running instrumentation.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            app.adjType = "instrumentation";
+            procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
+            }
+        } else if (mService.isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
+            // An app that is currently receiving a broadcast also
+            // counts as being in the foreground for OOM killer purposes.
+            // It's placed in a sched group based on the nature of the
+            // broadcast as reflected by which queue it's active in.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue))
+                    ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+            app.adjType = "broadcast";
+            procState = ActivityManager.PROCESS_STATE_RECEIVER;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
+            }
+        } else if (app.executingServices.size() > 0) {
+            // An app that is currently executing a service callback also
+            // counts as being in the foreground.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = app.execServicesFg ?
+                    ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+            app.adjType = "exec-service";
+            procState = ActivityManager.PROCESS_STATE_SERVICE;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
+            }
+            //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
+        } else if (app == TOP_APP) {
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+            app.adjType = "top-sleeping";
+            foregroundActivities = true;
+            procState = PROCESS_STATE_CUR_TOP;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app);
+            }
+        } else {
+            // As far as we know the process is empty.  We may change our mind later.
+            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+            // At this point we don't actually know the adjustment.  Use the cached adj
+            // value that the caller wants us to.
+            adj = cachedAdj;
+            procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+            app.cached = true;
+            app.empty = true;
+            app.adjType = "cch-empty";
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
+            }
+        }
+
+        // Examine all activities if not already foreground.
+        if (!foregroundActivities && wpc.hasActivities()) {
+            mTmpComputeOomAdjWindowCallback.initialize(app, adj, foregroundActivities, procState,
+                    schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP);
+            final int minLayer = wpc.computeOomAdjFromActivities(
+                    ProcessList.VISIBLE_APP_LAYER_MAX, mTmpComputeOomAdjWindowCallback);
+
+            adj = mTmpComputeOomAdjWindowCallback.adj;
+            foregroundActivities = mTmpComputeOomAdjWindowCallback.foregroundActivities;
+            procState = mTmpComputeOomAdjWindowCallback.procState;
+            schedGroup = mTmpComputeOomAdjWindowCallback.schedGroup;
+
+            if (adj == ProcessList.VISIBLE_APP_ADJ) {
+                adj += minLayer;
+            }
+        }
+
+        if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
+            procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
+            app.adjType = "cch-rec";
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
+            }
+        }
+
+        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
+                || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            if (app.hasForegroundServices()) {
+                // The user is aware of this app, so make it visible.
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+                app.cached = false;
+                app.adjType = "fg-service";
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app);
+                }
+            } else if (app.hasOverlayUi()) {
+                // The process is display an overlay UI.
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                app.cached = false;
+                app.adjType = "has-overlay-ui";
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app);
+                }
+            }
+        }
+
+        // If the app was recently in the foreground and moved to a foreground service status,
+        // allow it to get a higher rank in memory for some time, compared to other foreground
+        // services so that it can finish performing any persistence/processing of in-memory state.
+        if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
+                && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
+                || app.setProcState <= ActivityManager.PROCESS_STATE_TOP)) {
+            adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
+            app.adjType = "fg-service-act";
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
+            }
+        }
+
+        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
+                || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+            if (app.forcingToImportant != null) {
+                // This is currently used for toasts...  they are not interactive, and
+                // we don't want them to cause the app to become fully foreground (and
+                // thus out of background check), so we yes the best background level we can.
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+                app.cached = false;
+                app.adjType = "force-imp";
+                app.adjSource = app.forcingToImportant;
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
+                }
+            }
+        }
+
+        if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
+            if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                // We don't want to kill the current heavy-weight process.
+                adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "heavy";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
+                }
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+                procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+                app.adjType = "heavy";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
+                }
+            }
+        }
+
+        if (wpc.isHomeProcess()) {
+            if (adj > ProcessList.HOME_APP_ADJ) {
+                // This process is hosting what we currently consider to be the
+                // home app, so we don't want to let it go into the background.
+                adj = ProcessList.HOME_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "home";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
+                }
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HOME) {
+                procState = ActivityManager.PROCESS_STATE_HOME;
+                app.adjType = "home";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
+                }
+            }
+        }
+
+        if (wpc.isPreviousProcess() && app.hasActivities()) {
+            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+                // This was the previous process that showed UI to the user.
+                // We want to try to keep it around more aggressively, to give
+                // a good experience around switching between two apps.
+                adj = ProcessList.PREVIOUS_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "previous";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
+                }
+            }
+            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                procState = PROCESS_STATE_LAST_ACTIVITY;
+                app.adjType = "previous";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
+                }
+            }
+        }
+
+        if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
+                + " reason=" + app.adjType);
+
+        // By default, we use the computed adjustment.  It may be changed if
+        // 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. If we're re-evaluating due to cycles, use the previously computed
+        // values.
+        app.setCurRawAdj(!cycleReEval ? adj : Math.min(adj, app.getCurRawAdj()));
+        app.setCurRawProcState(!cycleReEval
+                ? procState
+                : Math.min(procState, app.getCurRawProcState()));
+
+        app.hasStartedServices = false;
+        app.adjSeq = mAdjSeq;
+
+        final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
+        if (backupTarget != null && app == backupTarget.app) {
+            // If possible we want to avoid killing apps while they're being backed up
+            if (adj > ProcessList.BACKUP_APP_ADJ) {
+                if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
+                adj = ProcessList.BACKUP_APP_ADJ;
+                if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+                }
+                app.adjType = "backup";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
+                }
+                app.cached = false;
+            }
+            if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
+                procState = ActivityManager.PROCESS_STATE_BACKUP;
+                app.adjType = "backup";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
+                }
+            }
+        }
+
+        boolean mayBeTop = false;
+        String mayBeTopType = null;
+        Object mayBeTopSource = null;
+        Object mayBeTopTarget = null;
+
+        for (int is = app.services.size() - 1;
+                is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                        || procState > ActivityManager.PROCESS_STATE_TOP);
+                is--) {
+            ServiceRecord s = app.services.valueAt(is);
+            if (s.startRequested) {
+                app.hasStartedServices = true;
+                if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
+                    procState = ActivityManager.PROCESS_STATE_SERVICE;
+                    app.adjType = "started-services";
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise procstate to started service: " + app);
+                    }
+                }
+                if (app.hasShownUi && !wpc.isHomeProcess()) {
+                    // If this process has shown some UI, let it immediately
+                    // go to the LRU list because it may be pretty heavy with
+                    // UI stuff.  We'll tag it with a label just to help
+                    // debug and understand what is going on.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-ui-services";
+                    }
+                } else {
+                    if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
+                        // This service has seen some activity within
+                        // recent memory, so we will keep its process ahead
+                        // of the background processes.
+                        if (adj > ProcessList.SERVICE_ADJ) {
+                            adj = ProcessList.SERVICE_ADJ;
+                            app.adjType = "started-services";
+                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                        "Raise adj to started service: " + app);
+                            }
+                            app.cached = false;
+                        }
+                    }
+                    // If we have let the service slide into the background
+                    // state, still have some text describing what it is doing
+                    // even though the service no longer has an impact.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-services";
+                    }
+                }
+            }
+
+            for (int conni = s.connections.size() - 1;
+                    conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                            || procState > ActivityManager.PROCESS_STATE_TOP);
+                    conni--) {
+                ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
+                for (int i = 0;
+                        i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
+                                || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                                || procState > ActivityManager.PROCESS_STATE_TOP);
+                        i++) {
+                    // XXX should compute this based on the max of
+                    // all connected clients.
+                    ConnectionRecord cr = clist.get(i);
+                    if (cr.binding.client == app) {
+                        // Binding to oneself is not interesting.
+                        continue;
+                    }
+
+                    boolean trackedProcState = false;
+                    if ((cr.flags& Context.BIND_WAIVE_PRIORITY) == 0) {
+                        ProcessRecord client = cr.binding.client;
+                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                            continue;
+                        }
+
+                        int clientAdj = client.getCurRawAdj();
+                        int clientProcState = client.getCurRawProcState();
+
+                        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
+                            // doesn't propagate except under certain conditions.
+                            clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                        }
+                        String adjType = null;
+                        if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+                            // Not doing bind OOM management, so treat
+                            // this guy more like a started service.
+                            if (app.hasShownUi && !wpc.isHomeProcess()) {
+                                // If this process has shown some UI, let it immediately
+                                // go to the LRU list because it may be pretty heavy with
+                                // UI stuff.  We'll tag it with a label just to help
+                                // debug and understand what is going on.
+                                if (adj > clientAdj) {
+                                    adjType = "cch-bound-ui-services";
+                                }
+                                app.cached = false;
+                                clientAdj = adj;
+                                clientProcState = procState;
+                            } else {
+                                if (now >= (s.lastActivity
+                                        + mConstants.MAX_SERVICE_INACTIVITY)) {
+                                    // This service has not seen activity within
+                                    // recent memory, so allow it to drop to the
+                                    // LRU list if there is no other reason to keep
+                                    // it around.  We'll also tag it with a label just
+                                    // to help debug and undertand what is going on.
+                                    if (adj > clientAdj) {
+                                        adjType = "cch-bound-services";
+                                    }
+                                    clientAdj = adj;
+                                }
+                            }
+                        }
+                        if (adj > clientAdj) {
+                            // If this process has recently shown UI, and
+                            // the process that is binding to it is less
+                            // important than being visible, then we don't
+                            // care about the binding as much as we care
+                            // about letting this process get into the LRU
+                            // list to be killed and restarted if needed for
+                            // memory.
+                            if (app.hasShownUi && !wpc.isHomeProcess()
+                                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                                    adjType = "cch-bound-ui-services";
+                                }
+                            } else {
+                                int newAdj;
+                                if ((cr.flags&(Context.BIND_ABOVE_CLIENT
+                                        |Context.BIND_IMPORTANT)) != 0) {
+                                    if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
+                                        newAdj = clientAdj;
+                                    } else {
+                                        // make this service persistent
+                                        newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+                                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                                        procState = ActivityManager.PROCESS_STATE_PERSISTENT;
+                                        cr.trackProcState(procState, mAdjSeq, now);
+                                        trackedProcState = true;
+                                    }
+                                } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0
+                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ + 1) {
+                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ + 1;
+                                } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
+                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                                } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                    newAdj = clientAdj;
+                                } else {
+                                    if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                                        newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
+                                    } else {
+                                        newAdj = adj;
+                                    }
+                                }
+                                if (!client.cached) {
+                                    app.cached = false;
+                                }
+                                if (adj >  newAdj) {
+                                    adj = newAdj;
+                                    app.setCurRawAdj(adj);
+                                    adjType = "service";
+                                }
+                            }
+                        }
+                        if ((cr.flags & (Context.BIND_NOT_FOREGROUND
+                                | Context.BIND_IMPORTANT_BACKGROUND)) == 0) {
+                            // This will treat important bound services identically to
+                            // the top app, which may behave differently than generic
+                            // foreground work.
+                            final int curSchedGroup = client.getCurrentSchedulingGroup();
+                            if (curSchedGroup > schedGroup) {
+                                if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
+                                    schedGroup = curSchedGroup;
+                                } else {
+                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                                }
+                            }
+                            if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
+                                if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
+                                    // Special handling of clients who are in the top state.
+                                    // We *may* want to consider this process to be in the
+                                    // top state as well, but only if there is not another
+                                    // reason for it to be running.  Being on the top is a
+                                    // special state, meaning you are specifically running
+                                    // for the current top app.  If the process is already
+                                    // running in the background for some other reason, it
+                                    // is more important to continue considering it to be
+                                    // in the background state.
+                                    mayBeTop = true;
+                                    mayBeTopType = "service";
+                                    mayBeTopSource = cr.binding.client;
+                                    mayBeTopTarget = s.instanceName;
+                                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                                } else {
+                                    // Special handling for above-top states (persistent
+                                    // processes).  These should not bring the current process
+                                    // into the top state, since they are not on top.  Instead
+                                    // give them the best state after that.
+                                    if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
+                                        clientProcState =
+                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                                    } else if (mService.mWakefulness
+                                            == PowerManagerInternal.WAKEFULNESS_AWAKE &&
+                                            (cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
+                                                    != 0) {
+                                        clientProcState =
+                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                                    } else {
+                                        clientProcState =
+                                                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                                    }
+                                }
+                            }
+                        } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+                            }
+                        } else {
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                            }
+                        }
+                        if (!trackedProcState) {
+                            cr.trackProcState(clientProcState, mAdjSeq, now);
+                        }
+                        if (procState > clientProcState) {
+                            procState = clientProcState;
+                            app.setCurRawProcState(procState);
+                            if (adjType == null) {
+                                adjType = "service";
+                            }
+                        }
+                        if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                                && (cr.flags & Context.BIND_SHOWING_UI) != 0) {
+                            app.setPendingUiClean(true);
+                        }
+                        if (adjType != null) {
+                            app.adjType = adjType;
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = cr.binding.client;
+                            app.adjSourceProcState = clientProcState;
+                            app.adjTarget = s.instanceName;
+                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+                                        + ": " + app + ", due to " + cr.binding.client
+                                        + " adj=" + adj + " procState="
+                                        + ProcessList.makeProcStateString(procState));
+                            }
+                        }
+                    }
+                    if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+                        app.treatLikeActivity = true;
+                    }
+                    final ActivityServiceConnectionsHolder a = cr.activity;
+                    if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+                        if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
+                                && a.isActivityVisible()) {
+                            adj = ProcessList.FOREGROUND_APP_ADJ;
+                            app.setCurRawAdj(adj);
+                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
+                                    schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
+                                } else {
+                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                                }
+                            }
+                            app.cached = false;
+                            app.adjType = "service";
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = a;
+                            app.adjSourceProcState = procState;
+                            app.adjTarget = s.instanceName;
+                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                        "Raise to service w/activity: " + app);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        for (int provi = app.pubProviders.size() - 1;
+                provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                        || procState > ActivityManager.PROCESS_STATE_TOP);
+                provi--) {
+            ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
+            for (int i = cpr.connections.size() - 1;
+                    i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                            || procState > ActivityManager.PROCESS_STATE_TOP);
+                    i--) {
+                ContentProviderConnection conn = cpr.connections.get(i);
+                ProcessRecord client = conn.client;
+                if (client == app) {
+                    // Being our own client is not interesting.
+                    continue;
+                }
+                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                    continue;
+                }
+
+                int clientAdj = client.getCurRawAdj();
+                int clientProcState = client.getCurRawProcState();
+
+                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.
+                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                }
+                String adjType = null;
+                if (adj > clientAdj) {
+                    if (app.hasShownUi && !wpc.isHomeProcess()
+                            && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                        adjType = "cch-ui-provider";
+                    } else {
+                        adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
+                                ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+                        app.setCurRawAdj(adj);
+                        adjType = "provider";
+                    }
+                    app.cached &= client.cached;
+                }
+                if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
+                    if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
+                        // Special handling of clients who are in the top state.
+                        // We *may* want to consider this process to be in the
+                        // top state as well, but only if there is not another
+                        // reason for it to be running.  Being on the top is a
+                        // special state, meaning you are specifically running
+                        // for the current top app.  If the process is already
+                        // running in the background for some other reason, it
+                        // is more important to continue considering it to be
+                        // in the background state.
+                        mayBeTop = true;
+                        clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                        mayBeTopType = adjType = "provider-top";
+                        mayBeTopSource = client;
+                        mayBeTopTarget = cpr.name;
+                    } else {
+                        // Special handling for above-top states (persistent
+                        // processes).  These should not bring the current process
+                        // into the top state, since they are not on top.  Instead
+                        // give them the best state after that.
+                        clientProcState =
+                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                        if (adjType == null) {
+                            adjType = "provider";
+                        }
+                    }
+                }
+                conn.trackProcState(clientProcState, mAdjSeq, now);
+                if (procState > clientProcState) {
+                    procState = clientProcState;
+                    app.setCurRawProcState(procState);
+                }
+                if (client.getCurrentSchedulingGroup() > schedGroup) {
+                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                }
+                if (adjType != null) {
+                    app.adjType = adjType;
+                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                            .REASON_PROVIDER_IN_USE;
+                    app.adjSource = client;
+                    app.adjSourceProcState = clientProcState;
+                    app.adjTarget = cpr.name;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+                                + ": " + app + ", due to " + client
+                                + " adj=" + adj + " procState="
+                                + ProcessList.makeProcStateString(procState));
+                    }
+                }
+            }
+            // If the provider has external (non-framework) process
+            // dependencies, ensure that its adjustment is at least
+            // FOREGROUND_APP_ADJ.
+            if (cpr.hasExternalProcessHandles()) {
+                if (adj > ProcessList.FOREGROUND_APP_ADJ) {
+                    adj = ProcessList.FOREGROUND_APP_ADJ;
+                    app.setCurRawAdj(adj);
+                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                    app.cached = false;
+                    app.adjType = "ext-provider";
+                    app.adjTarget = cpr.name;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise adj to external provider: " + app);
+                    }
+                }
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                    app.setCurRawProcState(procState);
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise procstate to external provider: " + app);
+                    }
+                }
+            }
+        }
+
+        if (app.lastProviderTime > 0 &&
+                (app.lastProviderTime + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
+            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+                adj = ProcessList.PREVIOUS_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "recent-provider";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise adj to recent provider: " + app);
+                }
+            }
+            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                procState = PROCESS_STATE_LAST_ACTIVITY;
+                app.adjType = "recent-provider";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to recent provider: " + app);
+                }
+            }
+        }
+
+        if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
+            // A client of one of our services or providers is in the top state.  We
+            // *may* want to be in the top state, but not if we are already running in
+            // the background for some other reason.  For the decision here, we are going
+            // to pick out a few specific states that we want to remain in when a client
+            // is top (states that tend to be longer-term) and otherwise allow it to go
+            // to the top state.
+            switch (procState) {
+                case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+                case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
+                    // Something else is keeping it at this level, just leave it.
+                    break;
+                case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+                case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+                case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
+                case ActivityManager.PROCESS_STATE_SERVICE:
+                    // These all are longer-term states, so pull them up to the top
+                    // of the background states, but not all the way to the top state.
+                    procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                    app.adjType = mayBeTopType;
+                    app.adjSource = mayBeTopSource;
+                    app.adjTarget = mayBeTopTarget;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
+                                + ": " + app + ", due to " + mayBeTopSource
+                                + " adj=" + adj + " procState="
+                                + ProcessList.makeProcStateString(procState));
+                    }
+                    break;
+                default:
+                    // Otherwise, top is a better choice, so take it.
+                    procState = ActivityManager.PROCESS_STATE_TOP;
+                    app.adjType = mayBeTopType;
+                    app.adjSource = mayBeTopSource;
+                    app.adjTarget = mayBeTopTarget;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
+                                + ": " + app + ", due to " + mayBeTopSource
+                                + " adj=" + adj + " procState="
+                                + ProcessList.makeProcStateString(procState));
+                    }
+                    break;
+            }
+        }
+
+        if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+            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";
+            } else if (app.treatLikeActivity) {
+                // This is a cached process, but somebody wants us to treat it like it has
+                // an activity, okay!
+                procState = PROCESS_STATE_CACHED_ACTIVITY;
+                app.adjType = "cch-as-act";
+            }
+        }
+
+        if (adj == ProcessList.SERVICE_ADJ) {
+            if (doingAll) {
+                app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
+                mNewNumServiceProcs++;
+                //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
+                if (!app.serviceb) {
+                    // This service isn't far enough down on the LRU list to
+                    // normally be a B service, but if we are low on RAM and it
+                    // is large we want to force it down since we would prefer to
+                    // keep launcher over it.
+                    if (mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                            && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
+                        app.serviceHighRam = true;
+                        app.serviceb = true;
+                        //Slog.i(TAG, "ADJ " + app + " high ram!");
+                    } else {
+                        mNewNumAServiceProcs++;
+                        //Slog.i(TAG, "ADJ " + app + " not high ram!");
+                    }
+                } else {
+                    app.serviceHighRam = false;
+                }
+            }
+            if (app.serviceb) {
+                adj = ProcessList.SERVICE_B_ADJ;
+            }
+        }
+
+        app.setCurRawAdj(adj);
+
+        //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
+        //      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
+        if (adj > app.maxAdj) {
+            adj = app.maxAdj;
+            if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+        }
+
+        // Put bound foreground services in a special sched group for additional
+        // restrictions on screen off
+        if (procState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE &&
+                mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
+            if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
+                schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
+            }
+        }
+
+        // Do final modification to adj.  Everything we do between here and applying
+        // the final setAdj must be done in this function, because we will also use
+        // it when computing the final cached adj later.  Note that we don't need to
+        // worry about this for max adj above, since max adj will always be used to
+        // keep it out of the cached vaues.
+        app.curAdj = app.modifyRawOomAdj(adj);
+        app.setCurrentSchedulingGroup(schedGroup);
+        app.setCurProcState(procState);
+        app.setCurRawProcState(procState);
+        app.setHasForegroundActivities(foregroundActivities);
+        app.completedAdjSeq = mAdjSeq;
+
+        // if curAdj or curProcState improved, then this process was promoted
+        return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
+    }
+
+    /**
+     * Checks if for the given app and client, there's a cycle that should skip over the client
+     * for now or use partial values to evaluate the effect of the client binding.
+     * @param app
+     * @param client
+     * @param procState procstate evaluated so far for this app
+     * @param adj oom_adj evaluated so far for this app
+     * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
+     *                    evaluation.
+     * @return whether to skip using the client connection at this time
+     */
+    private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+            int procState, int adj, boolean cycleReEval) {
+        if (client.containsCycle) {
+            // We've detected a cycle. We should retry computeOomAdjLocked later in
+            // case a later-checked connection from a client  would raise its
+            // priority legitimately.
+            app.containsCycle = true;
+            // If the client has not been completely evaluated, check if it's worth
+            // using the partial values.
+            if (client.completedAdjSeq < mAdjSeq) {
+                if (cycleReEval) {
+                    // If the partial values are no better, skip until the next
+                    // attempt
+                    if (client.getCurRawProcState() >= procState
+                            && client.getCurRawAdj() >= adj) {
+                        return true;
+                    }
+                    // Else use the client's partial procstate and adj to adjust the
+                    // effect of the binding
+                } else {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Inform the oomadj observer of changes to oomadj. Used by tests. */
+    @GuardedBy("mService")
+    void reportOomAdjMessageLocked(String tag, String msg) {
+        Slog.d(tag, msg);
+        if (mService.mCurOomAdjObserver != null) {
+            mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
+        }
+    }
+
+    /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
+    @GuardedBy("mService")
+    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
+            long nowElapsed) {
+        boolean success = true;
+
+        if (app.getCurRawAdj() != app.setRawAdj) {
+            app.setRawAdj = app.getCurRawAdj();
+        }
+
+        int changes = 0;
+
+        if (app.curAdj != app.setAdj) {
+            // don't compact during bootup
+            if (mConstants.USE_COMPACTION && mService.mBooted) {
+                // Perform a minor compaction when a perceptible app becomes the prev/home app
+                // Perform a major compaction when any app enters cached
+                // reminder: here, setAdj is previous state, curAdj is upcoming state
+                if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
+                        (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
+                                app.curAdj == ProcessList.HOME_APP_ADJ)) {
+                    mAppCompact.compactAppSome(app);
+                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
+                        app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                    mAppCompact.compactAppFull(app);
+                }
+            }
+            ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
+                String msg = "Set " + app.pid + " " + app.processName + " adj "
+                        + app.curAdj + ": " + app.adjType;
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+            }
+            app.setAdj = app.curAdj;
+            app.verifiedAdj = ProcessList.INVALID_ADJ;
+        }
+
+        final int curSchedGroup = app.getCurrentSchedulingGroup();
+        if (app.setSchedGroup != curSchedGroup) {
+            int oldSchedGroup = app.setSchedGroup;
+            app.setSchedGroup = curSchedGroup;
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
+                String msg = "Setting sched group of " + app.processName
+                        + " to " + curSchedGroup + ": " + app.adjType;
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+            }
+            if (app.waitingToKill != null && app.curReceivers.isEmpty()
+                    && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
+                app.kill(app.waitingToKill, true);
+                success = false;
+            } else {
+                int processGroup;
+                switch (curSchedGroup) {
+                    case ProcessList.SCHED_GROUP_BACKGROUND:
+                        processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
+                        break;
+                    case ProcessList.SCHED_GROUP_TOP_APP:
+                    case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
+                        processGroup = THREAD_GROUP_TOP_APP;
+                        break;
+                    case ProcessList.SCHED_GROUP_RESTRICTED:
+                        processGroup = THREAD_GROUP_RESTRICTED;
+                        break;
+                    default:
+                        processGroup = THREAD_GROUP_DEFAULT;
+                        break;
+                }
+                long oldId = Binder.clearCallingIdentity();
+                try {
+                    setProcessGroup(app.pid, processGroup);
+                    if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+                        // do nothing if we already switched to RT
+                        if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+                            app.getWindowProcessController().onTopProcChanged();
+                            if (mService.mUseFifoUiScheduling) {
+                                // Switch UI pipeline for app to SCHED_FIFO
+                                app.savedPriority = Process.getThreadPriority(app.pid);
+                                mService.scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
+                                if (app.renderThreadTid != 0) {
+                                    mService.scheduleAsFifoPriority(app.renderThreadTid,
+                                            /* suppressLogs */true);
+                                    if (DEBUG_OOM_ADJ) {
+                                        Slog.d("UI_FIFO", "Set RenderThread (TID " +
+                                                app.renderThreadTid + ") to FIFO");
+                                    }
+                                } else {
+                                    if (DEBUG_OOM_ADJ) {
+                                        Slog.d("UI_FIFO", "Not setting RenderThread TID");
+                                    }
+                                }
+                            } else {
+                                // Boost priority for top app UI and render threads
+                                setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
+                                if (app.renderThreadTid != 0) {
+                                    try {
+                                        setThreadPriority(app.renderThreadTid,
+                                                TOP_APP_PRIORITY_BOOST);
+                                    } catch (IllegalArgumentException e) {
+                                        // thread died, ignore
+                                    }
+                                }
+                            }
+                        }
+                    } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+                            curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+                        app.getWindowProcessController().onTopProcChanged();
+                        if (mService.mUseFifoUiScheduling) {
+                            try {
+                                // Reset UI pipeline to SCHED_OTHER
+                                setThreadScheduler(app.pid, SCHED_OTHER, 0);
+                                setThreadPriority(app.pid, app.savedPriority);
+                                if (app.renderThreadTid != 0) {
+                                    setThreadScheduler(app.renderThreadTid,
+                                            SCHED_OTHER, 0);
+                                    setThreadPriority(app.renderThreadTid, -4);
+                                }
+                            } catch (IllegalArgumentException e) {
+                                Slog.w(TAG,
+                                        "Failed to set scheduling policy, thread does not exist:\n"
+                                                + e);
+                            } catch (SecurityException e) {
+                                Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
+                            }
+                        } else {
+                            // Reset priority for top app UI and render threads
+                            setThreadPriority(app.pid, 0);
+                            if (app.renderThreadTid != 0) {
+                                setThreadPriority(app.renderThreadTid, 0);
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    if (false) {
+                        Slog.w(TAG, "Failed setting process group of " + app.pid
+                                + " to " + app.getCurrentSchedulingGroup());
+                        Slog.w(TAG, "at location", e);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(oldId);
+                }
+            }
+        }
+        if (app.repForegroundActivities != app.hasForegroundActivities()) {
+            app.repForegroundActivities = app.hasForegroundActivities();
+            changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
+        }
+        if (app.getReportedProcState() != app.getCurProcState()) {
+            app.setReportedProcState(app.getCurProcState());
+            if (app.thread != null) {
+                try {
+                    if (false) {
+                        //RuntimeException h = new RuntimeException("here");
+                        Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
+                                + " to " + app /*, h*/);
+                    }
+                    app.thread.setProcessState(app.getReportedProcState());
+                } catch (RemoteException e) {
+                }
+            }
+        }
+        if (app.setProcState == PROCESS_STATE_NONEXISTENT
+                || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
+            if (false && mService.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
+                // the data right when a process is transitioning between process
+                // states, which will tend to give noisy data.
+                long start = SystemClock.uptimeMillis();
+                long startTime = SystemClock.currentThreadTimeMillis();
+                long pss = Debug.getPss(app.pid, mTmpLong, null);
+                long endTime = SystemClock.currentThreadTimeMillis();
+                mService.recordPssSampleLocked(app, app.getCurProcState(), pss,
+                        mTmpLong[0], mTmpLong[1], mTmpLong[2],
+                        ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
+                mService.mPendingPssProcesses.remove(app);
+                Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
+                        + " to " + app.getCurProcState() + ": "
+                        + (SystemClock.uptimeMillis()-start) + "ms");
+            }
+            app.lastStateTime = now;
+            app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
+                    app.procStateMemTracker, mService.mTestPssMode,
+                    mService.mAtmInternal.isSleeping(), now);
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
+                    + ProcessList.makeProcStateString(app.setProcState) + " to "
+                    + 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(
+                    mService.mTestPssMode)))) {
+                if (mService.requestPssLocked(app, app.setProcState)) {
+                    app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
+                            app.procStateMemTracker, mService.mTestPssMode,
+                            mService.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.getCurProcState()) {
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
+                String msg = "Proc state change of " + app.processName
+                        + " 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.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.setWhenUnimportant(now);
+                app.lastCpuTime = 0;
+            }
+            // Inform UsageStats of important process state change
+            // Must be called before updating setProcState
+            maybeUpdateUsageStatsLocked(app, nowElapsed);
+
+            maybeUpdateLastTopTime(app, now);
+
+            app.setProcState = app.getCurProcState();
+            if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
+                app.notCachedSinceIdle = false;
+            }
+            if (!doingAll) {
+                mService.setProcessTrackerStateLocked(app,
+                        mService.mProcessStats.getMemFactorLocked(), now);
+            } else {
+                app.procStateChanged = true;
+            }
+        } 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.
+            maybeUpdateUsageStatsLocked(app, nowElapsed);
+        }
+
+        if (changes != 0) {
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                    "Changes in " + app + ": " + changes);
+            int i = mService.mPendingProcessChanges.size()-1;
+            ActivityManagerService.ProcessChangeItem item = null;
+            while (i >= 0) {
+                item = mService.mPendingProcessChanges.get(i);
+                if (item.pid == app.pid) {
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Re-using existing item: " + item);
+                    break;
+                }
+                i--;
+            }
+            if (i < 0) {
+                // No existing item in pending changes; need a new one.
+                final int NA = mService.mAvailProcessChanges.size();
+                if (NA > 0) {
+                    item = mService.mAvailProcessChanges.remove(NA-1);
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Retrieving available item: " + item);
+                } else {
+                    item = new ActivityManagerService.ProcessChangeItem();
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Allocating new item: " + item);
+                }
+                item.changes = 0;
+                item.pid = app.pid;
+                item.uid = app.info.uid;
+                if (mService.mPendingProcessChanges.size() == 0) {
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "*** Enqueueing dispatch processes changed!");
+                    mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
+                            .sendToTarget();
+                }
+                mService.mPendingProcessChanges.add(item);
+            }
+            item.changes |= changes;
+            item.foregroundActivities = app.repForegroundActivities;
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                    "Item " + Integer.toHexString(System.identityHashCode(item))
+                            + " " + app.toShortString() + ": changes=" + item.changes
+                            + " foreground=" + item.foregroundActivities
+                            + " type=" + app.adjType + " source=" + app.adjSource
+                            + " target=" + app.adjTarget);
+        }
+
+        return success;
+    }
+
+    @GuardedBy("mService")
+    private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
+        if (DEBUG_USAGE_STATS) {
+            Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
+                    + "] state changes: old = " + app.setProcState + ", new = "
+                    + app.getCurProcState());
+        }
+        if (mService.mUsageStatsService == null) {
+            return;
+        }
+        boolean isInteraction;
+        // 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.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
+            isInteraction = true;
+            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.getFgInteractionTime()
+                        + mConstants.SERVICE_USAGE_INTERACTION_TIME;
+            }
+        } else {
+            isInteraction =
+                    app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            app.setFgInteractionTime(0);
+        }
+        if (isInteraction
+                && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
+                > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
+            app.setInteractionEventTime(nowElapsed);
+            String[] packages = app.getPackageList();
+            if (packages != null) {
+                for (int i = 0; i < packages.length; i++) {
+                    mService.mUsageStatsService.reportEvent(packages[i], app.userId,
+                            UsageEvents.Event.SYSTEM_INTERACTION);
+                }
+            }
+        }
+        app.reportedInteraction = isInteraction;
+        if (!isInteraction) {
+            app.setInteractionEventTime(0);
+        }
+    }
+
+    private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
+        if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
+                && app.getCurProcState() > ActivityManager.PROCESS_STATE_TOP) {
+            app.lastTopTime = nowUptime;
+        }
+    }
+
+    /**
+     * Look for recently inactive apps and mark them idle after a grace period. If idled, stop
+     * any background services and inform listeners.
+     */
+    @GuardedBy("mService")
+    void idleUidsLocked() {
+        final int N = mActiveUids.size();
+        if (N <= 0) {
+            return;
+        }
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
+        long nextTime = 0;
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.startUidChanges();
+        }
+        for (int i = N - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            final long bgTime = uidRec.lastBackgroundTime;
+            if (bgTime > 0 && !uidRec.idle) {
+                if (bgTime <= maxBgTime) {
+                    EventLogTags.writeAmUidIdle(uidRec.uid);
+                    uidRec.idle = true;
+                    uidRec.setIdle = true;
+                    mService.doStopUidLocked(uidRec.uid, uidRec);
+                } else {
+                    if (nextTime == 0 || nextTime > bgTime) {
+                        nextTime = bgTime;
+                    }
+                }
+            }
+        }
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.finishUidChanges();
+        }
+        if (nextTime > 0) {
+            mService.mHandler.removeMessages(IDLE_UIDS_MSG);
+            mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+                    nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
+        }
+    }
+
+    @GuardedBy("mService")
+    final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
+        boolean changed = false;
+        for (int i = mActiveUids.size() - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) {
+                uidRec.curWhitelist = onWhitelist;
+                changed = true;
+            }
+        }
+        if (changed) {
+            updateOomAdjLocked();
+        }
+    }
+
+    @GuardedBy("mService")
+    final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+        boolean changed = false;
+        final UidRecord uidRec = mActiveUids.get(uid);
+        if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
+            uidRec.curWhitelist = onWhitelist;
+            updateOomAdjLocked();
+        }
+    }
+
+    @GuardedBy("mService")
+    void dumpProcessListVariablesLocked(ProtoOutputStream proto) {
+        proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
+        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);
+
+    }
+
+    @GuardedBy("mService")
+    void dumpSequenceNumbersLocked(PrintWriter pw) {
+        pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
+    }
+
+    @GuardedBy("mService")
+    void dumpProcCountsLocked(PrintWriter pw) {
+        pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
+                + " (" + mProcessList.getLruSizeLocked() + " total)"
+                + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+                + " mNumServiceProcs=" + mNumServiceProcs
+                + " mNewNumServiceProcs=" + mNewNumServiceProcs);
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index b675d9d..98c9ad6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -51,6 +51,7 @@
 
     public static final int FLAG_ACTIVITY_SENDER = 1 << 0;
     public static final int FLAG_BROADCAST_SENDER = 1 << 1;
+    public static final int FLAG_SERVICE_SENDER = 1 << 2;
 
     final PendingIntentController controller;
     final Key key;
@@ -62,6 +63,7 @@
     private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
     private ArraySet<IBinder> mAllowBgActivityStartsForActivitySender = new ArraySet<>();
     private ArraySet<IBinder> mAllowBgActivityStartsForBroadcastSender = new ArraySet<>();
+    private ArraySet<IBinder> mAllowBgActivityStartsForServiceSender = new ArraySet<>();
 
     String stringName;
     String lastTagPrefix;
@@ -228,6 +230,9 @@
         if ((flags & FLAG_BROADCAST_SENDER) != 0) {
             mAllowBgActivityStartsForBroadcastSender.add(token);
         }
+        if ((flags & FLAG_SERVICE_SENDER) != 0) {
+            mAllowBgActivityStartsForServiceSender.add(token);
+        }
     }
 
     public void registerCancelListenerLocked(IResultReceiver receiver) {
@@ -426,7 +431,8 @@
                     try {
                         controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
                                 key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
-                                key.packageName, userId);
+                                key.packageName, userId,
+                                mAllowBgActivityStartsForServiceSender.contains(whitelistToken));
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startService intent", e);
                     } catch (TransactionTooLargeException e) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index d133dea..c1be387 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -17,10 +17,9 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 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;
@@ -34,6 +33,8 @@
 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.KILL_APP_ZYGOTE_DELAY_MS;
+import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_MSG;
 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;
@@ -58,6 +59,7 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.net.Uri;
+import android.os.AppZygote;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -75,10 +77,12 @@
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.StatsLog;
 import android.view.Display;
 
@@ -108,6 +112,7 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 /**
@@ -119,7 +124,7 @@
  * </ul>
  */
 public final class ProcessList {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
+    static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
 
     // The minimum time we allow between crashes, for us to consider this
     // application to be bad and stop and its services and reject broadcasts.
@@ -348,16 +353,144 @@
      */
     int mLruSeq = 0;
 
+    ActiveUids mActiveUids;
+
     /**
      * The currently running isolated processes.
      */
     final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
 
     /**
-     * Counter for assigning isolated process uids, to avoid frequently reusing the
-     * same ones.
+     * The currently running application zygotes.
      */
-    int mNextIsolatedProcessUid = 0;
+    final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
+
+    /**
+     * The processes that are forked off an application zygote.
+     */
+    final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
+            new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
+
+    final class IsolatedUidRange {
+        @VisibleForTesting
+        public final int mFirstUid;
+        @VisibleForTesting
+        public final int mLastUid;
+
+        @GuardedBy("ProcessList.this.mService")
+        private final SparseBooleanArray mUidUsed = new SparseBooleanArray();
+
+        @GuardedBy("ProcessList.this.mService")
+        private int mNextUid;
+
+        IsolatedUidRange(int firstUid, int lastUid) {
+            mFirstUid = firstUid;
+            mLastUid = lastUid;
+            mNextUid = firstUid;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        int allocateIsolatedUidLocked(int userId) {
+            int uid;
+            int stepsLeft = (mLastUid - mFirstUid + 1);
+            for (int i = 0; i < stepsLeft; ++i) {
+                if (mNextUid < mFirstUid || mNextUid > mLastUid) {
+                    mNextUid = mFirstUid;
+                }
+                uid = UserHandle.getUid(userId, mNextUid);
+                mNextUid++;
+                if (!mUidUsed.get(uid, false)) {
+                    mUidUsed.put(uid, true);
+                    return uid;
+                }
+            }
+            return -1;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        void freeIsolatedUidLocked(int uid) {
+            // Strip out userId
+            final int appId = UserHandle.getAppId(uid);
+            mUidUsed.delete(appId);
+        }
+    };
+
+    /**
+     * A class that allocates ranges of isolated UIDs per application, and keeps track of them.
+     */
+    final class IsolatedUidRangeAllocator {
+        private final int mFirstUid;
+        private final int mNumUidRanges;
+        private final int mNumUidsPerRange;
+        /**
+         * We map the uid range [mFirstUid, mFirstUid + mNumUidRanges * mNumUidsPerRange)
+         * back to an underlying bitset of [0, mNumUidRanges) and allocate out of that.
+         */
+        @GuardedBy("ProcessList.this.mService")
+        private final BitSet mAvailableUidRanges;
+        @GuardedBy("ProcessList.this.mService")
+        private final ProcessMap<IsolatedUidRange> mAppRanges = new ProcessMap<IsolatedUidRange>();
+
+        IsolatedUidRangeAllocator(int firstUid, int lastUid, int numUidsPerRange) {
+            mFirstUid = firstUid;
+            mNumUidsPerRange = numUidsPerRange;
+            mNumUidRanges = (lastUid - firstUid + 1) / numUidsPerRange;
+            mAvailableUidRanges = new BitSet(mNumUidRanges);
+            // Mark all as available
+            mAvailableUidRanges.set(0, mNumUidRanges);
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        IsolatedUidRange getIsolatedUidRangeLocked(ApplicationInfo info) {
+            return mAppRanges.get(info.processName, info.uid);
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        IsolatedUidRange getOrCreateIsolatedUidRangeLocked(ApplicationInfo info) {
+            IsolatedUidRange range = getIsolatedUidRangeLocked(info);
+            if (range == null) {
+                int uidRangeIndex = mAvailableUidRanges.nextSetBit(0);
+                if (uidRangeIndex < 0) {
+                    // No free range
+                    return null;
+                }
+                mAvailableUidRanges.clear(uidRangeIndex);
+                int actualUid = mFirstUid + uidRangeIndex * mNumUidsPerRange;
+                range = new IsolatedUidRange(actualUid, actualUid + mNumUidsPerRange - 1);
+                mAppRanges.put(info.processName, info.uid, range);
+            }
+            return range;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        void freeUidRangeLocked(ApplicationInfo info) {
+            // Find the UID range
+            IsolatedUidRange range = mAppRanges.get(info.processName, info.uid);
+            if (range != null) {
+                // Map back to starting uid
+                final int uidRangeIndex = (range.mFirstUid - mFirstUid) / mNumUidsPerRange;
+                // Mark it as available in the underlying bitset
+                mAvailableUidRanges.set(uidRangeIndex);
+                // And the map
+                mAppRanges.remove(info.processName, info.uid);
+            }
+        }
+    }
+
+    /**
+     * The available isolated UIDs for processes that are not spawned from an application zygote.
+     */
+    @VisibleForTesting
+    IsolatedUidRange mGlobalIsolatedUids = new IsolatedUidRange(Process.FIRST_ISOLATED_UID,
+            Process.LAST_ISOLATED_UID);
+
+    /**
+     * An allocator for isolated UID ranges for apps that use an application zygote.
+     */
+    @VisibleForTesting
+    IsolatedUidRangeAllocator mAppIsolatedUidRangeAllocator =
+            new IsolatedUidRangeAllocator(Process.FIRST_APP_ZYGOTE_ISOLATED_UID,
+                    Process.LAST_APP_ZYGOTE_ISOLATED_UID, Process.NUM_UIDS_PER_APP_ZYGOTE);
 
     /**
      * Processes that are being forcibly torn down.
@@ -419,8 +552,10 @@
         updateOomLevels(0, 0, false);
     }
 
-    void init(ActivityManagerService service) {
+    void init(ActivityManagerService service, ActiveUids activeUids) {
         mService = service;
+        mActiveUids = activeUids;
+
         if (sKillHandler == null) {
             sKillThread = new ServiceThread(TAG + ":kill",
                     THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
@@ -1505,6 +1640,69 @@
         }
     }
 
+    @GuardedBy("mService")
+    public void killAppZygoteIfNeededLocked(AppZygote appZygote) {
+        final ApplicationInfo appInfo = appZygote.getAppInfo();
+        ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
+        if (zygoteProcesses.size() == 0) { // Only remove if no longer in use now
+            mAppZygotes.remove(appInfo.processName, appInfo.uid);
+            mAppZygoteProcesses.remove(appZygote);
+            mAppIsolatedUidRangeAllocator.freeUidRangeLocked(appInfo);
+            appZygote.stopZygote();
+        }
+    }
+
+    @GuardedBy("mService")
+    private void removeProcessFromAppZygoteLocked(final ProcessRecord app) {
+        // Free the isolated uid for this process
+        final IsolatedUidRange appUidRange =
+                mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info);
+        if (appUidRange != null) {
+            appUidRange.freeIsolatedUidLocked(app.uid);
+        }
+
+        final AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid);
+        if (appZygote != null) {
+            ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
+            zygoteProcesses.remove(app);
+            if (zygoteProcesses.size() == 0) {
+                Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG);
+                msg.obj = appZygote;
+                mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS);
+            }
+        }
+    }
+
+    private AppZygote createAppZygoteForProcessIfNeeded(final ProcessRecord app) {
+        synchronized (mService) {
+            AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid);
+            final ArrayList<ProcessRecord> zygoteProcessList;
+            if (appZygote == null) {
+                final IsolatedUidRange uidRange =
+                        mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info);
+                final int userId = UserHandle.getUserId(app.info.uid);
+                // Create the app-zygote and provide it with the UID-range it's allowed
+                // to setresuid/setresgid to.
+                final int firstUid = UserHandle.getUid(userId, uidRange.mFirstUid);
+                final int lastUid = UserHandle.getUid(userId, uidRange.mLastUid);
+                appZygote = new AppZygote(app.info, app.info.uid, firstUid, lastUid);
+                mAppZygotes.put(app.info.processName, app.info.uid, appZygote);
+                zygoteProcessList = new ArrayList<ProcessRecord>();
+                mAppZygoteProcesses.put(appZygote, zygoteProcessList);
+            } else {
+                mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG, appZygote);
+                zygoteProcessList = mAppZygoteProcesses.get(appZygote);
+            }
+            // Note that we already add the app to mAppZygoteProcesses here;
+            // this is so that another thread can't come in and kill the zygote
+            // before we've even tried to start the process. If the process launch
+            // goes wrong, we'll clean this up in removeProcessNameLocked()
+            zygoteProcessList.add(app);
+
+            return appZygote;
+        }
+    }
+
     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,
@@ -1525,6 +1723,15 @@
                         app.info.dataDir, null, app.info.packageName,
                         packageNames, visibleVolIds,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+            } else if (hostingType.equals("app_zygote")) {
+                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
+
+                startResult = appZygote.getProcess().start(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,
@@ -1614,13 +1821,6 @@
                 app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
                 checkSlow(startTime, "startProcess: done, added package to proc");
                 return app;
-            } else if (app.getActiveInstrumentation() != null) {
-                // We don't want to kill running instrumentation.
-                if (DEBUG_PROCESSES) {
-                    Slog.v(TAG_PROCESSES, "Instrumentation already running: " + app);
-                }
-                checkSlow(startTime, "startProcess: keep instrumentation proc");
-                return app;
             }
 
             // An application record is attached to a previous process,
@@ -1636,8 +1836,9 @@
                 ? hostingName.flattenToShortString() : null;
 
         if (app == null) {
+            final boolean fromAppZygote = "app_zygote".equals(hostingType);
             checkSlow(startTime, "startProcess: creating new process record");
-            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+            app = newProcessRecordLocked(info, processName, isolated, isolatedUid, fromAppZygote);
             if (app == null) {
                 Slog.w(TAG, "Failed making new process record for "
                         + processName + "/" + info.uid + " isolated=" + isolated);
@@ -1982,7 +2183,7 @@
         } else if (old != null) {
             Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
         }
-        UidRecord uidRec = mService.mActiveUids.get(proc.uid);
+        UidRecord uidRec = 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!
@@ -1994,7 +2195,7 @@
                 uidRec.setWhitelist = uidRec.curWhitelist = true;
             }
             uidRec.updateHasInternetPermission();
-            mService.mActiveUids.put(proc.uid, uidRec);
+            mActiveUids.put(proc.uid, uidRec);
             EventLogTags.writeAmUidRunning(uidRec.uid);
             mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
         }
@@ -2010,29 +2211,31 @@
     }
 
     @GuardedBy("mService")
+    private IsolatedUidRange getOrCreateIsolatedUidRangeLocked(ApplicationInfo info,
+            boolean fromAppZygote) {
+        if (!fromAppZygote) {
+            // Allocate an isolated UID from the global range
+            return mGlobalIsolatedUids;
+        } else {
+            return mAppIsolatedUidRangeAllocator.getOrCreateIsolatedUidRangeLocked(info);
+        }
+    }
+
+    @GuardedBy("mService")
     final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
-            boolean isolated, int isolatedUid) {
+            boolean isolated, int isolatedUid, boolean fromAppZygote) {
         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;
-                    }
+                IsolatedUidRange uidRange = getOrCreateIsolatedUidRangeLocked(info, fromAppZygote);
+                if (uidRange == null) {
+                    return null;
+                }
+                uid = uidRange.allocateIsolatedUidLocked(userId);
+                if (uid == -1) {
+                    return null;
                 }
             } else {
                 // Special case for startIsolatedProcess (internal only), where
@@ -2094,12 +2297,19 @@
                         "No more processes in " + old.uidRecord);
                 mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
                 EventLogTags.writeAmUidStopped(uid);
-                mService.mActiveUids.remove(uid);
+                mActiveUids.remove(uid);
                 mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
             }
             old.uidRecord = null;
         }
         mIsolatedProcesses.remove(uid);
+        mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
+        // Remove the (expected) ProcessRecord from the app zygote
+        final ProcessRecord record = expecting != null ? expecting : old;
+        if (record != null && record.appZygote) {
+            removeProcessFromAppZygoteLocked(record);
+        }
+
         return old;
     }
 
@@ -2847,4 +3057,37 @@
             }
         }
     }
+
+    /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
+    @GuardedBy("mService")
+    int getUidProcStateLocked(int uid) {
+        UidRecord uidRec = mActiveUids.get(uid);
+        return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
+    }
+
+    /** Returns the UidRecord for the given uid, if it exists. */
+    @GuardedBy("mService")
+    UidRecord getUidRecordLocked(int uid) {
+        return mActiveUids.get(uid);
+    }
+
+    /**
+     * Call {@link ActivityManagerService#doStopUidLocked}
+     * (which will also stop background services) for all idle UIDs.
+     */
+    @GuardedBy("mService")
+    void doStopUidForIdleUidsLocked() {
+        final int size = mActiveUids.size();
+        for (int i = 0; i < size; i++) {
+            final int uid = mActiveUids.keyAt(i);
+            if (UserHandle.isCore(uid)) {
+                continue;
+            }
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (!uidRec.idle) {
+                continue;
+            }
+            mService.doStopUidLocked(uidRec.uid, uidRec);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 6f0a562..054c830 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -76,6 +76,7 @@
     private final ActivityManagerService mService; // where we came from
     final ApplicationInfo info; // all about the first app in the process
     final boolean isolated;     // true if this is a special isolated process
+    final boolean appZygote;    // true if this is forked from the app zygote
     final int uid;              // uid of process; may be different from 'info' if isolated
     final int userId;           // user of process.
     final String processName;   // name of the process
@@ -559,6 +560,8 @@
         mService = _service;
         info = _info;
         isolated = _info.uid != _uid;
+        appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
@@ -1139,11 +1142,13 @@
     }
 
     void addAllowBackgroundActivityStartsToken(Binder entity) {
+        if (entity == null) return;
         mAllowBackgroundActivityStartsTokens.add(entity);
         mWindowProcessController.setAllowBackgroundActivityStarts(true);
     }
 
     void removeAllowBackgroundActivityStartsToken(Binder entity) {
+        if (entity == null) return;
         mAllowBackgroundActivityStartsTokens.remove(entity);
         mWindowProcessController.setAllowBackgroundActivityStarts(
                 !mAllowBackgroundActivityStartsTokens.isEmpty());
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index d5ede5b..c2117a7 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -77,6 +77,7 @@
     // permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sDeviceConfigScopes = new String[] {
+        DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
     };
 
     private final String[] mGlobalSettings;
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 48b4145..c84b5c7 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -7,7 +7,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -21,7 +21,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
similarity index 94%
rename from services/core/java/com/android/server/AppOpsService.java
rename to services/core/java/com/android/server/appop/AppOpsService.java
index 3cdf09e..7ede6dc 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.appop;
 
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
+import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
 import static android.app.AppOpsManager.UID_STATE_CACHED;
 import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
@@ -35,8 +36,7 @@
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.AppOpsManager.HistoricalOpEntry;
-import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.HistoricalOps;
 import android.app.AppOpsManagerInternal;
 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.content.BroadcastReceiver;
@@ -48,7 +48,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioAttributes;
@@ -59,6 +58,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -95,6 +95,8 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
 
+import com.android.server.LocalServices;
+import com.android.server.LockGuard;
 import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -213,6 +215,8 @@
     @VisibleForTesting
     final SparseArray<UidState> mUidStates = new SparseArray<>();
 
+    private final HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this);
+
     long mLastRealtime;
 
     /*
@@ -654,6 +658,7 @@
 
     public void systemReady() {
         mConstants.startMonitoring(mContext.getContentResolver());
+        mHistoricalRegistry.systemReady(mContext.getContentResolver());
 
         synchronized (this) {
             boolean changed = false;
@@ -1012,113 +1017,99 @@
     }
 
     @Override
-    public @Nullable ParceledListSlice getAllHistoricalPackagesOps(@Nullable String[] opNames,
-            long beginTimeMillis, long endTimeMillis) {
+    public void getHistoricalOps(int uid, @NonNull String packageName,
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @NonNull RemoteCallback callback) {
+        Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
+                "uid must be " + Process.INVALID_UID + " or non negative");
         Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
                 "beginTimeMillis must be non negative and lesser than endTimeMillis");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        checkValidOpsOrNull(opNames);
 
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
-                Binder.getCallingPid(), Binder.getCallingUid(), "getAllHistoricalPackagesOps");
+                Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
 
-        ArrayList<HistoricalPackageOps> historicalPackageOpsList = null;
-
-        final int uidStateCount = mUidStates.size();
-        for (int i = 0; i < uidStateCount; i++) {
-            final UidState uidState = mUidStates.valueAt(i);
-            if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()) {
-                continue;
-            }
-            final ArrayMap<String, Ops> packages = uidState.pkgOps;
-            final int packageCount = packages.size();
-            for (int j = 0; j < packageCount; j++) {
-                final Ops pkgOps = packages.valueAt(j);
-                final AppOpsManager.HistoricalPackageOps historicalPackageOps =
-                        createHistoricalPackageOps(uidState.uid, pkgOps, opNames,
-                                beginTimeMillis, endTimeMillis);
-                if (historicalPackageOps != null) {
-                    if (historicalPackageOpsList == null) {
-                        historicalPackageOpsList = new ArrayList<>();
-                    }
-                    historicalPackageOpsList.add(historicalPackageOps);
-                }
-            }
+        if (mHistoricalRegistry.getMode() == AppOpsManager.HISTORICAL_MODE_DISABLED) {
+            // TODO (bug:122218838): Remove once the feature fully enabled.
+            getHistoricalPackagesOpsCompat(uid, packageName, opNames, beginTimeMillis,
+                    endTimeMillis, callback);
+        } else {
+            // Must not hold the appops lock
+            mHistoricalRegistry.getHistoricalOps(uid, packageName, opNames,
+                    beginTimeMillis, endTimeMillis, callback);
         }
-
-        if (historicalPackageOpsList == null) {
-            return null;
-        }
-
-        return new ParceledListSlice<>(historicalPackageOpsList);
     }
 
-    private static @Nullable HistoricalPackageOps createHistoricalPackageOps(int uid,
-            @Nullable Ops pkgOps, @Nullable String[] opNames, long beginTimeMillis,
-            long endTimeMillis) {
-        // TODO: Implement historical data collection
-        if (pkgOps == null) {
-            return null;
+    private void getHistoricalPackagesOpsCompat(int uid, @NonNull String packageName,
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @NonNull RemoteCallback callback) {
+        synchronized (AppOpsService.this) {
+            final HistoricalOps ops = new HistoricalOps(beginTimeMillis, endTimeMillis);
+            if (opNames == null) {
+                opNames = AppOpsManager.getOpStrs();
+            }
+            final int uidStateCount = mUidStates.size();
+            for (int uidIdx = 0; uidIdx < uidStateCount; uidIdx++) {
+                final UidState uidState = mUidStates.valueAt(uidIdx);
+                if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()
+                        || (uid != Process.INVALID_UID && uid != uidState.uid)) {
+                    continue;
+                }
+                final ArrayMap<String, Ops> packages = uidState.pkgOps;
+                final int packageCount = packages.size();
+                for (int pkgIdx = 0; pkgIdx < packageCount; pkgIdx++) {
+                    final Ops pkgOps = packages.valueAt(pkgIdx);
+                    if (packageName != null && !packageName.equals(pkgOps.packageName)) {
+                        continue;
+                    }
+                    final int opCount = opNames.length;
+                    for (int opIdx = 0; opIdx < opCount; opIdx++) {
+                        final String opName = opNames[opIdx];
+                        if (!ArrayUtils.contains(opNames, opName)) {
+                            continue;
+                        }
+                        final int opCode = AppOpsManager.strOpToOp(opName);
+                        final Op op = pkgOps.get(opCode);
+                        if (op == null) {
+                            continue;
+                        }
+                        final int stateCount = AppOpsManager._NUM_UID_STATE;
+                        for (int stateIdx = 0; stateIdx < stateCount; stateIdx++) {
+                            if (op.rejectTime[stateIdx] != 0) {
+                                ops.increaseRejectCount(opCode, uidState.uid,
+                                        pkgOps.packageName, stateIdx, 1);
+                            } else if (op.time[stateIdx] != 0) {
+                                ops.increaseAccessCount(opCode, uidState.uid,
+                                        pkgOps.packageName, stateIdx, 1);
+                            }
+                        }
+                    }
+                }
+            }
+            final Bundle payload = new Bundle();
+            payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, ops);
+            callback.sendResult(payload);
         }
-
-        final HistoricalPackageOps historicalPackageOps = new HistoricalPackageOps(uid,
-                pkgOps.packageName);
-
-        if (opNames == null) {
-            opNames = AppOpsManager.getOpStrs();
-        }
-        for (String opName : opNames) {
-            addHistoricOpEntry(AppOpsManager.strOpToOp(opName), pkgOps, historicalPackageOps);
-        }
-
-        return historicalPackageOps;
     }
 
     @Override
-    public @Nullable HistoricalPackageOps getHistoricalPackagesOps(int uid,
-            @NonNull String packageName, @Nullable String[] opNames,
-            long beginTimeMillis, long endTimeMillis) {
-        Preconditions.checkNotNull(packageName,
-                "packageName cannot be null");
+    public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @NonNull RemoteCallback callback) {
+        Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
+                "uid must be " + Process.INVALID_UID + " or non negative");
         Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
                 "beginTimeMillis must be non negative and lesser than endTimeMillis");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        checkValidOpsOrNull(opNames);
 
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
-                Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalPackagesOps");
+                Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
 
-        final String resolvedPackageName = resolvePackageName(uid, packageName);
-        if (resolvedPackageName == null) {
-            return null;
-        }
-
-        // TODO: Implement historical data collection
-        final Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */,
-                false /* uidMismatchExpected */);
-        return createHistoricalPackageOps(uid, pkgOps, opNames, beginTimeMillis, endTimeMillis);
-    }
-
-    private static void addHistoricOpEntry(int opCode, @NonNull Ops ops,
-            @NonNull HistoricalPackageOps outHistoricalPackageOps) {
-        final Op op = ops.get(opCode);
-        if (op == null) {
-            return;
-        }
-
-        final HistoricalOpEntry historicalOpEntry = new HistoricalOpEntry(opCode);
-
-        // TODO: Keep per UID state duration
-        for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) {
-            final int acceptCount;
-            final int rejectCount;
-            if (op.rejectTime[uidState] == 0) {
-                acceptCount = 1;
-                rejectCount = 0;
-            } else {
-                acceptCount = 0;
-                rejectCount = 1;
-            }
-            historicalOpEntry.addEntry(uidState, acceptCount, rejectCount, 0);
-        }
-
-        outHistoricalPackageOps.addEntry(historicalOpEntry);
+        // Must not hold the appops lock
+        mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNames,
+                beginTimeMillis, endTimeMillis, callback);
     }
 
     @Override
@@ -1884,6 +1875,8 @@
                             + packageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
+                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
+                            uidState.state);
                     return uidMode;
                 }
             } else {
@@ -1895,12 +1888,16 @@
                             + packageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
                     scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode);
+                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
+                            uidState.state);
                     return mode;
                 }
             }
             if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                     + " package " + packageName);
             op.time[uidState.state] = System.currentTimeMillis();
+            mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
+                    uidState.state);
             op.rejectTime[uidState.state] = 0;
             op.proxyUid = proxyUid;
             op.proxyPackageName = proxyPackageName;
@@ -2035,6 +2032,8 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
+                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
+                            uidState.state);
                     return uidMode;
                 }
             } else {
@@ -2046,6 +2045,8 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
+                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
+                            uidState.state);
                     return mode;
                 }
             }
@@ -2054,6 +2055,8 @@
             if (op.startNesting == 0) {
                 op.startRealtime = SystemClock.elapsedRealtime();
                 op.time[uidState.state] = System.currentTimeMillis();
+                mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
+                        uidState.state);
                 op.rejectTime[uidState.state] = 0;
                 op.duration = -1;
                 scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
@@ -2216,6 +2219,8 @@
         if (op.startNesting <= 1 || finishNested) {
             if (op.startNesting == 1 || finishNested) {
                 op.duration = (int)(SystemClock.elapsedRealtime() - op.startRealtime);
+                mHistoricalRegistry.increaseOpAccessDuration(op.op, op.uid, op.packageName,
+                        op.uidState.state, op.duration);
                 op.time[op.uidState.state] = System.currentTimeMillis();
             } else {
                 Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
@@ -2344,7 +2349,7 @@
                         ApplicationInfo appInfo = ActivityThread.getPackageManager()
                                 .getApplicationInfo(packageName,
                                         PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                                         UserHandle.getUserId(uid));
                         if (appInfo != null) {
                             pkgUid = appInfo.uid;
@@ -3391,6 +3396,8 @@
         pw.println("    Limit output to data associated with the given app op mode.");
         pw.println("  --package [PACKAGE]");
         pw.println("    Limit output to data associated with the given package name.");
+        pw.println("  --watchers");
+        pw.println("    Only output the watcher sections.");
     }
 
     private void dumpTimesLocked(PrintWriter pw, String firstPrefix, String prefix, long[] times,
@@ -3425,10 +3432,11 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
 
-        int dumpOp = -1;
+        int dumpOp = OP_NONE;
         String dumpPackage = null;
-        int dumpUid = -1;
+        int dumpUid = Process.INVALID_UID;
         int dumpMode = -1;
+        boolean dumpWatchers = false;
 
         if (args != null) {
             for (int i=0; i<args.length; i++) {
@@ -3476,6 +3484,8 @@
                     if (dumpMode < 0) {
                         return;
                     }
+                } else if ("--watchers".equals(arg)) {
+                    dumpWatchers = true;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
                     pw.println("Unknown option: " + arg);
                     return;
@@ -3496,7 +3506,8 @@
             final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
             final Date date = new Date();
             boolean needSep = false;
-            if (dumpOp < 0 && dumpMode < 0 && dumpPackage == null && mProfileOwners != null) {
+            if (dumpOp < 0 && dumpMode < 0 && dumpPackage == null && mProfileOwners != null
+                    && !dumpWatchers) {
                 pw.println("  Profile owners:");
                 for (int poi = 0; poi < mProfileOwners.size(); poi++) {
                     pw.print("    User #");
@@ -3517,7 +3528,7 @@
                     ArraySet<ModeCallback> callbacks = mOpModeWatchers.valueAt(i);
                     for (int j=0; j<callbacks.size(); j++) {
                         final ModeCallback cb = callbacks.valueAt(j);
-                        if (dumpPackage != null && cb.mWatchingUid >= 0
+                        if (dumpPackage != null
                                 && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
                             continue;
                         }
@@ -3561,7 +3572,7 @@
                 boolean printedHeader = false;
                 for (int i=0; i<mModeWatchers.size(); i++) {
                     final ModeCallback cb = mModeWatchers.valueAt(i);
-                    if (dumpPackage != null && cb.mWatchingUid >= 0
+                    if (dumpPackage != null
                             && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
                         continue;
                     }
@@ -3587,7 +3598,7 @@
                     if (dumpOp >= 0 && activeWatchers.indexOfKey(dumpOp) < 0) {
                         continue;
                     }
-                    if (dumpPackage != null && cb.mWatchingUid >= 0
+                    if (dumpPackage != null
                             && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
                         continue;
                     }
@@ -3627,7 +3638,7 @@
                     if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) {
                         continue;
                     }
-                    if (dumpPackage != null && cb.mWatchingUid >= 0
+                    if (dumpPackage != null
                             && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
                         continue;
                     }
@@ -3655,7 +3666,7 @@
                     pw.println(cb);
                 }
             }
-            if (mClients.size() > 0 && dumpMode < 0) {
+            if (mClients.size() > 0 && dumpMode < 0 && !dumpWatchers) {
                 needSep = true;
                 boolean printedHeader = false;
                 for (int i=0; i<mClients.size(); i++) {
@@ -3692,7 +3703,7 @@
                 }
             }
             if (mAudioRestrictions.size() > 0 && dumpOp < 0 && dumpPackage != null
-                    && dumpMode < 0) {
+                    && dumpMode < 0 && !dumpWatchers) {
                 boolean printedHeader = false;
                 for (int o=0; o<mAudioRestrictions.size(); o++) {
                     final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
@@ -3725,6 +3736,9 @@
                 final SparseIntArray opModes = uidState.opModes;
                 final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
 
+                if (dumpWatchers) {
+                    continue;
+                }
                 if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
                     boolean hasOp = dumpOp < 0 || (uidState.opModes != null
                             && uidState.opModes.indexOfKey(dumpOp) >= 0);
@@ -3739,8 +3753,8 @@
                     }
                     if (pkgOps != null) {
                         for (int pkgi = 0;
-                                (!hasOp || !hasPackage || !hasMode) && pkgi < pkgOps.size();
-                                pkgi++) {
+                                 (!hasOp || !hasPackage || !hasMode) && pkgi < pkgOps.size();
+                                 pkgi++) {
                             Ops ops = pkgOps.valueAt(pkgi);
                             if (!hasOp && ops != null && ops.indexOfKey(dumpOp) >= 0) {
                                 hasOp = true;
@@ -3880,18 +3894,34 @@
             for (int i = 0; i < userRestrictionCount; i++) {
                 IBinder token = mOpUserRestrictions.keyAt(i);
                 ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
-                pw.println("  User restrictions for token " + token + ":");
+                boolean printedTokenHeader = false;
+
+                if (dumpMode >= 0 || dumpWatchers) {
+                    continue;
+                }
 
                 final int restrictionCount = restrictionState.perUserRestrictions != null
                         ? restrictionState.perUserRestrictions.size() : 0;
-                if (restrictionCount > 0) {
-                    pw.println("      Restricted ops:");
+                if (restrictionCount > 0 && dumpPackage == null) {
+                    boolean printedOpsHeader = false;
                     for (int j = 0; j < restrictionCount; j++) {
                         int userId = restrictionState.perUserRestrictions.keyAt(j);
                         boolean[] restrictedOps = restrictionState.perUserRestrictions.valueAt(j);
                         if (restrictedOps == null) {
                             continue;
                         }
+                        if (dumpOp >= 0 && (dumpOp >= restrictedOps.length
+                                || !restrictedOps[dumpOp])) {
+                            continue;
+                        }
+                        if (!printedTokenHeader) {
+                            pw.println("  User restrictions for token " + token + ":");
+                            printedTokenHeader = true;
+                        }
+                        if (!printedOpsHeader) {
+                            pw.println("      Restricted ops:");
+                            printedOpsHeader = true;
+                        }
                         StringBuilder restrictedOpsValue = new StringBuilder();
                         restrictedOpsValue.append("[");
                         final int restrictedOpCount = restrictedOps.length;
@@ -3911,17 +3941,46 @@
 
                 final int excludedPackageCount = restrictionState.perUserExcludedPackages != null
                         ? restrictionState.perUserExcludedPackages.size() : 0;
-                if (excludedPackageCount > 0) {
-                    pw.println("      Excluded packages:");
+                if (excludedPackageCount > 0 && dumpOp < 0) {
+                    boolean printedPackagesHeader = false;
                     for (int j = 0; j < excludedPackageCount; j++) {
                         int userId = restrictionState.perUserExcludedPackages.keyAt(j);
                         String[] packageNames = restrictionState.perUserExcludedPackages.valueAt(j);
+                        if (packageNames == null) {
+                            continue;
+                        }
+                        boolean hasPackage;
+                        if (dumpPackage != null) {
+                            hasPackage = false;
+                            for (String pkg : packageNames) {
+                                if (dumpPackage.equals(pkg)) {
+                                    hasPackage = true;
+                                    break;
+                                }
+                            }
+                        } else {
+                            hasPackage = true;
+                        }
+                        if (!hasPackage) {
+                            continue;
+                        }
+                        if (!printedTokenHeader) {
+                            pw.println("  User restrictions for token " + token + ":");
+                            printedTokenHeader = true;
+                        }
+                        if (!printedPackagesHeader) {
+                            pw.println("      Excluded packages:");
+                            printedPackagesHeader = true;
+                        }
                         pw.print("        "); pw.print("user: "); pw.print(userId);
                                 pw.print(" packages: "); pw.println(Arrays.toString(packageNames));
                     }
                 }
             }
         }
+
+        // Must not hold the appops lock
+        mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpOp);
     }
 
     private static final class Restriction {
@@ -4042,6 +4101,47 @@
         return false;
     }
 
+    @Override
+    public void setHistoryParameters(@AppOpsManager.HistoricalMode int mode,
+            long baseSnapshotInterval, int compressionStep) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+                "setHistoryParameters");
+        // Must not hold the appops lock
+        mHistoricalRegistry.setHistoryParameters(mode, baseSnapshotInterval, compressionStep);
+    }
+
+    @Override
+    public void offsetHistory(long offsetMillis) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+                "offsetHistory");
+        // Must not hold the appops lock
+        mHistoricalRegistry.offsetHistory(offsetMillis);
+    }
+
+    @Override
+    public void addHistoricalOps(HistoricalOps ops) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+                "addHistoricalOps");
+        // Must not hold the appops lock
+        mHistoricalRegistry.addHistoricalOps(ops);
+    }
+
+    @Override
+    public void resetHistoryParameters() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+                "resetHistoryParameters");
+        // Must not hold the appops lock
+        mHistoricalRegistry.resetHistoryParameters();
+    }
+
+    @Override
+    public void clearHistory() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+                "clearHistory");
+        // Must not hold the appops lock
+        mHistoricalRegistry.clearHistory();
+    }
+
     private void removeUidsForUserLocked(int userHandle) {
         for (int i = mUidStates.size() - 1; i >= 0; --i) {
             final int uid = mUidStates.keyAt(i);
@@ -4112,6 +4212,16 @@
         return packageNames;
     }
 
+    private static void checkValidOpsOrNull(String[] opNames) {
+        if (opNames != null) {
+            for (String opName : opNames) {
+                if (AppOpsManager.strOpToOp(opName) == AppOpsManager.OP_NONE) {
+                    throw new IllegalArgumentException("Unknown op: " + opName);
+                }
+            }
+        }
+    }
+
     private final class ClientRestrictionState implements DeathRecipient {
         private final IBinder token;
         SparseArray<boolean[]> perUserRestrictions;
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
new file mode 100644
index 0000000..714a807
--- /dev/null
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -0,0 +1,1496 @@
+/*
+ * 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.appop;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalMode;
+import android.app.AppOpsManager.HistoricalOp;
+import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.HistoricalUidOps;
+import android.app.AppOpsManager.UidState;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteCallback;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.AtomicDirectory;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class managers historical app op state. This includes reading, persistence,
+ * accounting, querying.
+ * <p>
+ * The history is kept forever in multiple files. Each file time contains the
+ * relative offset from the current time which time is encoded in the file name.
+ * The files contain historical app op state snapshots which have times that
+ * are relative to the time of the container file.
+ *
+ * The data in the files are stored in a logarithmic fashion where where every
+ * subsequent file would contain data for ten times longer interval with ten
+ * times more time distance between snapshots. Hence, the more time passes
+ * the lesser the fidelity.
+ * <p>
+ * For example, the first file would contain data for 1 days with snapshots
+ * every 0.1 days, the next file would contain data for the period 1 to 10
+ * days with snapshots every 1 days, and so on.
+ * <p>
+ * THREADING AND LOCKING: Reported ops must be processed as quickly as possible.
+ * We keep ops pending to be persisted in memory and write to disk on a background
+ * thread. Hence, methods that report op changes are locking only the in memory
+ * state guarded by the mInMemoryLock which happens to be the app ops service lock
+ * avoiding a lock addition on the critical path. When a query comes we need to
+ * evaluate it based off both in memory and on disk state. This means they need to
+ * be frozen with respect to each other and not change from the querying caller's
+ * perspective. To achieve this we add a dedicated mOnDiskLock to guard the on
+ * disk state. To have fast critical path we need to limit the locking of the
+ * mInMemoryLock, thus for operations that touch in memory and on disk state one
+ * must grab first the mOnDiskLock and then the mInMemoryLock and limit the
+ * in memory lock to extraction of relevant data. Locking order is critical to
+ * avoid deadlocks. The convention is that xxxDLocked suffix means the method
+ * must be called with the mOnDiskLock lock, xxxMLocked suffix means the method
+ * must be called with the mInMemoryLock, xxxDMLocked suffix means the method
+ * must be called with the mOnDiskLock and mInMemoryLock locks acquired in that
+ * exact order.
+ */
+// TODO (bug:122218838): Make sure we handle start of epoch time
+// TODO (bug:122218838): Validate changed time is handled correctly
+final class HistoricalRegistry {
+    private static final boolean DEBUG = false;
+
+    private static final String LOG_TAG = HistoricalRegistry.class.getSimpleName();
+
+    private static final String PARAMETER_DELIMITER = ",";
+    private static final String PARAMETER_ASSIGNMENT = "=";
+
+    @GuardedBy("mLock")
+    private @NonNull LinkedList<HistoricalOps> mPendingWrites = new LinkedList<>();
+
+    // Lock for read/write access to on disk state
+    private final Object mOnDiskLock = new Object();
+
+    //Lock for read/write access to in memory state
+    private final @NonNull Object mInMemoryLock;
+
+    private static final int MSG_WRITE_PENDING_HISTORY = 1;
+
+    // See mMode
+    private static final int DEFAULT_MODE = AppOpsManager.HISTORICAL_MODE_DISABLED;
+
+    // See mBaseSnapshotInterval
+    private static final long DEFAULT_SNAPSHOT_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(15);
+
+    // See mIntervalCompressionMultiplier
+    private static final long DEFAULT_COMPRESSION_STEP = 10;
+
+    /**
+     * Whether history is enabled.
+     */
+    @GuardedBy("mInMemoryLock")
+    private int mMode = AppOpsManager.HISTORICAL_MODE_DISABLED;
+
+    /**
+     * This granularity has been chosen to allow clean delineation for intervals
+     * humans understand, 15 min, 60, min, a day, a week, a month (30 days).
+     */
+    @GuardedBy("mInMemoryLock")
+    private long mBaseSnapshotInterval = DEFAULT_SNAPSHOT_INTERVAL_MILLIS;
+
+    /**
+     * The compression between steps. Each subsequent step is this much longer
+     * in terms of duration and each snapshot is this much more apart from the
+     * previous step.
+     */
+    @GuardedBy("mInMemoryLock")
+    private long mIntervalCompressionMultiplier = DEFAULT_COMPRESSION_STEP;
+
+    // The current ops to which to add statistics.
+    @GuardedBy("mInMemoryLock")
+    private @Nullable HistoricalOps mCurrentHistoricalOps;
+
+    // The time we should write the next snapshot.
+    @GuardedBy("mInMemoryLock")
+    private long mNextPersistDueTimeMillis;
+
+    // How much to offset the history on the next write.
+    @GuardedBy("mInMemoryLock")
+    private long mPendingHistoryOffsetMillis;
+
+    // Object managing persistence (read/write)
+    @GuardedBy("mOnDiskLock")
+    private Persistence mPersistence = new Persistence(mBaseSnapshotInterval,
+            mIntervalCompressionMultiplier);
+
+    HistoricalRegistry(@NonNull Object lock) {
+        mInMemoryLock = lock;
+        if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) {
+            synchronized (mInMemoryLock) {
+                // When starting always adjust history to now.
+                mPendingHistoryOffsetMillis = System.currentTimeMillis()
+                        - mPersistence.getLastPersistTimeMillisDLocked();
+            }
+        }
+    }
+
+    void systemReady(@NonNull ContentResolver resolver) {
+        updateParametersFromSetting(resolver);
+        final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS);
+        resolver.registerContentObserver(uri, false, new ContentObserver(
+                FgThread.getHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateParametersFromSetting(resolver);
+            }
+        });
+    }
+
+    private void updateParametersFromSetting(@NonNull ContentResolver resolver) {
+        final String setting = Settings.Global.getString(resolver,
+                Settings.Global.APPOP_HISTORY_PARAMETERS);
+        if (setting == null) {
+            return;
+        }
+        String modeValue = null;
+        String baseSnapshotIntervalValue = null;
+        String intervalMultiplierValue = null;
+        final String[] parameters = setting.split(PARAMETER_DELIMITER);
+        for (String parameter : parameters) {
+            final String[] parts = parameter.split(PARAMETER_ASSIGNMENT);
+            if (parts.length == 2) {
+                final String key = parts[0].trim();
+                switch (key) {
+                    case Settings.Global.APPOP_HISTORY_MODE: {
+                        modeValue = parts[1].trim();
+                    } break;
+                    case Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS: {
+                        baseSnapshotIntervalValue = parts[1].trim();
+                    } break;
+                    case Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER: {
+                        intervalMultiplierValue = parts[1].trim();
+                    } break;
+                    default: {
+                        Slog.w(LOG_TAG, "Unknown parameter: " + parameter);
+                    }
+                }
+            }
+        }
+        if (modeValue != null && baseSnapshotIntervalValue != null
+                && intervalMultiplierValue != null) {
+            try {
+                final int mode = AppOpsManager.parseHistoricalMode(modeValue);
+                final long baseSnapshotInterval = Long.parseLong(baseSnapshotIntervalValue);
+                final int intervalCompressionMultiplier = Integer.parseInt(intervalMultiplierValue);
+                setHistoryParameters(mode, baseSnapshotInterval,intervalCompressionMultiplier);
+                return;
+            } catch (NumberFormatException ignored) {}
+        }
+        Slog.w(LOG_TAG, "Bad value for" + Settings.Global.APPOP_HISTORY_PARAMETERS
+                + "=" + setting + " resetting!");
+    }
+
+    void dump(String prefix, PrintWriter pw,  int filterUid,
+            String filterPackage, int filterOp) {
+        synchronized (mOnDiskLock) {
+            synchronized (mInMemoryLock) {
+                pw.println();
+                pw.print(prefix);
+                pw.print("History:");
+
+                pw.print("  mode=");
+                pw.println(AppOpsManager.historicalModeToString(mMode));
+
+                final StringDumpVisitor visitor = new StringDumpVisitor(prefix + "  ",
+                        pw, filterUid, filterPackage, filterOp);
+                final long nowMillis = System.currentTimeMillis();
+
+                // Dump in memory state first
+                final HistoricalOps currentOps = getUpdatedPendingHistoricalOpsMLocked(
+                        nowMillis);
+                makeRelativeToEpochStart(currentOps, nowMillis);
+                currentOps.accept(visitor);
+
+                final List<HistoricalOps> ops = mPersistence.readHistoryDLocked();
+                if (ops != null) {
+                    // TODO (bug:122218838): Make sure this is properly dumped
+                    final long remainingToFillBatchMillis = mNextPersistDueTimeMillis
+                            - nowMillis - mBaseSnapshotInterval;
+                    final int opCount = ops.size();
+                    for (int i = 0; i < opCount; i++) {
+                        final HistoricalOps op = ops.get(i);
+                        op.offsetBeginAndEndTime(remainingToFillBatchMillis);
+                        makeRelativeToEpochStart(op, nowMillis);
+                        op.accept(visitor);
+                    }
+                } else {
+                    pw.println("  Empty");
+                }
+            }
+        }
+    }
+
+    @HistoricalMode int getMode() {
+        synchronized (mInMemoryLock) {
+            return mMode;
+        }
+    }
+
+    @Nullable void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @NonNull RemoteCallback callback) {
+        final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
+        mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
+                beginTimeMillis, endTimeMillis);
+        final Bundle payload = new Bundle();
+        payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
+        callback.sendResult(payload);
+    }
+
+    @Nullable void getHistoricalOps(int uid, @NonNull String packageName,
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @NonNull RemoteCallback callback) {
+        final long currentTimeMillis = System.currentTimeMillis();
+        if (endTimeMillis == Long.MAX_VALUE) {
+            endTimeMillis = currentTimeMillis;
+        }
+
+        // Argument times are based off epoch start while our internal store is
+        // based off now, so take this into account.
+        final long inMemoryAdjBeginTimeMillis = Math.max(currentTimeMillis - endTimeMillis, 0);
+        final long inMemoryAdjEndTimeMillis = Math.max(currentTimeMillis - beginTimeMillis, 0);
+        final HistoricalOps result = new HistoricalOps(inMemoryAdjBeginTimeMillis,
+                inMemoryAdjEndTimeMillis);
+
+        synchronized (mOnDiskLock) {
+            final List<HistoricalOps> pendingWrites;
+            final HistoricalOps currentOps;
+            synchronized (mInMemoryLock) {
+                currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis);
+                if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis()
+                        || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
+                    // Some of the current batch falls into the query, so extract that.
+                    final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps);
+                    currentOpsCopy.filter(uid, packageName, opNames, inMemoryAdjBeginTimeMillis,
+                            inMemoryAdjEndTimeMillis);
+                    result.merge(currentOpsCopy);
+                }
+                pendingWrites = new ArrayList<>(mPendingWrites);
+                mPendingWrites.clear();
+            }
+
+            // If the query was only for in-memory state - done.
+            if (inMemoryAdjEndTimeMillis > currentOps.getEndTimeMillis()) {
+                // If there is a write in flight we need to force it now
+                persistPendingHistory(pendingWrites);
+                // Collect persisted state.
+                final long onDiskAndInMemoryOffsetMillis = currentTimeMillis
+                        - mNextPersistDueTimeMillis + mBaseSnapshotInterval;
+                final long onDiskAdjBeginTimeMillis = Math.max(inMemoryAdjBeginTimeMillis
+                        - onDiskAndInMemoryOffsetMillis, 0);
+                final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
+                        - onDiskAndInMemoryOffsetMillis, 0);
+                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
+                        onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis);
+            }
+
+            // Rebase the result time to be since epoch.
+            result.setBeginAndEndTime(beginTimeMillis, endTimeMillis);
+
+            // Send back the result.
+            final Bundle payload = new Bundle();
+            payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
+            callback.sendResult(payload);
+        }
+    }
+
+    void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
+            @UidState int uidState) {
+        synchronized (mInMemoryLock) {
+            if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
+                getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
+                        .increaseAccessCount(op, uid, packageName, uidState, 1);
+
+            }
+        }
+    }
+
+    void incrementOpRejected(int op, int uid, @NonNull String packageName,
+            @UidState int uidState) {
+        synchronized (mInMemoryLock) {
+            if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
+                getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
+                        .increaseRejectCount(op, uid, packageName, uidState, 1);
+            }
+        }
+    }
+
+    void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
+            @UidState int uidState, long increment) {
+        synchronized (mInMemoryLock) {
+            if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
+                getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
+                        .increaseAccessDuration(op, uid, packageName, uidState, increment);
+            }
+        }
+    }
+
+    void setHistoryParameters(@HistoricalMode int mode,
+            long baseSnapshotInterval, long intervalCompressionMultiplier) {
+        synchronized (mOnDiskLock) {
+            synchronized (mInMemoryLock) {
+                boolean resampleHistory = false;
+                Slog.i(LOG_TAG, "New history parameters: mode:"
+                        + AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:"
+                        + baseSnapshotInterval + " intervalCompressionMultiplier:"
+                        + intervalCompressionMultiplier);
+                if (mMode != mode) {
+                    mMode = mode;
+                    if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) {
+                        clearHistoryOnDiskLocked();
+                    }
+                }
+                if (mBaseSnapshotInterval != baseSnapshotInterval) {
+                    mBaseSnapshotInterval = baseSnapshotInterval;
+                    resampleHistory = true;
+                }
+                if (mIntervalCompressionMultiplier != intervalCompressionMultiplier) {
+                    mIntervalCompressionMultiplier = intervalCompressionMultiplier;
+                    resampleHistory = true;
+                }
+                if (resampleHistory) {
+                    resampleHistoryOnDiskInMemoryDMLocked(0);
+                }
+            }
+        }
+    }
+
+    void offsetHistory(long offsetMillis) {
+        synchronized (mOnDiskLock) {
+            synchronized (mInMemoryLock) {
+                final List<HistoricalOps> history = mPersistence.readHistoryDLocked();
+                clearHistory();
+                if (history != null) {
+                    final int historySize = history.size();
+                    for (int i = 0; i < historySize; i++) {
+                        final HistoricalOps ops = history.get(i);
+                        ops.offsetBeginAndEndTime(offsetMillis);
+                    }
+                    if (offsetMillis < 0) {
+                        pruneFutureOps(history);
+                    }
+                    mPersistence.persistHistoricalOpsDLocked(history);
+                }
+            }
+        }
+    }
+
+    void addHistoricalOps(HistoricalOps ops) {
+        final List<HistoricalOps> pendingWrites;
+        synchronized (mInMemoryLock) {
+            // The history files start from mBaseSnapshotInterval - take this into account.
+            ops.offsetBeginAndEndTime(mBaseSnapshotInterval);
+            mPendingWrites.offerFirst(ops);
+            pendingWrites = new ArrayList<>(mPendingWrites);
+            mPendingWrites.clear();
+        }
+        persistPendingHistory(pendingWrites);
+    }
+
+    private void resampleHistoryOnDiskInMemoryDMLocked(long offsetMillis) {
+        mPersistence = new Persistence(mBaseSnapshotInterval, mIntervalCompressionMultiplier);
+        offsetHistory(offsetMillis);
+    }
+
+    void resetHistoryParameters() {
+        setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS,
+                DEFAULT_COMPRESSION_STEP);
+    }
+
+    void clearHistory() {
+        synchronized (mOnDiskLock) {
+            clearHistoryOnDiskLocked();
+        }
+    }
+
+    private void clearHistoryOnDiskLocked() {
+        BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY);
+        synchronized (mInMemoryLock) {
+            mCurrentHistoricalOps = null;
+            mNextPersistDueTimeMillis = System.currentTimeMillis();
+            mPendingWrites.clear();
+        }
+        mPersistence.clearHistoryDLocked();
+    }
+
+    private @NonNull HistoricalOps getUpdatedPendingHistoricalOpsMLocked(long now) {
+        if (mCurrentHistoricalOps != null) {
+            final long remainingTimeMillis = mNextPersistDueTimeMillis - now;
+            if (remainingTimeMillis > mBaseSnapshotInterval) {
+                // If time went backwards we need to push history to the future with the
+                // overflow over our snapshot interval. If time went forward do nothing
+                // as we would naturally push history into the past on the next write.
+                mPendingHistoryOffsetMillis = remainingTimeMillis - mBaseSnapshotInterval;
+            }
+            final long elapsedTimeMillis = mBaseSnapshotInterval - remainingTimeMillis;
+            mCurrentHistoricalOps.setEndTime(elapsedTimeMillis);
+            if (remainingTimeMillis > 0) {
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Returning current in-memory state");
+                }
+                return mCurrentHistoricalOps;
+            }
+            if (mCurrentHistoricalOps.isEmpty()) {
+                mCurrentHistoricalOps.setBeginAndEndTime(0, 0);
+                mNextPersistDueTimeMillis = now + mBaseSnapshotInterval;
+                return mCurrentHistoricalOps;
+            }
+            // The current batch is full, so persist taking into account overdue persist time.
+            mCurrentHistoricalOps.offsetBeginAndEndTime(mBaseSnapshotInterval);
+            mCurrentHistoricalOps.setBeginTime(mCurrentHistoricalOps.getEndTimeMillis()
+                    - mBaseSnapshotInterval);
+            final long overdueTimeMillis = Math.abs(remainingTimeMillis);
+            mCurrentHistoricalOps.offsetBeginAndEndTime(overdueTimeMillis);
+            schedulePersistHistoricalOpsMLocked(mCurrentHistoricalOps);
+        }
+        // The current batch is in the future, i.e. not complete yet.
+        mCurrentHistoricalOps = new HistoricalOps(0, 0);
+        mNextPersistDueTimeMillis = now + mBaseSnapshotInterval;
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "Returning new in-memory state");
+        }
+        return mCurrentHistoricalOps;
+    }
+
+    private void persistPendingHistory() {
+        final List<HistoricalOps> pendingWrites;
+        synchronized (mOnDiskLock) {
+            synchronized (mInMemoryLock) {
+                pendingWrites = new ArrayList<>(mPendingWrites);
+                mPendingWrites.clear();
+                if (mPendingHistoryOffsetMillis != 0) {
+                    resampleHistoryOnDiskInMemoryDMLocked(mPendingHistoryOffsetMillis);
+                    mPendingHistoryOffsetMillis = 0;
+                }
+            }
+            persistPendingHistory(pendingWrites);
+        }
+    }
+    private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) {
+        synchronized (mOnDiskLock) {
+            BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY);
+            if (pendingWrites.isEmpty()) {
+                return;
+            }
+            final int opCount = pendingWrites.size();
+            // Pending writes are offset relative to each other, so take this
+            // into account to persist everything in one shot - single write.
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalOps current = pendingWrites.get(i);
+                if (i > 0) {
+                    final HistoricalOps previous = pendingWrites.get(i - 1);
+                    current.offsetBeginAndEndTime(previous.getBeginTimeMillis());
+                }
+            }
+            mPersistence.persistHistoricalOpsDLocked(pendingWrites);
+        }
+    }
+
+    private void schedulePersistHistoricalOpsMLocked(@NonNull HistoricalOps ops) {
+        final Message message = PooledLambda.obtainMessage(
+                HistoricalRegistry::persistPendingHistory, HistoricalRegistry.this);
+        message.what = MSG_WRITE_PENDING_HISTORY;
+        BackgroundThread.getHandler().sendMessage(message);
+        mPendingWrites.offerFirst(ops);
+    }
+
+    private static void makeRelativeToEpochStart(@NonNull HistoricalOps ops, long nowMillis) {
+        ops.setBeginAndEndTime(nowMillis - ops.getEndTimeMillis(),
+                nowMillis- ops.getBeginTimeMillis());
+    }
+
+    private void pruneFutureOps(@NonNull List<HistoricalOps> ops) {
+        final int opCount = ops.size();
+        for (int i = opCount - 1; i >= 0; i--) {
+            final HistoricalOps op = ops.get(i);
+            if (op.getEndTimeMillis() <= mBaseSnapshotInterval) {
+                ops.remove(i);
+            } else if (op.getBeginTimeMillis() < mBaseSnapshotInterval) {
+                final double filterScale = (double) (op.getEndTimeMillis() - mBaseSnapshotInterval)
+                        / (double) op.getDurationMillis();
+                Persistence.spliceFromBeginning(op, filterScale);
+            }
+        }
+    }
+
+    private static final class Persistence {
+        private static final boolean DEBUG = false;
+
+        private static final String LOG_TAG = Persistence.class.getSimpleName();
+
+        private static final String HISTORY_FILE_SUFFIX = ".xml";
+
+        private static final String TAG_HISTORY = "history";
+        private static final String TAG_OPS = "ops";
+        private static final String TAG_UID = "uid";
+        private static final String TAG_PACKAGE = "package";
+        private static final String TAG_OP = "op";
+        private static final String TAG_STATE = "state";
+
+        private static final String ATTR_VERSION = "version";
+        private static final String ATTR_NAME = "name";
+        private static final String ATTR_ACCESS_COUNT = "accessCount";
+        private static final String ATTR_REJECT_COUNT = "rejectCount";
+        private static final String ATTR_ACCESS_DURATION = "accessDuration";
+        private static final String ATTR_BEGIN_TIME = "beginTime";
+        private static final String ATTR_END_TIME = "endTime";
+        private static final String ATTR_OVERFLOW = "overflow";
+
+        private static final int CURRENT_VERSION = 1;
+
+        private final long mBaseSnapshotInterval;
+        private final long mIntervalCompressionMultiplier;
+
+        Persistence(long baseSnapshotInterval, long intervalCompressionMultiplier) {
+            mBaseSnapshotInterval = baseSnapshotInterval;
+            mIntervalCompressionMultiplier = intervalCompressionMultiplier;
+        }
+
+        private final AtomicDirectory mHistoricalAppOpsDir = new AtomicDirectory(
+                new File(new File(Environment.getDataSystemDeDirectory(), "appops"), "history"));
+
+        private File generateFile(@NonNull File baseDir, int depth) {
+            final long globalBeginMillis = computeGlobalIntervalBeginMillis(depth);
+            return new File(baseDir, Long.toString(globalBeginMillis) + HISTORY_FILE_SUFFIX);
+        }
+
+        void clearHistoryDLocked() {
+            mHistoricalAppOpsDir.delete();
+        }
+
+        void persistHistoricalOpsDLocked(@NonNull List<HistoricalOps> ops) {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Persisting ops:\n" + opsToDebugString(ops));
+                enforceOpsWellFormed(ops);
+            }
+            try {
+                final File newBaseDir = mHistoricalAppOpsDir.startWrite();
+                final File oldBaseDir = mHistoricalAppOpsDir.getBackupDirectory();
+                handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, ops, 0);
+                mHistoricalAppOpsDir.finishWrite();
+            } catch (Throwable t) {
+                Slog.wtf(LOG_TAG, "Failed to write historical app ops, restoring backup", t);
+                mHistoricalAppOpsDir.failWrite();
+            }
+        }
+
+        @Nullable List<HistoricalOps> readHistoryRawDLocked() {
+            return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/,
+                    null /*filterPackageName*/, null /*filterOpNames*/,
+                    0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/);
+        }
+
+        @Nullable List<HistoricalOps> readHistoryDLocked() {
+            final List<HistoricalOps> result = readHistoryRawDLocked();
+            // Take into account in memory state duration.
+            if (result != null) {
+                final int opCount = result.size();
+                for (int i = 0; i < opCount; i++) {
+                    result.get(i).offsetBeginAndEndTime(mBaseSnapshotInterval);
+                }
+            }
+            return result;
+        }
+
+        long getLastPersistTimeMillisDLocked() {
+            try {
+                final File baseDir = mHistoricalAppOpsDir.startRead();
+                final File file = generateFile(baseDir, 0);
+                if (file.exists()) {
+                    return file.lastModified();
+                }
+                mHistoricalAppOpsDir.finishRead();
+            } catch (IOException e) {
+                Slog.wtf("Error reading historical app ops. Deleting history.", e);
+                mHistoricalAppOpsDir.delete();
+            }
+            return 0;
+        }
+
+        private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps,
+                int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
+                long filterBeingMillis, long filterEndMillis) {
+            final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid,
+                    filterPackageName, filterOpNames, filterBeingMillis, filterEndMillis);
+            if (readOps != null) {
+                final int readCount = readOps.size();
+                for (int i = 0; i < readCount; i++) {
+                    final HistoricalOps readOp = readOps.get(i);
+                    currentOps.merge(readOp);
+                }
+             }
+        }
+
+        private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(
+                int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
+                long filterBeginTimeMillis, long filterEndTimeMillis) {
+            try {
+                final File baseDir = mHistoricalAppOpsDir.startRead();
+                final File[] files = baseDir.listFiles();
+                if (files == null) {
+                    return null;
+                }
+                final ArraySet<File> historyFiles = new ArraySet<>(files.length);
+                for (File file : files) {
+                    if (file.isFile() && file.getName().endsWith(HISTORY_FILE_SUFFIX)) {
+                        historyFiles.add(file);
+                    }
+                }
+                final long[] globalContentOffsetMillis = {0};
+                final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked(
+                        baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
+                        filterEndTimeMillis, globalContentOffsetMillis, null /*outOps*/,
+                        0 /*depth*/, historyFiles);
+                mHistoricalAppOpsDir.finishRead();
+                return ops;
+            } catch (IOException | XmlPullParserException e) {
+                Slog.wtf("Error reading historical app ops. Deleting history.", e);
+                mHistoricalAppOpsDir.delete();
+            }
+            return null;
+        }
+
+        private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked(
+                @NonNull File baseDir, int filterUid, @NonNull String filterPackageName,
+                @Nullable String[] filterOpNames, long filterBeginTimeMillis,
+                long filterEndTimeMillis, @NonNull long[] globalContentOffsetMillis,
+                @Nullable LinkedList<HistoricalOps> outOps, int depth,
+                @NonNull ArraySet<File> historyFiles)
+                throws IOException, XmlPullParserException {
+            final long previousIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier,
+                    depth) * mBaseSnapshotInterval;
+            final long currentIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier,
+                    depth + 1) * mBaseSnapshotInterval;
+
+            filterBeginTimeMillis = Math.max(filterBeginTimeMillis - previousIntervalEndMillis, 0);
+            filterEndTimeMillis = filterEndTimeMillis - previousIntervalEndMillis;
+
+            // Read historical data at this level
+            final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir,
+                    previousIntervalEndMillis, currentIntervalEndMillis, filterUid,
+                    filterPackageName, filterOpNames, filterBeginTimeMillis, filterEndTimeMillis,
+                    globalContentOffsetMillis, depth, historyFiles);
+
+            // Empty is a special signal to stop diving
+            if (readOps != null && readOps.isEmpty()) {
+                return outOps;
+            }
+
+            // Collect older historical data from subsequent levels
+            outOps = collectHistoricalOpsRecursiveDLocked(
+                    baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
+                    filterEndTimeMillis, globalContentOffsetMillis, outOps, depth + 1,
+                    historyFiles);
+
+            // Make older historical data relative to the current historical level
+            if (outOps != null) {
+                final int opCount = outOps.size();
+                for (int i = 0; i < opCount; i++) {
+                    final HistoricalOps collectedOp = outOps.get(i);
+                    collectedOp.offsetBeginAndEndTime(currentIntervalEndMillis);
+                }
+            }
+
+            if (readOps != null) {
+                if (outOps == null) {
+                    outOps = new LinkedList<>();
+                }
+                // Add the read ops to output
+                final int opCount = readOps.size();
+                for (int i = opCount - 1; i >= 0; i--) {
+                    outOps.offerFirst(readOps.get(i));
+                }
+            }
+
+            return outOps;
+        }
+
+        private boolean createHardLinkToExistingFile(@NonNull File fromFile, @NonNull File toFile)
+                throws IOException {
+            if (!fromFile.exists()) {
+                return false;
+            }
+            Files.createLink(toFile.toPath(), fromFile.toPath());
+            return true;
+        }
+
+        private void handlePersistHistoricalOpsRecursiveDLocked(@NonNull File newBaseDir,
+                @NonNull File oldBaseDir, @Nullable List<HistoricalOps> passedOps, int depth)
+                throws IOException, XmlPullParserException {
+            final long previousIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier,
+                    depth) * mBaseSnapshotInterval;
+            final long currentIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier,
+                    depth + 1) * mBaseSnapshotInterval;
+
+            if (passedOps == null || passedOps.isEmpty()) {
+                // If there is an old file we need to copy it over to the new state.
+                final File oldFile = generateFile(oldBaseDir, depth);
+                final File newFile = generateFile(newBaseDir, depth);
+                if (createHardLinkToExistingFile(oldFile, newFile)) {
+                    handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir,
+                            passedOps, depth + 1);
+                }
+                return;
+            }
+
+            if (DEBUG) {
+                enforceOpsWellFormed(passedOps);
+            }
+
+            // Normalize passed ops time to be based off this interval start
+            final int passedOpCount = passedOps.size();
+            for (int i = 0; i < passedOpCount; i++) {
+                final HistoricalOps passedOp = passedOps.get(i);
+                passedOp.offsetBeginAndEndTime(-previousIntervalEndMillis);
+            }
+
+            if (DEBUG) {
+                enforceOpsWellFormed(passedOps);
+            }
+
+            // Read persisted ops for this interval
+            final List<HistoricalOps> existingOps = readHistoricalOpsLocked(oldBaseDir,
+                    previousIntervalEndMillis, currentIntervalEndMillis,
+                    Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/,
+                    null /*filterOpNames*/, Long.MIN_VALUE /*filterBeginTimeMillis*/,
+                    Long.MAX_VALUE /*filterEndTimeMillis*/, null, depth,
+                    null /*historyFiles*/);
+
+            if (DEBUG) {
+                enforceOpsWellFormed(existingOps);
+            }
+
+            // Offset existing ops to account for elapsed time
+            final int existingOpCount = existingOps.size();
+            if (existingOpCount > 0) {
+                // Compute elapsed time
+                final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1)
+                        .getEndTimeMillis();
+                for (int i = 0; i < existingOpCount; i++) {
+                    final HistoricalOps existingOp = existingOps.get(i);
+                    existingOp.offsetBeginAndEndTime(elapsedTimeMillis);
+                }
+            }
+
+            if (DEBUG) {
+                enforceOpsWellFormed(existingOps);
+            }
+
+            final long slotDurationMillis = previousIntervalEndMillis;
+
+            // Consolidate passed ops at the current slot duration ensuring each snapshot is
+            // full. To achieve this we put all passed and existing ops in a list and will
+            // merge them to ensure each represents a snapshot at the current granularity.
+            final List<HistoricalOps> allOps = new LinkedList<>();
+            allOps.addAll(passedOps);
+            allOps.addAll(existingOps);
+
+            if (DEBUG) {
+                enforceOpsWellFormed(allOps);
+            }
+
+            // Compute ops to persist and overflow ops
+            List<HistoricalOps> persistedOps = null;
+            List<HistoricalOps> overflowedOps = null;
+
+            // We move a snapshot into the next level only if the start time is
+            // after the end of the current interval. This avoids rewriting all
+            // files to propagate the information added to the history on every
+            // iteration. Instead, we would rewrite the next level file only if
+            // an entire snapshot from the previous level is being propagated.
+            // The trade off is that we need to store how much the last snapshot
+            // of the current interval overflows past the interval end. We write
+            // the overflow data to avoid parsing all snapshots on query.
+            long intervalOverflowMillis = 0;
+            final int opCount = allOps.size();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalOps op = allOps.get(i);
+                final HistoricalOps persistedOp;
+                final HistoricalOps overflowedOp;
+                if (op.getEndTimeMillis() <= currentIntervalEndMillis) {
+                    persistedOp = op;
+                    overflowedOp = null;
+                } else if (op.getBeginTimeMillis() < currentIntervalEndMillis) {
+                    persistedOp = op;
+                    intervalOverflowMillis = op.getEndTimeMillis() - currentIntervalEndMillis;
+                    if (intervalOverflowMillis > previousIntervalEndMillis) {
+                        final double splitScale = (double) intervalOverflowMillis
+                                / op.getDurationMillis();
+                        overflowedOp = spliceFromEnd(op, splitScale);
+                        intervalOverflowMillis = op.getEndTimeMillis() - currentIntervalEndMillis;
+                    } else {
+                        overflowedOp = null;
+                    }
+                } else {
+                    persistedOp = null;
+                    overflowedOp = op;
+                }
+                if (persistedOp != null) {
+                    if (persistedOps == null) {
+                        persistedOps = new ArrayList<>();
+                    }
+                    persistedOps.add(persistedOp);
+                }
+                if (overflowedOp != null) {
+                    if (overflowedOps == null) {
+                        overflowedOps = new ArrayList<>();
+                    }
+                    overflowedOps.add(overflowedOp);
+                }
+            }
+
+            if (DEBUG) {
+                enforceOpsWellFormed(persistedOps);
+                enforceOpsWellFormed(overflowedOps);
+            }
+
+            if (persistedOps != null) {
+                normalizeSnapshotForSlotDuration(persistedOps, slotDurationMillis);
+                final File newFile = generateFile(newBaseDir, depth);
+                writeHistoricalOpsDLocked(persistedOps, intervalOverflowMillis, newFile);
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Persisted at depth: " + depth
+                            + " ops:\n" + opsToDebugString(persistedOps));
+                    enforceOpsWellFormed(persistedOps);
+                }
+            }
+
+            handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir,
+                    overflowedOps, depth + 1);
+        }
+
+        private @NonNull List<HistoricalOps> readHistoricalOpsLocked(File baseDir,
+                long intervalBeginMillis, long intervalEndMillis, int filterUid,
+                @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                long filterBeginTimeMillis, long filterEndTimeMillis,
+                @Nullable long[] cumulativeOverflowMillis, int depth,
+                @NonNull ArraySet<File> historyFiles)
+                throws IOException, XmlPullParserException {
+            final File file = generateFile(baseDir, depth);
+            if (historyFiles != null) {
+                historyFiles.remove(file);
+            }
+            if (filterBeginTimeMillis >= filterEndTimeMillis
+                    || filterEndTimeMillis < intervalBeginMillis) {
+                // Don't go deeper
+                return Collections.emptyList();
+            }
+            if (filterBeginTimeMillis >= (intervalEndMillis
+                    + ((intervalEndMillis - intervalBeginMillis) / mIntervalCompressionMultiplier)
+                    + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0))
+                    || !file.exists()) {
+                if (historyFiles == null || historyFiles.isEmpty()) {
+                    // Don't go deeper
+                    return Collections.emptyList();
+                } else {
+                    // Keep diving
+                    return null;
+                }
+            }
+            return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterOpNames,
+                    filterBeginTimeMillis, filterEndTimeMillis, cumulativeOverflowMillis);
+        }
+
+        private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
+                int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                long filterBeginTimeMillis, long filterEndTimeMillis,
+                @Nullable long[] cumulativeOverflowMillis)
+                throws IOException, XmlPullParserException {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Reading ops from:" + file);
+            }
+            List<HistoricalOps> allOps = null;
+            try (FileInputStream stream = new FileInputStream(file)) {
+                final XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(stream, StandardCharsets.UTF_8.name());
+                XmlUtils.beginDocument(parser, TAG_HISTORY);
+                final long overflowMillis = XmlUtils.readLongAttribute(parser, ATTR_OVERFLOW, 0);
+                final int depth = parser.getDepth();
+                while (XmlUtils.nextElementWithin(parser, depth)) {
+                    if (TAG_OPS.equals(parser.getName())) {
+                        final HistoricalOps ops = readeHistoricalOpsDLocked(parser,
+                                filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
+                                filterEndTimeMillis, cumulativeOverflowMillis);
+                        if (ops == null) {
+                            continue;
+                        }
+                        if (ops.isEmpty()) {
+                            XmlUtils.skipCurrentTag(parser);
+                            continue;
+                        }
+                        if (allOps == null) {
+                            allOps = new ArrayList<>();
+                        }
+                        allOps.add(ops);
+                    }
+                }
+                if (cumulativeOverflowMillis != null) {
+                    cumulativeOverflowMillis[0] += overflowMillis;
+                }
+            } catch (FileNotFoundException e) {
+                Slog.i(LOG_TAG, "No history file: " + file.getName());
+                return Collections.emptyList();
+            }
+            if (DEBUG) {
+                if (allOps != null) {
+                    Slog.i(LOG_TAG, "Read from file: " + file + "ops:\n"
+                            + opsToDebugString(allOps));
+                    enforceOpsWellFormed(allOps);
+                }
+            }
+            return allOps;
+        }
+
+        private @Nullable HistoricalOps readeHistoricalOpsDLocked(
+                @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
+                @Nullable String[] filterOpNames, long filterBeginTimeMillis,
+                long filterEndTimeMillis, @Nullable long[] cumulativeOverflowMillis)
+                throws IOException, XmlPullParserException {
+            final long beginTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_BEGIN_TIME, 0)
+                    + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0);
+            final long endTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_END_TIME, 0)
+                    + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0);
+            // Keep reading as subsequent records may start matching
+            if (filterEndTimeMillis < beginTimeMillis) {
+                return null;
+            }
+            // Stop reading as subsequent records will not match
+            if (filterBeginTimeMillis > endTimeMillis) {
+                return new HistoricalOps(0, 0);
+            }
+            final long filteredBeginTimeMillis = Math.max(beginTimeMillis, filterBeginTimeMillis);
+            final long filteredEndTimeMillis = Math.min(endTimeMillis, filterEndTimeMillis);
+            final double filterScale = (double) (filteredEndTimeMillis - filteredBeginTimeMillis)
+                    / (double) (endTimeMillis - beginTimeMillis);
+            HistoricalOps ops = null;
+            final int depth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, depth)) {
+                if (TAG_UID.equals(parser.getName())) {
+                    final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser,
+                            filterUid, filterPackageName, filterOpNames, filterScale);
+                    if (ops == null) {
+                        ops = returnedOps;
+                    }
+                }
+            }
+            if (ops != null) {
+                ops.setBeginAndEndTime(filteredBeginTimeMillis, filteredEndTimeMillis);
+            }
+            return ops;
+        }
+
+        private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
+                @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
+                @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                double filterScale) throws IOException, XmlPullParserException {
+            final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+            if (filterUid != Process.INVALID_UID && filterUid != uid) {
+                XmlUtils.skipCurrentTag(parser);
+                return null;
+            }
+            final int depth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, depth)) {
+                if (TAG_PACKAGE.equals(parser.getName())) {
+                    final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops,
+                            uid, parser, filterPackageName, filterOpNames, filterScale);
+                    if (ops == null) {
+                        ops = returnedOps;
+                    }
+                }
+            }
+            return ops;
+        }
+
+        private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
+                @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
+                @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                double filterScale) throws IOException, XmlPullParserException {
+            final String packageName = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+            if (filterPackageName != null && !filterPackageName.equals(packageName)) {
+                XmlUtils.skipCurrentTag(parser);
+                return null;
+            }
+            final int depth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, depth)) {
+                if (TAG_OP.equals(parser.getName())) {
+                    final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid,
+                            packageName, parser, filterOpNames, filterScale);
+                    if (ops == null) {
+                        ops = returnedOps;
+                    }
+                }
+            }
+            return ops;
+        }
+
+        private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
+                int uid, String packageName, @NonNull XmlPullParser parser,
+                @Nullable String[] filterOpNames, double filterScale)
+                throws IOException, XmlPullParserException {
+            final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+            if (filterOpNames != null && !ArrayUtils.contains(filterOpNames,
+                    AppOpsManager.opToName(op))) {
+                XmlUtils.skipCurrentTag(parser);
+                return null;
+            }
+            final int depth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, depth)) {
+                if (TAG_STATE.equals(parser.getName())) {
+                    final HistoricalOps returnedOps = readUidStateDLocked(ops, uid,
+                            packageName, op, parser, filterScale);
+                    if (ops == null) {
+                        ops = returnedOps;
+                    }
+                }
+            }
+            return ops;
+        }
+
+        private @Nullable HistoricalOps readUidStateDLocked(@Nullable HistoricalOps ops,
+                int uid, String packageName, int op, @NonNull XmlPullParser parser,
+                double filterScale) throws IOException {
+            final int uidState = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+            long accessCount = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_COUNT, 0);
+            if (accessCount > 0) {
+                if (!Double.isNaN(filterScale)) {
+                    accessCount = (long) HistoricalOps.round(
+                            (double) accessCount * filterScale);
+                }
+                if (ops == null) {
+                    ops = new HistoricalOps(0, 0);
+                }
+                ops.increaseAccessCount(op, uid, packageName, uidState, accessCount);
+            }
+            long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
+            if (rejectCount > 0) {
+                if (!Double.isNaN(filterScale)) {
+                    rejectCount = (long) HistoricalOps.round(
+                            (double) rejectCount * filterScale);
+                }
+                if (ops == null) {
+                    ops = new HistoricalOps(0, 0);
+                }
+                ops.increaseRejectCount(op, uid, packageName, uidState, rejectCount);
+            }
+            long accessDuration =  XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
+            if (accessDuration > 0) {
+                if (!Double.isNaN(filterScale)) {
+                    accessDuration = (long) HistoricalOps.round(
+                            (double) accessDuration * filterScale);
+                }
+                if (ops == null) {
+                    ops = new HistoricalOps(0, 0);
+                }
+                ops.increaseAccessDuration(op, uid, packageName, uidState, accessDuration);
+            }
+            return ops;
+        }
+
+        private void writeHistoricalOpsDLocked(@Nullable List<HistoricalOps> allOps,
+                long intervalOverflowMillis, @NonNull File file) throws IOException {
+            final FileOutputStream output = mHistoricalAppOpsDir.openWrite(file);
+            try {
+                final XmlSerializer serializer = Xml.newSerializer();
+                serializer.setOutput(output, StandardCharsets.UTF_8.name());
+                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
+                        true);
+                serializer.startDocument(null, true);
+                serializer.startTag(null, TAG_HISTORY);
+                serializer.attribute(null, ATTR_VERSION, String.valueOf(CURRENT_VERSION));
+                if (intervalOverflowMillis != 0) {
+                    serializer.attribute(null, ATTR_OVERFLOW,
+                            Long.toString(intervalOverflowMillis));
+                }
+                if (allOps != null) {
+                    final int opsCount = allOps.size();
+                    for (int i = 0; i < opsCount; i++) {
+                        final HistoricalOps ops = allOps.get(i);
+                        writeHistoricalOpDLocked(ops, serializer);
+                    }
+                }
+                serializer.endTag(null, TAG_HISTORY);
+                serializer.endDocument();
+                mHistoricalAppOpsDir.closeWrite(output);
+            } catch (IOException e) {
+                mHistoricalAppOpsDir.failWrite(output);
+                throw e;
+            }
+        }
+
+        private void writeHistoricalOpDLocked(@NonNull HistoricalOps ops,
+                @NonNull XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, TAG_OPS);
+            serializer.attribute(null, ATTR_BEGIN_TIME, Long.toString(ops.getBeginTimeMillis()));
+            serializer.attribute(null, ATTR_END_TIME, Long.toString(ops.getEndTimeMillis()));
+            final int uidCount = ops.getUidCount();
+            for (int i = 0; i < uidCount; i++) {
+                final HistoricalUidOps uidOp = ops.getUidOpsAt(i);
+                writeHistoricalUidOpsDLocked(uidOp, serializer);
+            }
+            serializer.endTag(null, TAG_OPS);
+        }
+
+        private void writeHistoricalUidOpsDLocked(@NonNull HistoricalUidOps uidOps,
+                @NonNull XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, TAG_UID);
+            serializer.attribute(null, ATTR_NAME, Integer.toString(uidOps.getUid()));
+            final int packageCount = uidOps.getPackageCount();
+            for (int i = 0; i < packageCount; i++) {
+                final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(i);
+                writeHistoricalPackageOpsDLocked(packageOps, serializer);
+            }
+            serializer.endTag(null, TAG_UID);
+        }
+
+        private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps,
+                @NonNull XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, TAG_PACKAGE);
+            serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
+            final int opCount = packageOps.getOpCount();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalOp op = packageOps.getOpAt(i);
+                writeHistoricalOpDLocked(op, serializer);
+            }
+            serializer.endTag(null, TAG_PACKAGE);
+        }
+
+        private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
+                @NonNull XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, TAG_OP);
+            serializer.attribute(null, ATTR_NAME, Integer.toString(op.getOpCode()));
+            for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) {
+                writeUidStateOnLocked(op, uidState, serializer);
+            }
+            serializer.endTag(null, TAG_OP);
+        }
+
+        private void writeUidStateOnLocked(@NonNull HistoricalOp op, @UidState int uidState,
+                @NonNull XmlSerializer serializer) throws IOException {
+            final long accessCount = op.getAccessCount(uidState);
+            final long rejectCount = op.getRejectCount(uidState);
+            final long accessDuration = op.getAccessDuration(uidState);
+            if (accessCount == 0 && rejectCount == 0 && accessDuration == 0) {
+                return;
+            }
+            serializer.startTag(null, TAG_STATE);
+            serializer.attribute(null, ATTR_NAME, Integer.toString(uidState));
+            if (accessCount > 0) {
+                serializer.attribute(null, ATTR_ACCESS_COUNT, Long.toString(accessCount));
+            }
+            if (rejectCount > 0) {
+                serializer.attribute(null, ATTR_REJECT_COUNT, Long.toString(rejectCount));
+            }
+            if (accessDuration > 0) {
+                serializer.attribute(null, ATTR_ACCESS_DURATION, Long.toString(accessDuration));
+            }
+            serializer.endTag(null, TAG_STATE);
+        }
+
+        private static void enforceOpsWellFormed(@NonNull List<HistoricalOps> ops) {
+            if (ops == null) {
+                return;
+            }
+            HistoricalOps previous;
+            HistoricalOps current = null;
+            final int opsCount = ops.size();
+            for (int i = 0; i < opsCount; i++) {
+                previous = current;
+                current = ops.get(i);
+                if (current.isEmpty()) {
+                    throw new IllegalStateException("Empty ops:\n"
+                            + opsToDebugString(ops));
+                }
+                if (current.getEndTimeMillis() < current.getBeginTimeMillis()) {
+                    throw new IllegalStateException("Begin after end:\n"
+                            + opsToDebugString(ops));
+                }
+                if (previous != null) {
+                    if (previous.getEndTimeMillis() > current.getBeginTimeMillis()) {
+                        throw new IllegalStateException("Intersecting ops:\n"
+                                + opsToDebugString(ops));
+                    }
+                    if (previous.getBeginTimeMillis() > current.getBeginTimeMillis()) {
+                        throw new IllegalStateException("Non increasing ops:\n"
+                                + opsToDebugString(ops));
+                    }
+                }
+            }
+        }
+
+        private long computeGlobalIntervalBeginMillis(int depth) {
+            long beginTimeMillis = 0;
+            for (int i = 0; i < depth + 1; i++) {
+                beginTimeMillis += Math.pow(mIntervalCompressionMultiplier, i);
+            }
+            return beginTimeMillis * mBaseSnapshotInterval;
+        }
+
+        private static @NonNull HistoricalOps spliceFromEnd(@NonNull HistoricalOps ops,
+                double spliceRatio) {
+            if (DEBUG) {
+                Slog.w(LOG_TAG, "Splicing from end:" + ops + " ratio:" + spliceRatio);
+            }
+            final HistoricalOps splice = ops.spliceFromEnd(spliceRatio);
+            if (DEBUG) {
+                Slog.w(LOG_TAG, "Spliced into:" + ops + " and:" + splice);
+            }
+            return splice;
+        }
+
+
+        private static @NonNull HistoricalOps spliceFromBeginning(@NonNull HistoricalOps ops,
+                double spliceRatio) {
+            if (DEBUG) {
+                Slog.w(LOG_TAG, "Splicing from beginning:" + ops + " ratio:" + spliceRatio);
+            }
+            final HistoricalOps splice = ops.spliceFromBeginning(spliceRatio);
+            if (DEBUG) {
+                Slog.w(LOG_TAG, "Spliced into:" + ops + " and:" + splice);
+            }
+            return splice;
+        }
+
+        private static void normalizeSnapshotForSlotDuration(@NonNull List<HistoricalOps> ops,
+                long slotDurationMillis) {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Normalizing for slot duration: " + slotDurationMillis
+                        + " ops:\n" + opsToDebugString(ops));
+                enforceOpsWellFormed(ops);
+            }
+            long slotBeginTimeMillis;
+            final int opCount = ops.size();
+            for (int processedIdx = opCount - 1; processedIdx >= 0; processedIdx--) {
+                final HistoricalOps processedOp = ops.get(processedIdx);
+                slotBeginTimeMillis = Math.max(processedOp.getEndTimeMillis()
+                        - slotDurationMillis, 0);
+                for (int candidateIdx = processedIdx - 1; candidateIdx >= 0; candidateIdx--) {
+                    final HistoricalOps candidateOp = ops.get(candidateIdx);
+                    final long candidateSlotIntersectionMillis = candidateOp.getEndTimeMillis()
+                            - Math.min(slotBeginTimeMillis, processedOp.getBeginTimeMillis());
+                    if (candidateSlotIntersectionMillis <= 0) {
+                        break;
+                    }
+                    final float candidateSplitRatio = candidateSlotIntersectionMillis
+                            / (float) candidateOp.getDurationMillis();
+                    if (Float.compare(candidateSplitRatio, 1.0f) >= 0) {
+                        ops.remove(candidateIdx);
+                        processedIdx--;
+                        processedOp.merge(candidateOp);
+                    } else {
+                        final HistoricalOps endSplice = spliceFromEnd(candidateOp,
+                                candidateSplitRatio);
+                        if (endSplice != null) {
+                            processedOp.merge(endSplice);
+                        }
+                        if (candidateOp.isEmpty()) {
+                            ops.remove(candidateIdx);
+                            processedIdx--;
+                        }
+                    }
+                }
+            }
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Normalized for slot duration: " + slotDurationMillis
+                        + " ops:\n" + opsToDebugString(ops));
+                enforceOpsWellFormed(ops);
+            }
+        }
+
+        private static @NonNull String opsToDebugString(@NonNull List<HistoricalOps> ops) {
+            StringBuilder builder = new StringBuilder();
+            final int opCount = ops.size();
+            for (int i = 0; i < opCount; i++) {
+                builder.append("  ");
+                builder.append(ops.get(i));
+                if (i < opCount - 1) {
+                    builder.append('\n');
+                }
+            }
+            return builder.toString();
+        }
+    }
+
+    private final class StringDumpVisitor implements AppOpsManager.HistoricalOpsVisitor {
+        private final long mNow = System.currentTimeMillis();
+
+        private final SimpleDateFormat mDateFormatter = new SimpleDateFormat(
+                "yyyy-MM-dd HH:mm:ss.SSS");
+        private final Date mDate = new Date();
+
+        private final @NonNull String mOpsPrefix;
+        private final @NonNull String mUidPrefix;
+        private final @NonNull String mPackagePrefix;
+        private final @NonNull String mEntryPrefix;
+        private final @NonNull String mUidStatePrefix;
+        private final @NonNull PrintWriter mWriter;
+        private final int mFilterUid;
+        private final String mFilterPackage;
+        private final int mFilterOp;
+
+        StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer,
+                int filterUid, String filterPackage, int filterOp) {
+            mOpsPrefix = prefix + "  ";
+            mUidPrefix = mOpsPrefix + "  ";
+            mPackagePrefix = mUidPrefix + "  ";
+            mEntryPrefix = mPackagePrefix + "  ";
+            mUidStatePrefix = mEntryPrefix + "  ";
+            mWriter = writer;
+            mFilterUid = filterUid;
+            mFilterPackage = filterPackage;
+            mFilterOp = filterOp;
+        }
+
+        @Override
+        public void visitHistoricalOps(HistoricalOps ops) {
+            mWriter.println();
+            mWriter.print(mOpsPrefix);
+            mWriter.println("snapshot:");
+            mWriter.print(mUidPrefix);
+            mWriter.print("begin = ");
+            mDate.setTime(ops.getBeginTimeMillis());
+            mWriter.print(mDateFormatter.format(mDate));
+            mWriter.print("  (");
+            TimeUtils.formatDuration(ops.getBeginTimeMillis() - mNow, mWriter);
+            mWriter.println(")");
+            mWriter.print(mUidPrefix);
+            mWriter.print("end = ");
+            mDate.setTime(ops.getEndTimeMillis());
+            mWriter.print(mDateFormatter.format(mDate));
+            mWriter.print("  (");
+            TimeUtils.formatDuration(ops.getEndTimeMillis() - mNow, mWriter);
+            mWriter.println(")");
+        }
+
+        @Override
+        public void visitHistoricalUidOps(HistoricalUidOps ops) {
+            if (mFilterUid != Process.INVALID_UID && mFilterUid != ops.getUid()) {
+                return;
+            }
+            mWriter.println();
+            mWriter.print(mUidPrefix);
+            mWriter.print("Uid ");
+            UserHandle.formatUid(mWriter, ops.getUid());
+            mWriter.println(":");
+        }
+
+        @Override
+        public void visitHistoricalPackageOps(HistoricalPackageOps ops) {
+            if (mFilterPackage != null && !mFilterPackage.equals(ops.getPackageName())) {
+                return;
+            }
+            mWriter.print(mPackagePrefix);
+            mWriter.print("Package ");
+            mWriter.print(ops.getPackageName());
+            mWriter.println(":");
+        }
+
+        @Override
+        public void visitHistoricalOp(HistoricalOp ops) {
+            if (mFilterOp != AppOpsManager.OP_NONE && mFilterOp != ops.getOpCode()) {
+                return;
+            }
+            mWriter.print(mEntryPrefix);
+            mWriter.print(AppOpsManager.opToName(ops.getOpCode()));
+            mWriter.println(":");
+            for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) {
+                boolean printedUidState = false;
+                final long accessCount = ops.getAccessCount(uidState);
+                if (accessCount > 0) {
+                    if (!printedUidState) {
+                        mWriter.print(mUidStatePrefix);
+                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(" = ");
+                        printedUidState = true;
+                    }
+                    mWriter.print("access=");
+                    mWriter.print(accessCount);
+                }
+                final long rejectCount = ops.getRejectCount(uidState);
+                if (rejectCount > 0) {
+                    if (!printedUidState) {
+                        mWriter.print(mUidStatePrefix);
+                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(" = ");
+                        printedUidState = true;
+                    } else {
+                        mWriter.print(", ");
+                    }
+                    mWriter.print("reject=");
+                    mWriter.print(rejectCount);
+                }
+                final long accessDuration = ops.getAccessDuration(uidState);
+                if (accessDuration > 0) {
+                    if (!printedUidState) {
+                        mWriter.print(mUidStatePrefix);
+                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(" = ");
+                        printedUidState = true;
+                    } else {
+                        mWriter.print(", ");
+                    }
+                    mWriter.print("duration=");
+                    TimeUtils.formatDuration(accessDuration, mWriter);
+                }
+                if (printedUidState) {
+                    mWriter.println("");
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
new file mode 100644
index 0000000..f60d6b0
--- /dev/null
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2019 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.attention;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.attention.AttentionManagerInternal;
+import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.service.attention.AttentionService;
+import android.service.attention.AttentionService.AttentionFailureCodes;
+import android.service.attention.IAttentionCallback;
+import android.service.attention.IAttentionService;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+import com.android.server.SystemService;
+
+import java.io.PrintWriter;
+
+/**
+ * An attention service implementation that runs in System Server process.
+ * This service publishes a LocalService and reroutes calls to a {@link AttentionService} that it
+ * manages.
+ */
+public class AttentionManagerService extends SystemService {
+    private static final String LOG_TAG = "AttentionManagerService";
+
+    /** Service will unbind if connection is not used for that amount of time. */
+    private static final long CONNECTION_TTL_MILLIS = 60_000;
+
+    /** If the check attention called within that period - cached value will be returned. */
+    private static final long STALE_AFTER_MILLIS = 5_000;
+
+    private final Context mContext;
+    private final PowerManager mPowerManager;
+    private final ActivityManager mActivityManager;
+    private final Object mLock;
+    @GuardedBy("mLock")
+    private final SparseArray<UserState> mUserStates = new SparseArray<>();
+    private final AttentionHandler mAttentionHandler;
+
+    private ComponentName mComponentName;
+
+    public AttentionManagerService(Context context) {
+        super(context);
+        mContext = Preconditions.checkNotNull(context);
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        mLock = new Object();
+        mAttentionHandler = new AttentionHandler();
+    }
+
+    @Override
+    public void onStart() {
+        publishLocalService(AttentionManagerInternal.class, new LocalService());
+    }
+
+    @Override
+    public void onStopUser(int userId) {
+        cancelAndUnbindLocked(peekUserStateLocked(userId),
+                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        super.onBootPhase(phase);
+        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            mComponentName = resolveAttentionService(mContext);
+            if (mComponentName != null) {
+                // If the service is supported we want to keep receiving the screen off events.
+                mContext.registerReceiver(new ScreenStateReceiver(),
+                        new IntentFilter(Intent.ACTION_SCREEN_OFF));
+            }
+        }
+    }
+
+    /**
+     * Returns {@code true} if attention service is supported on this device.
+     */
+    public boolean isAttentionServiceSupported() {
+        return mComponentName != null;
+    }
+
+    /**
+     * Checks whether user attention is at the screen and calls in the provided callback.
+     *
+     * @return {@code true} if the framework was able to send the provided callback to the service
+     */
+    public boolean checkAttention(int requestCode, long timeout,
+            AttentionCallbackInternal callback) {
+        Preconditions.checkNotNull(callback);
+
+        if (!isAttentionServiceSupported()) {
+            Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device.");
+            return false;
+        }
+
+        // don't allow attention check in screen off state
+        if (!mPowerManager.isInteractive()) {
+            return false;
+        }
+
+        synchronized (mLock) {
+            unbindAfterTimeoutLocked();
+
+            final UserState userState = getOrCreateCurrentUserStateLocked();
+            // lazily start the service, which should be very lightweight to start
+            if (!userState.bindLocked()) {
+                return false;
+            }
+
+            if (userState.mService == null) {
+                // make sure every callback is called back
+                if (userState.mPendingAttentionCheck != null) {
+                    userState.mPendingAttentionCheck.cancel(
+                            AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                }
+                userState.mPendingAttentionCheck = new PendingAttentionCheck(requestCode,
+                        callback, () -> checkAttention(requestCode, timeout, callback));
+            } else {
+                try {
+                    // throttle frequent requests
+                    final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache;
+                    if (attentionCheckCache != null && SystemClock.uptimeMillis()
+                            < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) {
+                        callback.onSuccess(requestCode, attentionCheckCache.mResult,
+                                attentionCheckCache.mTimestamp);
+                        return true;
+                    }
+
+                    cancelAfterTimeoutLocked(timeout);
+
+                    userState.mCurrentAttentionCheckRequestCode = requestCode;
+                    userState.mService.checkAttention(requestCode, new IAttentionCallback.Stub() {
+                        @Override
+                        public void onSuccess(int requestCode, int result, long timestamp) {
+                            callback.onSuccess(requestCode, result, timestamp);
+                            userState.mAttentionCheckCache = new AttentionCheckCache(
+                                    SystemClock.uptimeMillis(), result,
+                                    timestamp);
+                        }
+
+                        @Override
+                        public void onFailure(int requestCode, int error) {
+                            callback.onFailure(requestCode, error);
+                        }
+
+                        @Override
+                        public IBinder asBinder() {
+                            return null;
+                        }
+                    });
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Cannot call into the AttentionService");
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /** Cancels the specified attention check. */
+    public void cancelAttentionCheck(int requestCode) {
+        final UserState userState = getOrCreateCurrentUserStateLocked();
+        try {
+            userState.mService.cancelAttentionCheck(requestCode);
+        } catch (RemoteException e) {
+            Slog.e(LOG_TAG, "Cannot call into the AttentionService");
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void unbindAfterTimeoutLocked() {
+        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CONNECTION_EXPIRED,
+                CONNECTION_TTL_MILLIS);
+    }
+
+    @GuardedBy("mLock")
+    private void cancelAfterTimeoutLocked(long timeout) {
+        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.ATTENTION_CHECK_TIMEOUT,
+                timeout);
+    }
+
+
+    @GuardedBy("mLock")
+    private UserState getOrCreateCurrentUserStateLocked() {
+        return getOrCreateUserStateLocked(mActivityManager.getCurrentUser());
+    }
+
+    @GuardedBy("mLock")
+    private UserState getOrCreateUserStateLocked(int userId) {
+        UserState result = mUserStates.get(userId);
+        if (result == null) {
+            result = new UserState(userId, mContext, mLock);
+            mUserStates.put(userId, result);
+        }
+        return result;
+    }
+
+    @GuardedBy("mLock")
+    UserState peekCurrentUserStateLocked() {
+        return peekUserStateLocked(mActivityManager.getCurrentUser());
+    }
+
+    @GuardedBy("mLock")
+    UserState peekUserStateLocked(int userId) {
+        return mUserStates.get(userId);
+    }
+
+    /**
+     * Provides attention service component name at runtime, making sure it's provided by the
+     * system.
+     */
+    private static ComponentName resolveAttentionService(Context context) {
+        // TODO(b/111939367): add a flag to turn on/off.
+        final String componentNameString = context.getString(
+                R.string.config_defaultAttentionService);
+
+        if (TextUtils.isEmpty(componentNameString)) {
+            return null;
+        }
+
+        final ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
+        if (componentName == null) {
+            return null;
+        }
+
+        final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage(
+                componentName.getPackageName());
+
+        // Make sure that only system apps can declare the AttentionService.
+        final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
+                PackageManager.MATCH_SYSTEM_ONLY);
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            Slog.wtf(LOG_TAG, String.format("Service %s not found in package %s",
+                    AttentionService.SERVICE_INTERFACE, componentName
+            ));
+            return null;
+        }
+
+        final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+        final String permission = serviceInfo.permission;
+        if (Manifest.permission.BIND_ATTENTION_SERVICE.equals(permission)) {
+            return serviceInfo.getComponentName();
+        }
+        Slog.e(LOG_TAG, String.format(
+                "Service %s should require %s permission. Found %s permission",
+                serviceInfo.getComponentName(),
+                Manifest.permission.BIND_ATTENTION_SERVICE,
+                serviceInfo.permission));
+        return null;
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println("Attention Manager Service (dumpsys attention)\n");
+
+        ipw.printPair("context", mContext);
+        pw.println();
+        synchronized (mLock) {
+            int size = mUserStates.size();
+            ipw.print("Number user states: ");
+            pw.println(size);
+            if (size > 0) {
+                ipw.increaseIndent();
+                for (int i = 0; i < size; i++) {
+                    UserState userState = mUserStates.valueAt(i);
+                    ipw.print(i);
+                    ipw.print(":");
+                    userState.dump(ipw);
+                    ipw.println();
+                }
+                ipw.decreaseIndent();
+            }
+        }
+    }
+
+    private final class LocalService extends AttentionManagerInternal {
+        @Override
+        public boolean isAttentionServiceSupported() {
+            return AttentionManagerService.this.isAttentionServiceSupported();
+        }
+
+        @Override
+        public boolean checkAttention(int requestCode, long timeout,
+                AttentionCallbackInternal callback) {
+            return AttentionManagerService.this.checkAttention(requestCode, timeout, callback);
+        }
+
+        @Override
+        public void cancelAttentionCheck(int requestCode) {
+            AttentionManagerService.this.cancelAttentionCheck(requestCode);
+        }
+    }
+
+    private static final class AttentionCheckCache {
+        private final long mLastComputed;
+        private final int mResult;
+        private final long mTimestamp;
+
+        AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result,
+                long timestamp) {
+            mLastComputed = lastComputed;
+            mResult = result;
+            mTimestamp = timestamp;
+        }
+    }
+
+    private static final class PendingAttentionCheck {
+        private final int mRequestCode;
+        private final AttentionCallbackInternal mCallback;
+        private final Runnable mRunnable;
+
+        PendingAttentionCheck(int requestCode, AttentionCallbackInternal callback,
+                Runnable runnable) {
+            mRequestCode = requestCode;
+            mCallback = callback;
+            mRunnable = runnable;
+        }
+
+        void cancel(@AttentionFailureCodes int failureCode) {
+            mCallback.onFailure(mRequestCode, failureCode);
+        }
+
+        void run() {
+            mRunnable.run();
+        }
+    }
+
+    private static final class UserState {
+        final AttentionServiceConnection mConnection = new AttentionServiceConnection();
+
+        @GuardedBy("mLock")
+        IAttentionService mService;
+        @GuardedBy("mLock")
+        boolean mBinding;
+        @GuardedBy("mLock")
+        int mCurrentAttentionCheckRequestCode;
+        @GuardedBy("mLock")
+        PendingAttentionCheck mPendingAttentionCheck;
+
+        @GuardedBy("mLock")
+        AttentionCheckCache mAttentionCheckCache;
+
+        @UserIdInt
+        final int mUserId;
+        final Context mContext;
+        final Object mLock;
+
+        private UserState(int userId, Context context, Object lock) {
+            mUserId = userId;
+            mContext = Preconditions.checkNotNull(context);
+            mLock = Preconditions.checkNotNull(lock);
+        }
+
+
+        @GuardedBy("mLock")
+        private void handlePendingCallbackLocked() {
+            if (mService != null && mPendingAttentionCheck != null) {
+                mPendingAttentionCheck.run();
+                mPendingAttentionCheck = null;
+            }
+        }
+
+        /** Binds to the system's AttentionService which provides an actual implementation. */
+        @GuardedBy("mLock")
+        private boolean bindLocked() {
+            // No need to bind if service is binding or has already been bound.
+            if (mBinding || mService != null) {
+                return true;
+            }
+
+            final boolean willBind;
+            final long identity = Binder.clearCallingIdentity();
+
+            try {
+                final ComponentName componentName =
+                        resolveAttentionService(mContext);
+                if (componentName == null) {
+                    // Might happen if the storage is encrypted and the user is not unlocked
+                    return false;
+                }
+                final Intent mServiceIntent = new Intent(
+                        AttentionService.SERVICE_INTERFACE).setComponent(
+                        componentName);
+                willBind = mContext.bindServiceAsUser(mServiceIntent, mConnection,
+                        Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
+                mBinding = willBind;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return willBind;
+        }
+
+        private void dump(IndentingPrintWriter pw) {
+            pw.printPair("context", mContext);
+            pw.printPair("userId", mUserId);
+            synchronized (mLock) {
+                pw.printPair("binding", mBinding);
+                pw.printPair("isAttentionCheckPending", mPendingAttentionCheck != null);
+            }
+        }
+
+        private final class AttentionServiceConnection implements ServiceConnection {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                init(IAttentionService.Stub.asInterface(service));
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onBindingDied(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                cleanupService();
+            }
+
+            void cleanupService() {
+                init(null);
+            }
+
+            private void init(@Nullable IAttentionService service) {
+                synchronized (mLock) {
+                    mService = service;
+                    mBinding = false;
+                    handlePendingCallbackLocked();
+                }
+            }
+        }
+    }
+
+    private class AttentionHandler extends Handler {
+        private static final int CONNECTION_EXPIRED = 1;
+        private static final int ATTENTION_CHECK_TIMEOUT = 2;
+
+        AttentionHandler() {
+            super(Looper.myLooper());
+        }
+
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                // Do not occupy resources when not in use - unbind proactively.
+                case CONNECTION_EXPIRED: {
+                    for (int i = 0; i < mUserStates.size(); i++) {
+                        cancelAndUnbindLocked(mUserStates.valueAt(i),
+                                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                    }
+
+                }
+                break;
+
+                // Callee is no longer interested in the attention check result - cancel.
+                case ATTENTION_CHECK_TIMEOUT: {
+                    cancelAndUnbindLocked(peekCurrentUserStateLocked(),
+                            AttentionService.ATTENTION_FAILURE_TIMED_OUT);
+                }
+                break;
+
+                default:
+                    break;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void cancelAndUnbindLocked(UserState userState,
+            @AttentionFailureCodes int failureCode) {
+        synchronized (mLock) {
+            if (userState != null && userState.mService != null) {
+                try {
+                    userState.mService.cancelAttentionCheck(
+                            userState.mCurrentAttentionCheckRequestCode);
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Unable to cancel attention check");
+                }
+
+                if (userState.mPendingAttentionCheck != null) {
+                    userState.mPendingAttentionCheck.cancel(failureCode);
+                }
+                mContext.unbindService(userState.mConnection);
+                userState.mConnection.cleanupService();
+                mUserStates.remove(userState.mUserId);
+            }
+        }
+    }
+
+    /**
+     * Unbinds and stops the service when the screen off intent is received.
+     * Attention service only makes sense when screen is ON; disconnect and stop service otherwise.
+     */
+    private final class ScreenStateReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                cancelAndUnbindLocked(peekCurrentUserStateLocked(),
+                        AttentionService.ATTENTION_FAILURE_UNKNOWN);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d704a3e..d9fcf9e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1614,7 +1614,9 @@
 
         // For notifications/ring, show the ui before making any adjustments
         // Don't suppress mute/unmute requests
-        if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
+        // Don't suppress adjustments for single volume device
+        if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)
+                && !mIsSingleVolume) {
             direction = 0;
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
             flags &= ~AudioManager.FLAG_VIBRATE;
@@ -7824,8 +7826,10 @@
 
     /** see AudioPolicy.setUidDeviceAffinity() */
     public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid,
-            @NonNull int[] deviceTypes,
-            @NonNull String[] deviceAddresses) {
+            @NonNull int[] deviceTypes, @NonNull String[] deviceAddresses) {
+        if (DEBUG_AP) {
+            Log.d(TAG, "setUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid);
+        }
         synchronized (mAudioPolicies) {
             final AudioPolicyProxy app =
                     checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
@@ -7835,21 +7839,23 @@
             if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
                 return AudioManager.ERROR;
             }
+            return app.setUidDeviceAffinities(uid, deviceTypes, deviceAddresses);
         }
-        return AudioManager.SUCCESS;
     }
 
     /** see AudioPolicy.removeUidDeviceAffinity() */
     public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) {
+        if (DEBUG_AP) {
+            Log.d(TAG, "removeUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid);
+        }
         synchronized (mAudioPolicies) {
             final AudioPolicyProxy app =
                     checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy");
             if (app == null) {
                 return AudioManager.ERROR;
             }
-
+            return app.removeUidDeviceAffinities(uid);
         }
-        return AudioManager.SUCCESS;
     }
 
     public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
@@ -8160,27 +8166,41 @@
             Binder.restoreCallingIdentity(identity);
         }
 
-        void setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+        int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
             final Integer Uid = new Integer(uid);
+            int res;
             if (mUidDeviceAffinities.remove(Uid) != null) {
                 final long identity = Binder.clearCallingIdentity();
-                AudioSystem.removeUidDeviceAffinities(uid);
+                res = AudioSystem.removeUidDeviceAffinities(uid);
                 Binder.restoreCallingIdentity(identity);
+                if (res != AudioSystem.SUCCESS) {
+                    Log.e(TAG, "AudioSystem. removeUidDeviceAffinities(" + uid + ") failed, "
+                            + " cannot call AudioSystem.setUidDeviceAffinities");
+                    return AudioManager.ERROR;
+                }
             }
             final long identity = Binder.clearCallingIdentity();
-            final int res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+            res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
             Binder.restoreCallingIdentity(identity);
             if (res == AudioSystem.SUCCESS) {
                 mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+                return AudioManager.SUCCESS;
             }
+            Log.e(TAG, "AudioSystem. setUidDeviceAffinities(" + uid + ") failed");
+            return AudioManager.ERROR;
         }
 
-        void removeUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+        int removeUidDeviceAffinities(int uid) {
             if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
                 final long identity = Binder.clearCallingIdentity();
-                AudioSystem.removeUidDeviceAffinities(uid);
+                final int res = AudioSystem.removeUidDeviceAffinities(uid);
                 Binder.restoreCallingIdentity(identity);
+                if (res == AudioSystem.SUCCESS) {
+                    return AudioManager.SUCCESS;
+                }
             }
+            Log.e(TAG, "AudioSystem. removeUidDeviceAffinities failed");
+            return AudioManager.ERROR;
         }
     };
 
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 905f826..9d6628c 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -206,7 +206,7 @@
             switch (event) {
             case AudioManager.RECORD_CONFIG_EVENT_STOP:
                 // return failure if an unknown recording session stopped
-                configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
+                configChanged = (mRecordConfigs.remove(new Integer(portId)) != null);
                 if (configChanged) {
                     sEventLogger.log(new RecordingEvent(event, uid, session, source, null));
                 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 36ca4dc..41fedc5 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -132,10 +132,13 @@
                 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
         private final Uri FACE_UNLOCK_APP_ENABLED =
                 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
+        private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
+                Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
 
         private final ContentResolver mContentResolver;
         private boolean mFaceEnabledOnKeyguard;
         private boolean mFaceEnabledForApps;
+        private boolean mFaceAlwaysRequireConfirmation;
 
         /**
          * Creates a content observer.
@@ -158,10 +161,15 @@
                     false /* notifyForDescendents */,
                     this /* observer */,
                     UserHandle.USER_CURRENT);
+            mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+                    false /* notifyForDescendents */,
+                    this /* observer */,
+                    UserHandle.USER_CURRENT);
 
             // Update the value immediately
             onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
             onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED);
+            onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
         }
 
         @Override
@@ -185,6 +193,13 @@
                                 Settings.Secure.FACE_UNLOCK_APP_ENABLED,
                                 1 /* default */,
                                 UserHandle.USER_CURRENT) != 0;
+            } else if (FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION.equals(uri)) {
+                mFaceAlwaysRequireConfirmation =
+                        Settings.Secure.getIntForUser(
+                                mContentResolver,
+                                Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+                                0 /* default */,
+                                UserHandle.USER_CURRENT) != 0;
             }
         }
 
@@ -195,6 +210,10 @@
         boolean getFaceEnabledForApps() {
             return mFaceEnabledForApps;
         }
+
+        boolean getFaceAlwaysRequireConfirmation() {
+            return mFaceAlwaysRequireConfirmation;
+        }
     }
 
     private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
@@ -332,10 +351,7 @@
                     if (!runningTasks.isEmpty()) {
                         final String topPackage = runningTasks.get(0).topActivity.getPackageName();
                         if (mCurrentAuthSession != null
-                                && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)
-                                && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
-                            // We only care about this state, since <Biometric>Service will
-                            // cancel any client that's still in STATE_AUTH_STARTED
+                                && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
                             mStatusBarService.hideBiometricDialog();
                             mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
                             mCurrentAuthSession.mClientReceiver.onError(
@@ -395,7 +411,7 @@
 
                     // Notify SysUI that the biometric has been authenticated. SysUI already knows
                     // the implicit/explicit state and will react accordingly.
-                    mStatusBarService.onBiometricAuthenticated();
+                    mStatusBarService.onBiometricAuthenticated(true);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Remote exception", e);
                 }
@@ -412,17 +428,20 @@
                         return;
                     }
 
-                    mStatusBarService.onBiometricHelp(getContext().getResources().getString(
-                            com.android.internal.R.string.biometric_not_recognized));
-                    if (requireConfirmation) {
+                    mStatusBarService.onBiometricAuthenticated(false);
+
+                    // TODO: This logic will need to be updated if BP is multi-modal
+                    if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
+                        // Pause authentication. onBiometricAuthenticated(false) causes the
+                        // dialog to show a "try again" button for passive modalities.
                         mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
-                        mStatusBarService.showBiometricTryAgain();
                         // Cancel authentication. Skip the token/package check since we are
                         // cancelling from system server. The interface is permission protected so
                         // this is fine.
                         cancelInternal(null /* token */, null /* package */,
                                 false /* fromClient */);
                     }
+
                     mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Remote exception", e);
@@ -442,8 +461,9 @@
                     if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
                         if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
                             mStatusBarService.onBiometricError(message);
-                            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
                             if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+                                    mActivityTaskManager.unregisterTaskStackListener(
+                                            mTaskStackListener);
                                     mCurrentAuthSession.mClientReceiver.onError(error, message);
                                     mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                                     mCurrentAuthSession = null;
@@ -452,9 +472,14 @@
                                 // Send errors after the dialog is dismissed.
                                 mHandler.postDelayed(() -> {
                                     try {
-                                        mCurrentAuthSession.mClientReceiver.onError(error, message);
-                                        mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                                        mCurrentAuthSession = null;
+                                        if (mCurrentAuthSession != null) {
+                                            mActivityTaskManager.unregisterTaskStackListener(
+                                                    mTaskStackListener);
+                                            mCurrentAuthSession.mClientReceiver.onError(error,
+                                                    message);
+                                            mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                                            mCurrentAuthSession = null;
+                                        }
                                     } catch (RemoteException e) {
                                         Slog.e(TAG, "Remote exception", e);
                                     }
@@ -518,6 +543,11 @@
 
             @Override
             public void onDialogDismissed(int reason) throws RemoteException {
+                if (mCurrentAuthSession == null) {
+                    Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
+                    return;
+                }
+
                 if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
                     // Positive button is used by passive modalities as a "confirm" button,
                     // do not send to client
@@ -579,8 +609,10 @@
             }
 
             if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
-                final boolean mContinuing = mCurrentAuthSession != null
-                        && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
+                final boolean continuing = mCurrentAuthSession != null &&
+                        (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+                                || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED);
+
                 mCurrentAuthSession = mPendingAuthSession;
                 mPendingAuthSession = null;
 
@@ -602,7 +634,7 @@
                         modality |= pair.getKey();
                     }
 
-                    if (!mContinuing) {
+                    if (!continuing) {
                         mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
                                 mInternalReceiver, modality, requireConfirmation, userId);
                         mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
@@ -706,7 +738,8 @@
 
                 mCurrentModality = modality;
 
-                // Actually start authentication
+                // Start preparing for authentication. Authentication starts when
+                // all modalities requested have invoked onReadyForAuthentication.
                 authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
                         callingUid, callingPid, callingUserId, modality);
             });
@@ -725,6 +758,9 @@
                 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
                 int callingUid, int callingPid, int callingUserId, int modality) {
             try {
+                boolean requireConfirmation = bundle.getBoolean(
+                        BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
+
                 // Generate random cookies to pass to the services that should prepare to start
                 // authenticating. Store the cookie here and wait for all services to "ack"
                 // with the cookie. Once all cookies are received, we can show the prompt
@@ -748,7 +784,10 @@
                     Slog.w(TAG, "Iris unsupported");
                 }
                 if ((modality & TYPE_FACE) != 0) {
-                    mFaceService.prepareForAuthentication(true /* requireConfirmation */,
+                    // Check if the user has forced confirmation to be required in Settings.
+                    requireConfirmation = requireConfirmation
+                            || mSettingObserver.getFaceAlwaysRequireConfirmation();
+                    mFaceService.prepareForAuthentication(requireConfirmation,
                             token, sessionId, userId, mInternalReceiver, opPackageName,
                             cookie, callingUid, callingPid, callingUserId);
                 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 32219aa..174ecfa 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -116,6 +116,7 @@
     protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
     // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
     protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+    protected int mHALDeathCount;
 
     protected class PerformanceStats {
         public int accept; // number of accepted biometrics
@@ -596,6 +597,7 @@
     public void serviceDied(long cookie) {
         Slog.e(getTag(), "HAL died");
         mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+        mHALDeathCount++;
         handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                 0 /*vendorCode */);
     }
@@ -819,8 +821,6 @@
 
     // Should be done on a handler thread - not on the Binder's thread.
     private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
-        updateActiveGroup(client.getGroupId(), opPackageName);
-
         if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
 
         int lockoutMode = getLockoutMode();
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 72f73f6..a2aacdd 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -133,10 +133,11 @@
         }
 
         @Override // Binder call
-        public void authenticate(final IBinder token, final long opId,
+        public void authenticate(final IBinder token, final long opId, int userId,
                 final IFaceServiceReceiver receiver, final int flags,
                 final String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
+            updateActiveGroup(userId, opPackageName);
             final boolean restricted = isRestricted();
             final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
@@ -156,7 +157,7 @@
                     mDaemonWrapper, mHalDeviceId, token,
                     new BiometricPromptServiceListenerImpl(wrapperReceiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
-                    true /* requireConfirmation */);
+                    requireConfirmation);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -674,6 +675,12 @@
     }
 
     @Override
+    public void serviceDied(long cookie) {
+        super.serviceDied(cookie);
+        mDaemon = null;
+    }
+
+    @Override
     protected void updateActiveGroup(int userId, String clientPackage) {
         IBiometricsFace daemon = getFaceDaemon();
 
@@ -863,6 +870,8 @@
             Slog.e(TAG, "dump formatting failure", e);
         }
         pw.println(dump);
+        pw.println("HAL Deaths: " + mHALDeathCount);
+        mHALDeathCount = 0;
     }
 
     private void dumpProto(FileDescriptor fd) {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 3895ef7..3db6a74 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -159,6 +159,7 @@
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags,
                 final String opPackageName) {
+            updateActiveGroup(groupId, opPackageName);
             final boolean restricted = isRestricted();
             final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
@@ -776,6 +777,12 @@
     }
 
     @Override
+    public void serviceDied(long cookie) {
+        super.serviceDied(cookie);
+        mDaemon = null;
+    }
+
+    @Override
     protected void updateActiveGroup(int userId, String clientPackage) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
 
@@ -1073,6 +1080,8 @@
             Slog.e(TAG, "dump formatting failure", e);
         }
         pw.println(dump);
+        pw.println("HAL Deaths: " + mHALDeathCount);
+        mHALDeathCount = 0;
     }
 
     private void dumpProto(FileDescriptor fd) {
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
index 24865bc..6fa98b8 100644
--- a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
+++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
@@ -21,22 +21,6 @@
  * @hide
  */
 public class ConnectivityConstants {
-    // IPC constants
-    public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
-            "android.net.conn.NETWORK_CONDITIONS_MEASURED";
-    public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
-    public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
-    public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
-    public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
-    public static final String EXTRA_CELL_ID = "extra_cellid";
-    public static final String EXTRA_SSID = "extra_ssid";
-    public static final String EXTRA_BSSID = "extra_bssid";
-    /** real time since boot */
-    public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
-    public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
-
-    public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
-            "android.permission.ACCESS_NETWORK_CONDITIONS";
 
     // Penalty applied to scores of Networks that have not been validated.
     public static final int UNVALIDATED_SCORE_PENALTY = 40;
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index b8f057d..d8bb635 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -18,10 +18,9 @@
 
 import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
-import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
 import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
 import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
 import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
 import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
@@ -35,6 +34,7 @@
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.Uri;
+import android.net.shared.PrivateDnsConfig;
 import android.os.Binder;
 import android.os.INetworkManagementService;
 import android.os.UserHandle;
@@ -43,10 +43,7 @@
 import android.util.Pair;
 import android.util.Slog;
 
-import com.android.server.connectivity.MockableSystemProperties;
-
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -54,10 +51,8 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
 import java.util.Set;
-import java.util.StringJoiner;
+import java.util.stream.Collectors;
 
 
 /**
@@ -123,43 +118,6 @@
     private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
     private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
 
-    public static class PrivateDnsConfig {
-        public final boolean useTls;
-        public final String hostname;
-        public final InetAddress[] ips;
-
-        public PrivateDnsConfig() {
-            this(false);
-        }
-
-        public PrivateDnsConfig(boolean useTls) {
-            this.useTls = useTls;
-            this.hostname = "";
-            this.ips = new InetAddress[0];
-        }
-
-        public PrivateDnsConfig(String hostname, InetAddress[] ips) {
-            this.useTls = !TextUtils.isEmpty(hostname);
-            this.hostname = useTls ? hostname : "";
-            this.ips = (ips != null) ? ips : new InetAddress[0];
-        }
-
-        public PrivateDnsConfig(PrivateDnsConfig cfg) {
-            useTls = cfg.useTls;
-            hostname = cfg.hostname;
-            ips = cfg.ips;
-        }
-
-        public boolean inStrictMode() {
-            return useTls && !TextUtils.isEmpty(hostname);
-        }
-
-        public String toString() {
-            return PrivateDnsConfig.class.getSimpleName() +
-                    "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
-        }
-    }
-
     public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
         final String mode = getPrivateDnsMode(cr);
 
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 6596d27..9d9b1cf 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,8 +16,9 @@
 
 package com.android.server.connectivity;
 
-import android.net.InterfaceConfiguration;
 import android.net.ConnectivityManager;
+import android.net.INetd;
+import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
@@ -59,6 +60,7 @@
         NetworkInfo.State.SUSPENDED,
     };
 
+    private final INetd mNetd;
     private final INetworkManagementService mNMService;
 
     // The network we're running on, and its type.
@@ -76,7 +78,8 @@
     private String mIface;
     private State mState = State.IDLE;
 
-    public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
+    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) {
+        mNetd = netd;
         mNMService = nmService;
         mNetwork = nai;
     }
@@ -140,7 +143,7 @@
             return;
         }
         try {
-            mNMService.startClatd(baseIface);
+            mNetd.clatdStart(baseIface);
         } catch(RemoteException|IllegalStateException e) {
             Slog.e(TAG, "Error starting clatd on " + baseIface, e);
         }
@@ -162,7 +165,7 @@
      */
     private void enterStoppingState() {
         try {
-            mNMService.stopClatd(mBaseIface);
+            mNetd.clatdStop(mBaseIface);
         } catch(RemoteException|IllegalStateException e) {
             Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
         }
@@ -204,7 +207,7 @@
             Slog.e(TAG, "startClat: Can't start clat on null interface");
             return;
         }
-        // TODO: should we only do this if mNMService.startClatd() succeeds?
+        // TODO: should we only do this if mNetd.clatdStart() succeeds?
         Slog.i(TAG, "Starting clatd on " + baseIface);
         enterStartingState(baseIface);
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 262184b..9ea73fb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,9 +16,9 @@
 
 package com.android.server.connectivity;
 
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-
 import android.content.Context;
+import android.net.INetd;
+import android.net.INetworkMonitor;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -29,7 +29,6 @@
 import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.Messenger;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.SparseArray;
@@ -37,11 +36,8 @@
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.WakeupMessage;
 import com.android.server.ConnectivityService;
-import com.android.server.connectivity.NetworkMonitor;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Comparator;
 import java.util.Objects;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -126,7 +122,6 @@
     public LinkProperties linkProperties;
     // This should only be modified via ConnectivityService.updateCapabilities().
     public NetworkCapabilities networkCapabilities;
-    public final NetworkMonitor networkMonitor;
     public final NetworkMisc networkMisc;
     // Indicates if netd has been told to create this Network. From this point on the appropriate
     // routing rules are setup and routes are added so packets can begin flowing over the Network.
@@ -239,15 +234,21 @@
     // Used by ConnectivityService to keep track of 464xlat.
     public Nat464Xlat clatd;
 
+    // Set after asynchronous creation of the NetworkMonitor.
+    private volatile INetworkMonitor mNetworkMonitor;
+
     private static final String TAG = ConnectivityService.class.getSimpleName();
     private static final boolean VDBG = false;
     private final ConnectivityService mConnService;
+    private final INetd mNetd;
+    private final INetworkManagementService mNMS;
     private final Context mContext;
     private final Handler mHandler;
 
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
-            NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
+            NetworkMisc misc, ConnectivityService connService, INetd netd,
+            INetworkManagementService nms) {
         this.messenger = messenger;
         asyncChannel = ac;
         network = net;
@@ -256,12 +257,20 @@
         networkCapabilities = nc;
         currentScore = score;
         mConnService = connService;
+        mNetd = netd;
+        mNMS = nms;
         mContext = context;
         mHandler = handler;
-        networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
         networkMisc = misc;
     }
 
+    /**
+     * Inform NetworkAgentInfo that a new NetworkMonitor was created.
+     */
+    public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
+        mNetworkMonitor = networkMonitor;
+    }
+
     public ConnectivityService connService() {
         return mConnService;
     }
@@ -278,6 +287,15 @@
         return network;
     }
 
+    /**
+     * Get the INetworkMonitor in this NetworkAgentInfo.
+     *
+     * <p>This will be null before {@link #onNetworkMonitorCreated(INetworkMonitor)} is called.
+     */
+    public INetworkMonitor networkMonitor() {
+        return mNetworkMonitor;
+    }
+
     // Functions for manipulating the requests satisfied by this network.
     //
     // These functions must only called on ConnectivityService's main thread.
@@ -575,18 +593,18 @@
 
     public void updateClat(INetworkManagementService netd) {
         if (Nat464Xlat.requiresClat(this)) {
-            maybeStartClat(netd);
+            maybeStartClat();
         } else {
             maybeStopClat();
         }
     }
 
     /** Ensure clat has started for this network. */
-    public void maybeStartClat(INetworkManagementService netd) {
+    public void maybeStartClat() {
         if (clatd != null && clatd.isStarted()) {
             return;
         }
-        clatd = new Nat464Xlat(netd, this);
+        clatd = new Nat464Xlat(this, mNetd, mNMS);
         clatd.start();
     }
 
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 3ea9810..9789688 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -282,6 +282,7 @@
     private void setCurrentProxyScript(String script) {
         if (mProxyService == null) {
             Log.e(TAG, "setCurrentProxyScript: no proxy service");
+            return;
         }
         try {
             mProxyService.setPacFile(script);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9dfdddb..eb5be77 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1837,7 +1837,7 @@
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
-                             mDeps.getIpServerDependencies()));
+                             mDeps.getIpServerDependencies(mContext)));
         mTetherStates.put(iface, tetherState);
         tetherState.ipServer.start();
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index d56b167..a42efe9 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -34,32 +34,53 @@
  * @hide
  */
 public class TetheringDependencies {
+    /**
+     * Get a reference to the offload hardware interface to be used by tethering.
+     */
     public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
         return new OffloadHardwareInterface(h, log);
     }
 
+    /**
+     * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
+     */
     public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
             SharedLog log, int what) {
         return new UpstreamNetworkMonitor(ctx, target, log, what);
     }
 
+    /**
+     * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
+     */
     public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
             ArrayList<IpServer> notifyList, SharedLog log) {
         return new IPv6TetheringCoordinator(notifyList, log);
     }
 
-    public IpServer.Dependencies getIpServerDependencies() {
-        return new IpServer.Dependencies();
+    /**
+     * Get dependencies to be used by IpServer.
+     */
+    public IpServer.Dependencies getIpServerDependencies(Context context) {
+        return new IpServer.Dependencies(context);
     }
 
+    /**
+     * Indicates whether tethering is supported on the device.
+     */
     public boolean isTetheringSupported() {
         return true;
     }
 
+    /**
+     * Get the NetworkRequest that should be fulfilled by the default network.
+     */
     public NetworkRequest getDefaultNetworkRequest() {
         return null;
     }
 
+    /**
+     * Get a reference to the EntitlementManager to be used by tethering.
+     */
     public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
             MockableSystemProperties systemProperties) {
         return new EntitlementManager(ctx, log, systemProperties);
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index bfcc629..aaf9cbc 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -141,10 +141,7 @@
             final long runtime = nowUptime - startUptime;
 
 
-            if (startUptime == 0) {
-                wtf("Job " + jobId + " start uptime not found: "
-                        + " params=" + jobParametersToString(params));
-            } else if (runtime > 60 * 1000) {
+            if (runtime > 60 * 1000) {
                 // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
                 // (1 minute threshold.)
                 // Also don't wtf when it's not ready to sync.
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index 5cbe5b9..fc20ef20 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -16,6 +16,7 @@
 
 package com.android.server.content;
 
+import android.accounts.Account;
 import android.app.job.JobParameters;
 import android.os.Build;
 import android.os.Environment;
@@ -31,6 +32,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IntPair;
 import com.android.server.IoThread;
+import com.android.server.content.SyncManager.ActiveSyncContext;
+import com.android.server.content.SyncStorageEngine.EndPoint;
 
 import libcore.io.IoUtils;
 
@@ -309,4 +312,20 @@
             }
         }
     }
+
+    static String logSafe(Account account) {
+        return account == null ? "[null]" : account.toSafeString();
+    }
+
+    static String logSafe(EndPoint endPoint) {
+        return endPoint == null ? "[null]" : endPoint.toSafeString();
+    }
+
+    static String logSafe(SyncOperation operation) {
+        return operation == null ? "[null]" : operation.toSafeString();
+    }
+
+    static String logSafe(ActiveSyncContext asc) {
+        return asc == null ? "[null]" : asc.toSafeString();
+    }
 }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8b93e04..7096477 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.content;
 
+import static com.android.server.content.SyncLogger.logSafe;
+
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
@@ -1140,7 +1142,7 @@
             /* ignore - local call */
         }
         if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) {
-            Log.w(TAG, "Access to " + account + " denied for package "
+            Log.w(TAG, "Access to " + logSafe(account) + " denied for package "
                     + owningPackage + " in UID " + syncAdapterInfo.uid);
             return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS;
         }
@@ -1474,7 +1476,8 @@
         if (!syncOperation.ignoreBackoff()) {
             Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target);
             if (backoff == null) {
-                Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target);
+                Slog.e(TAG, "Couldn't find backoff values for "
+                        + logSafe(syncOperation.target));
                 backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
                         SyncStorageEngine.NOT_IN_BACKOFF_MODE);
             }
@@ -1745,8 +1748,8 @@
             scheduleSyncOperationH(operation);
         } else {
             // Otherwise do not reschedule.
-            Log.d(TAG, "not retrying sync operation because the error is a hard error: "
-                    + operation);
+            Log.e(TAG, "not retrying sync operation because the error is a hard error: "
+                    + logSafe(operation));
         }
     }
 
@@ -1881,11 +1884,12 @@
             sendSyncFinishedOrCanceledMessage(this, result);
         }
 
-        public void toString(StringBuilder sb) {
+        public void toString(StringBuilder sb, boolean logSafe) {
             sb.append("startTime ").append(mStartTime)
                     .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
                     .append(", mHistoryRowId ").append(mHistoryRowId)
-                    .append(", syncOperation ").append(mSyncOperation);
+                    .append(", syncOperation ").append(
+                        logSafe ? logSafe(mSyncOperation) : mSyncOperation);
         }
 
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -1947,7 +1951,13 @@
 
         public String toString() {
             StringBuilder sb = new StringBuilder();
-            toString(sb);
+            toString(sb, false);
+            return sb.toString();
+        }
+
+        public String toSafeString() {
+            StringBuilder sb = new StringBuilder();
+            toString(sb, true);
             return sb.toString();
         }
 
@@ -2036,7 +2046,7 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (!op.isPeriodic) {
-                pw.println(op.dump(null, false, buckets));
+                pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
                 count++;
             }
         }
@@ -2053,7 +2063,7 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (op.isPeriodic) {
-                pw.println(op.dump(null, false, buckets));
+                pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
                 count++;
             }
         }
@@ -2186,7 +2196,7 @@
             sb.setLength(0);
             pw.print(formatDurationHMS(sb, durationInSeconds));
             pw.print(" - ");
-            pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets));
+            pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false));
             pw.println();
         }
         pw.println();
@@ -2974,7 +2984,7 @@
                     case SyncHandler.MESSAGE_CANCEL:
                         SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
                         Bundle extras = msg.peekData();
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
                                     + endpoint + " bundle: " + extras);
                         }
@@ -2985,9 +2995,11 @@
                         SyncFinishedOrCancelledMessagePayload payload =
                                 (SyncFinishedOrCancelledMessagePayload) msg.obj;
                         if (!isSyncStillActiveH(payload.activeSyncContext)) {
-                            Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
-                                    + "sync is no longer active: "
-                                    + payload.activeSyncContext);
+                            if (isLoggable) {
+                                Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+                                        + "sync is no longer active: "
+                                        + payload.activeSyncContext);
+                            }
                             break;
                         }
                         if (isLoggable) {
@@ -3002,7 +3014,7 @@
 
                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
                         ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
                                     + msgData.activeSyncContext);
                         }
@@ -3018,7 +3030,7 @@
                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
                         final ActiveSyncContext currentSyncContext =
                                 ((ServiceConnectionData) msg.obj).activeSyncContext;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
                                     + currentSyncContext);
                         }
@@ -3053,7 +3065,7 @@
 
                     case SyncHandler.MESSAGE_MONITOR_SYNC:
                         ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
                                     monitoredSyncContext.mSyncOperation.target);
                         }
@@ -3061,7 +3073,7 @@
                         if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
                             Log.w(TAG, String.format(
                                     "Detected sync making no progress for %s. cancelling.",
-                                    monitoredSyncContext));
+                                    logSafe(monitoredSyncContext)));
                             SyncJobService.callJobFinished(
                                     monitoredSyncContext.mSyncOperation.jobId, false,
                                     "no network activity");
@@ -3558,7 +3570,8 @@
             } catch (RuntimeException exc) {
                 mLogger.log("Sync failed with RuntimeException: ", exc.toString());
                 closeActiveSyncContext(activeSyncContext);
-                Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
+                Slog.e(TAG, "Caught RuntimeException while starting the sync "
+                        + logSafe(syncOperation), exc);
             }
         }
 
@@ -3658,7 +3671,8 @@
                         reschedulePeriodicSyncH(syncOperation);
                     }
                 } else {
-                    Log.w(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
+                    Log.w(TAG, "failed sync operation "
+                            + logSafe(syncOperation) + ", " + syncResult);
 
                     syncOperation.retries++;
                     if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) {
@@ -4042,11 +4056,6 @@
         getJobScheduler().cancel(op.jobId);
     }
 
-    private void wtfWithLog(String message) {
-        Slog.wtf(TAG, message);
-        mLogger.log("WTF: ", message);
-    }
-
     public void resetTodayStats() {
         mSyncStorageEngine.resetTodayStats(/*force=*/ true);
     }
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 25edf40..2abc2e6 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -363,14 +363,19 @@
 
     @Override
     public String toString() {
-        return dump(null, true, null);
+        return dump(null, true, null, false);
     }
 
-    String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates) {
+    public String toSafeString() {
+        return dump(null, true, null, true);
+    }
+
+    String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates,
+            boolean logSafe) {
         StringBuilder sb = new StringBuilder();
         sb.append("JobId=").append(jobId)
                 .append(" ")
-                .append(target.account.name)
+                .append(logSafe ? "***" : target.account.name)
                 .append("/")
                 .append(target.account.type)
                 .append(" u")
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index bfd1791..6b441a0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -16,6 +16,8 @@
 
 package com.android.server.content;
 
+import static com.android.server.content.SyncLogger.logSafe;
+
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
@@ -225,6 +227,15 @@
             sb.append(":u" + userId);
             return sb.toString();
         }
+
+        public String toSafeString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(account == null ? "ALL ACCS" : logSafe(account))
+                    .append("/")
+                    .append(provider == null ? "ALL PDRS" : provider);
+            sb.append(":u" + userId);
+            return sb.toString();
+        }
     }
 
     public static class AuthorityInfo {
@@ -1861,8 +1872,8 @@
 
                 }
             } else {
-                Slog.w(TAG, "Failure adding authority: account="
-                        + accountName + " auth=" + authorityName
+                Slog.w(TAG, "Failure adding authority:"
+                        + " auth=" + authorityName
                         + " enabled=" + enabled
                         + " syncable=" + syncable);
             }
diff --git a/services/core/java/com/android/server/display/AppSaturationController.java b/services/core/java/com/android/server/display/AppSaturationController.java
new file mode 100644
index 0000000..5d5e4f7
--- /dev/null
+++ b/services/core/java/com/android/server/display/AppSaturationController.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2019 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.display;
+
+import android.annotation.UserIdInt;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.ColorDisplayService.ColorTransformController;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+class AppSaturationController {
+
+    private final Object mLock = new Object();
+
+    /**
+     * A package name has one or more userIds it is running under. Each userId has zero or one
+     * saturation level, and zero or more ColorTransformControllers.
+     */
+    @GuardedBy("mLock")
+    private final Map<String, SparseArray<SaturationController>> mAppsMap = new HashMap<>();
+
+    @VisibleForTesting
+    static final float[] TRANSLATION_VECTOR = {0f, 0f, 0f};
+
+    /**
+     * Add an {@link WeakReference<ColorTransformController>} for a given package and userId.
+     */
+    boolean addColorTransformController(String packageName, @UserIdInt int userId,
+            WeakReference<ColorTransformController> controller) {
+        synchronized (mLock) {
+            return getSaturationControllerLocked(packageName, userId)
+                    .addColorTransformController(controller);
+        }
+    }
+
+    /**
+     * Set the saturation level ({@code ColorDisplayManager#SaturationLevel} constant for a given
+     * package name and userId.
+     */
+    public boolean setSaturationLevel(String packageName, @UserIdInt int userId,
+            int saturationLevel) {
+        synchronized (mLock) {
+            return getSaturationControllerLocked(packageName, userId)
+                    .setSaturationLevel(saturationLevel);
+        }
+    }
+
+    /**
+     * Dump state information.
+     */
+    public void dump(PrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("App Saturation: ");
+            if (mAppsMap.size() == 0) {
+                pw.println("    No packages");
+                return;
+            }
+            final List<String> packageNames = new ArrayList<>(mAppsMap.keySet());
+            Collections.sort(packageNames);
+            for (String packageName : packageNames) {
+                pw.println("    " + packageName + ":");
+                final SparseArray<SaturationController> appUserIdMap = mAppsMap.get(packageName);
+                for (int i = 0; i < appUserIdMap.size(); i++) {
+                    pw.println("        " + appUserIdMap.keyAt(i) + ":");
+                    appUserIdMap.valueAt(i).dump(pw);
+                }
+            }
+        }
+    }
+
+    /**
+     * Retrieve the SaturationController for a given package and userId, creating all intermediate
+     * connections as needed.
+     */
+    private SaturationController getSaturationControllerLocked(String packageName,
+            @UserIdInt int userId) {
+        return getOrCreateSaturationControllerLocked(getOrCreateUserIdMapLocked(packageName),
+                userId);
+    }
+
+    /**
+     * Retrieve or create the mapping between the app's given package name and its userIds (and
+     * their SaturationControllers).
+     */
+    private SparseArray<SaturationController> getOrCreateUserIdMapLocked(String packageName) {
+        if (mAppsMap.get(packageName) != null) {
+            return mAppsMap.get(packageName);
+        }
+
+        final SparseArray<SaturationController> appUserIdMap = new SparseArray<>();
+        mAppsMap.put(packageName, appUserIdMap);
+        return appUserIdMap;
+    }
+
+    /**
+     * Retrieve or create the mapping between an app's given userId and SaturationController.
+     */
+    private SaturationController getOrCreateSaturationControllerLocked(
+            SparseArray<SaturationController> appUserIdMap, @UserIdInt int userId) {
+        if (appUserIdMap.get(userId) != null) {
+            return appUserIdMap.get(userId);
+        }
+
+        final SaturationController saturationController = new SaturationController();
+        appUserIdMap.put(userId, saturationController);
+        return saturationController;
+    }
+
+    @VisibleForTesting
+    static void computeGrayscaleTransformMatrix(float saturation, float[] matrix) {
+        float desaturation = 1.0f - saturation;
+        float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
+                0.072f * desaturation};
+        matrix[0] = luminance[0] + saturation;
+        matrix[1] = luminance[0];
+        matrix[2] = luminance[0];
+        matrix[3] = luminance[1];
+        matrix[4] = luminance[1] + saturation;
+        matrix[5] = luminance[1];
+        matrix[6] = luminance[2];
+        matrix[7] = luminance[2];
+        matrix[8] = luminance[2] + saturation;
+    }
+
+    private static class SaturationController {
+
+        private final List<WeakReference<ColorTransformController>> mControllerRefs =
+                new ArrayList<>();
+        private int mSaturationLevel = 100;
+        private float[] mTransformMatrix = new float[9];
+
+        private boolean setSaturationLevel(int saturationLevel) {
+            mSaturationLevel = saturationLevel;
+            if (!mControllerRefs.isEmpty()) {
+                return updateState();
+            }
+            return false;
+        }
+
+        private boolean addColorTransformController(
+                WeakReference<ColorTransformController> controller) {
+            mControllerRefs.add(controller);
+            if (mSaturationLevel != 100) {
+                return updateState();
+            } else {
+                clearExpiredReferences();
+            }
+            return false;
+        }
+
+        private boolean updateState() {
+            computeGrayscaleTransformMatrix(mSaturationLevel / 100f, mTransformMatrix);
+
+            boolean updated = false;
+            final Iterator<WeakReference<ColorTransformController>> iterator = mControllerRefs
+                    .iterator();
+            while (iterator.hasNext()) {
+                WeakReference<ColorTransformController> controllerRef = iterator.next();
+                final ColorTransformController controller = controllerRef.get();
+                if (controller != null) {
+                    controller.applyAppSaturation(mTransformMatrix, TRANSLATION_VECTOR);
+                    updated = true;
+                } else {
+                    // Purge cleared refs lazily to avoid accumulating a lot of dead windows
+                    iterator.remove();
+                }
+            }
+            return updated;
+
+        }
+
+        private void clearExpiredReferences() {
+            final Iterator<WeakReference<ColorTransformController>> iterator = mControllerRefs
+                    .iterator();
+            while (iterator.hasNext()) {
+                WeakReference<ColorTransformController> controllerRef = iterator.next();
+                final ColorTransformController controller = controllerRef.get();
+                if (controller == null) {
+                    iterator.remove();
+                }
+            }
+        }
+
+        private void dump(PrintWriter pw) {
+            pw.println("            mSaturationLevel: " + mSaturationLevel);
+            pw.println("            mControllerRefs count: " + mControllerRefs.size());
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 52eccca..d57431e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,13 +17,12 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -34,7 +33,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -45,7 +43,6 @@
 
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 
@@ -66,13 +63,6 @@
     // the user is satisfied with the result before storing the sample.
     private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
 
-    // Timeout after which we remove the effects any user interactions might've had on the
-    // brightness mapping. This timeout doesn't start until we transition to a non-interactive
-    // display policy so that we don't reset while users are using their devices, but also so that
-    // we don't erroneously keep the short-term model if the device is dozing but the display is
-    // fully on.
-    private static final int SHORT_TERM_MODEL_TIMEOUT_MILLIS = 30000;
-
     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
     private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
@@ -140,6 +130,13 @@
 
     private boolean mLoggingEnabled;
 
+    // Timeout after which we remove the effects any user interactions might've had on the
+    // brightness mapping. This timeout doesn't start until we transition to a non-interactive
+    // display policy so that we don't reset while users are using their devices, but also so that
+    // we don't erroneously keep the short-term model if the device is dozing but the display is
+    // fully on.
+    private long mShortTermModelTimeout;
+
     // Amount of time to delay auto-brightness after screen on while waiting for
     // the light sensor to warm-up in milliseconds.
     // May be 0 if no warm-up is required.
@@ -210,14 +207,13 @@
     // package name and category, which is done by registering a TaskStackListener to call back to
     // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
     // package namd and PackageManager to get its category (so might as well cache them).
-    private int mUserId;
     private String mForegroundAppPackageName;
     private String mPendingForegroundAppPackageName;
     private @ApplicationInfo.Category int mForegroundAppCategory;
     private @ApplicationInfo.Category int mPendingForegroundAppCategory;
     private TaskStackListenerImpl mTaskStackListener;
     private IActivityTaskManager mActivityTaskManager;
-    private PackageManagerInternal mPackageManagerInternal;
+    private PackageManager mPackageManager;
 
     public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, BrightnessMappingStrategy mapper,
@@ -225,7 +221,8 @@
             int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
             long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
             HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds) {
+            HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
+            PackageManager packageManager) {
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
         mBrightnessMapper = mapper;
@@ -243,6 +240,7 @@
         mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
         mAmbientBrightnessThresholds = ambientBrightnessThresholds;
         mScreenBrightnessThresholds = screenBrightnessThresholds;
+        mShortTermModelTimeout = shortTermModelTimeout;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
 
@@ -254,9 +252,8 @@
             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
         }
 
-        mUserId = ActivityManager.getCurrentUser();
         mActivityTaskManager = ActivityTaskManager.getService();
-        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mPackageManager = packageManager;
         mTaskStackListener = new TaskStackListenerImpl();
         mForegroundAppPackageName = null;
         mPendingForegroundAppPackageName = null;
@@ -281,16 +278,6 @@
         return true;
     }
 
-    /**
-     * Update the current user's ID.
-     *
-     * @param userId
-     *      The current user's ID.
-     */
-    public void onSwitchUser(int userId) {
-        mUserId = userId;
-    }
-
     public int getAutomaticScreenBrightness() {
         if (!mAmbientLuxValid) {
             return -1;
@@ -358,7 +345,7 @@
         }
         if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
             mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
-                    SHORT_TERM_MODEL_TIMEOUT_MILLIS);
+                    mShortTermModelTimeout);
         } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
             mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
         }
@@ -440,13 +427,13 @@
         pw.println("  mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
         pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
         pw.println("  mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
+        pw.println("  mShortTermModelTimeout=" + mShortTermModelTimeout);
         pw.println("  mShortTermModelAnchor=" + mShortTermModelAnchor);
         pw.println("  mShortTermModelValid=" + mShortTermModelValid);
         pw.println("  mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
         pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
         pw.println("  mBrightnessAdjustmentSampleOldBrightness="
                 + mBrightnessAdjustmentSampleOldBrightness);
-        pw.println("  mUserId=" + mUserId);
         pw.println("  mForegroundAppPackageName=" + mForegroundAppPackageName);
         pw.println("  mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
         pw.println("  mForegroundAppCategory=" + mForegroundAppCategory);
@@ -787,6 +774,13 @@
                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
     }
 
+    private void cancelBrightnessAdjustmentSample() {
+        if (mBrightnessAdjustmentSamplePending) {
+            mBrightnessAdjustmentSamplePending = false;
+            mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
+        }
+    }
+
     private void collectBrightnessAdjustmentSample() {
         if (mBrightnessAdjustmentSamplePending) {
             mBrightnessAdjustmentSamplePending = false;
@@ -816,6 +810,9 @@
             // call it explicitly to get the current foreground app's info.
             updateForegroundApp();
         } catch (RemoteException e) {
+            if (mLoggingEnabled) {
+                Slog.e(TAG, "Failed to register foreground app updater: " + e);
+            }
             // Nothing to do.
         }
     }
@@ -832,6 +829,9 @@
 
     // Set the foreground app's package name and category, so brightness can be corrected per app.
     private void updateForegroundApp() {
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "Attempting to update foreground app");
+        }
         // The ActivityTaskManager's lock tends to get contended, so this is done in a background
         // thread and applied via this thread's handler synchronously.
         BackgroundThread.getHandler().post(new Runnable() {
@@ -850,18 +850,27 @@
                         return;
                     }
                     mPendingForegroundAppPackageName = packageName;
-                    ApplicationInfo app = mPackageManagerInternal.getApplicationInfo(packageName,
-                            0 /* flags */, Process.SYSTEM_UID /* filterCallingUid */, mUserId);
-                    mPendingForegroundAppCategory = app.category;
+                    mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+                    try {
+                        ApplicationInfo app = mPackageManager.getApplicationInfo(packageName,
+                                PackageManager.MATCH_ANY_USER);
+                        mPendingForegroundAppCategory = app.category;
+                    } catch (PackageManager.NameNotFoundException e) {
+                        // Nothing to do
+                    }
                     mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
                 } catch (RemoteException e) {
-                    // Nothing to do.
+                    // Nothing to do
                 }
             }
         });
     }
 
     private void updateForegroundAppSync() {
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "Updating foreground app: packageName=" + mPendingForegroundAppPackageName
+                    + ", category=" + mPendingForegroundAppCategory);
+        }
         mForegroundAppPackageName = mPendingForegroundAppPackageName;
         mPendingForegroundAppPackageName = null;
         mForegroundAppCategory = mPendingForegroundAppCategory;
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 73d3d95..9223739 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -18,20 +18,26 @@
 
 import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
 import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION;
 
+import android.Manifest;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.Size;
 import android.app.AlarmManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.ColorSpace;
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.IColorDisplayManager;
 import android.net.Uri;
@@ -39,28 +45,35 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.provider.Settings.System;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AnimationUtils;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
+import com.android.internal.util.DumpUtils;
 import com.android.server.DisplayThread;
 import com.android.server.SystemService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.time.DateTimeException;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeParseException;
+import java.util.Arrays;
 
 /**
  * Controls the display's color transforms.
@@ -83,6 +96,10 @@
         Matrix.setIdentityM(MATRIX_IDENTITY, 0);
     }
 
+    private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
+    private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
+    private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
+
     /**
      * Evaluator used to animate color matrix transitions.
      */
@@ -139,30 +156,280 @@
     };
 
     private final TintController mDisplayWhiteBalanceTintController = new TintController() {
+        // Three chromaticity coordinates per color: X, Y, and Z
+        private final int NUM_VALUES_PER_PRIMARY = 3;
+        // Four colors: red, green, blue, and white
+        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
 
+        private final Object mLock = new Object();
+        private int mTemperatureMin;
+        private int mTemperatureMax;
+        private int mTemperatureDefault;
+        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+        private ColorSpace.Rgb mDisplayColorSpaceRGB;
+        private float[] mChromaticAdaptationMatrix;
+        private int mCurrentColorTemperature;
+        private float[] mCurrentColorTemperatureXYZ;
+        private boolean mSetUp = false;
         private float[] mMatrixDisplayWhiteBalance = new float[16];
 
         @Override
         public void setUp(Context context, boolean needsLinear) {
+            mSetUp = false;
+
+            final Resources res = getContext().getResources();
+            final String[] displayPrimariesValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayPrimaries);
+            final String[] nominalWhiteValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
+
+            if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) {
+                Slog.e(TAG, "Unexpected display white balance primaries resource length " +
+                        displayPrimariesValues.length);
+                return;
+            }
+
+            if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) {
+                Slog.e(TAG, "Unexpected display white balance nominal white resource length " +
+                        nominalWhiteValues.length);
+                return;
+            }
+
+            float[] displayRedGreenBlueXYZ =
+                new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
+                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
+            }
+            for (int i = 0; i < displayWhiteXYZ.length; i++) {
+                displayWhiteXYZ[i] = Float.parseFloat(
+                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
+            }
+
+            final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
+                "Display Color Space",
+                displayRedGreenBlueXYZ,
+                displayWhiteXYZ,
+                2.2f // gamma, unused for display white balance
+            );
+
+            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+            for (int i = 0; i < nominalWhiteValues.length; i++) {
+                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
+            }
+
+            final int colorTemperatureMin = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
+            if (colorTemperatureMin <= 0) {
+                Slog.e(TAG, "display white balance minimum temperature must be greater than 0");
+                return;
+            }
+
+            final int colorTemperatureMax = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
+            if (colorTemperatureMax < colorTemperatureMin) {
+                Slog.e(TAG, "display white balance max temp must be greater or equal to min");
+                return;
+            }
+
+            final int colorTemperature = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
+
+            synchronized (mLock) {
+                mDisplayColorSpaceRGB = displayColorSpaceRGB;
+                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
+                mTemperatureMin = colorTemperatureMin;
+                mTemperatureMax = colorTemperatureMax;
+                mTemperatureDefault = colorTemperature;
+                mSetUp = true;
+            }
+
+            setMatrix(mTemperatureDefault);
+        }
+
+        @Override
+        public float[] getMatrix() {
+            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+        }
+
+        @Override
+        public void setMatrix(int cct) {
+            if (!mSetUp) {
+                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
+                return;
+            }
+
+            if (cct < mTemperatureMin) {
+                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
+                cct = mTemperatureMin;
+            } else if (cct > mTemperatureMax) {
+                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
+                cct = mTemperatureMax;
+            }
+
+            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
+
+            synchronized (mLock) {
+                mCurrentColorTemperature = cct;
+
+                // Adapt the display's nominal white point to match the requested CCT value
+                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
+
+                mChromaticAdaptationMatrix =
+                    ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
+                            mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
+
+                // Convert the adaptation matrix to RGB space
+                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
+                        mDisplayColorSpaceRGB.getTransform());
+                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
+
+                // Normalize the transform matrix to peak white value in RGB space
+                final float adaptedMaxR = result[0] + result[3] + result[6];
+                final float adaptedMaxG = result[1] + result[4] + result[7];
+                final float adaptedMaxB = result[2] + result[5] + result[8];
+                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
+                for (int i = 0; i < result.length; i++) {
+                    result[i] /= denum;
+                }
+
+                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
+                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
+                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
+                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
+            }
+        }
+
+        @Override
+        public int getLevel() {
+            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
+        }
+
+        /**
+         * Format a given matrix into a string.
+         *
+         * @param matrix the matrix to format
+         * @param cols number of columns in the matrix
+         */
+        private String matrixToString(float[] matrix, int cols) {
+            if (matrix == null || cols <= 0) {
+                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
+                return "";
+            }
+
+            StringBuilder sb = new StringBuilder("");
+            for (int i = 0; i < matrix.length; i++) {
+                if (i % cols == 0) {
+                    sb.append("\n    ");
+                }
+                sb.append(String.format("%9.6f ", matrix[i]));
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void dump(PrintWriter pw) {
+            synchronized (mLock) {
+                pw.println("ColorDisplayService");
+                pw.println("  mSetUp = " + mSetUp);
+
+                if (!mSetUp) {
+                    return;
+                }
+
+                pw.println("  isActivated = " + isActivated());
+                pw.println("  mTemperatureMin = " + mTemperatureMin);
+                pw.println("  mTemperatureMax = " + mTemperatureMax);
+                pw.println("  mTemperatureDefault = " + mTemperatureDefault);
+                pw.println("  mCurrentColorTemperature = " + mCurrentColorTemperature);
+                pw.println("  mCurrentColorTemperatureXYZ = " +
+                        matrixToString(mCurrentColorTemperatureXYZ, 3));
+                pw.println("  mDisplayColorSpaceRGB RGB-to-XYZ = " +
+                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
+                pw.println("  mChromaticAdaptationMatrix = " +
+                        matrixToString(mChromaticAdaptationMatrix, 3));
+                pw.println("  mDisplayColorSpaceRGB XYZ-to-RGB = " +
+                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
+                pw.println("  mMatrixDisplayWhiteBalance = " +
+                        matrixToString(mMatrixDisplayWhiteBalance, 4));
+            }
+        }
+    };
+
+    private final TintController mGlobalSaturationTintController = new TintController() {
+
+        private float[] mMatrixGlobalSaturation = new float[16];
+
+        @Override
+        public void setUp(Context context, boolean needsLinear) {
         }
 
         @Override
         public float[] getMatrix() {
-            return isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+            return Arrays.copyOf(mMatrixGlobalSaturation, mMatrixGlobalSaturation.length);
         }
 
         @Override
-        public void setMatrix(int cct) {
+        public void setMatrix(int saturationLevel) {
+            if (saturationLevel < 0) {
+                saturationLevel = 0;
+            } else if (saturationLevel > 100) {
+                saturationLevel = 100;
+            }
+            Slog.d(TAG, "Setting saturation level: " + saturationLevel);
+
+            if (saturationLevel == 100) {
+                Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
+            } else {
+                float saturation = saturationLevel * 0.1f;
+                float desaturation = 1.0f - saturation;
+                float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
+                    0.072f * desaturation};
+                mMatrixGlobalSaturation[0] = luminance[0] + saturation;
+                mMatrixGlobalSaturation[1] = luminance[0];
+                mMatrixGlobalSaturation[2] = luminance[0];
+                mMatrixGlobalSaturation[4] = luminance[1];
+                mMatrixGlobalSaturation[5] = luminance[1] + saturation;
+                mMatrixGlobalSaturation[6] = luminance[1];
+                mMatrixGlobalSaturation[8] = luminance[2];
+                mMatrixGlobalSaturation[9] = luminance[2];
+                mMatrixGlobalSaturation[10] = luminance[2] + saturation;
+            }
         }
 
         @Override
         public int getLevel() {
-            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
+            return LEVEL_COLOR_MATRIX_SATURATION;
         }
     };
 
+    /**
+     * Matrix and offset used for converting color to grayscale.
+     */
+    private static final float[] MATRIX_GRAYSCALE = new float[]{
+            .2126f, .2126f, .2126f, 0f,
+            .7152f, .7152f, .7152f, 0f,
+            .0722f, .0722f, .0722f, 0f,
+            0f, 0f, 0f, 1f
+    };
+
+    /**
+     * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
+     * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
+     * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
+     * ProgramCache for full implementation details.
+     */
+    private static final float[] MATRIX_INVERT_COLOR = new float[] {
+            0.402f, -0.598f, -0.599f, 0f,
+            -1.174f, -0.174f, -1.175f, 0f,
+            -0.228f, -0.228f, 0.772f, 0f,
+            1f, 1f, 1f, 1f
+    };
+
     private final Handler mHandler;
 
+    private final AppSaturationController mAppSaturationController = new AppSaturationController();
+
     private int mCurrentUser = UserHandle.USER_NULL;
     private ContentObserver mUserSetupObserver;
     private boolean mBootCompleted;
@@ -174,11 +441,9 @@
 
     private NightDisplayAutoMode mNightDisplayAutoMode;
 
-    private Integer mDisplayWhiteBalanceColorTemperature;
-
     public ColorDisplayService(Context context) {
         super(context);
-        mHandler = new Handler(Looper.getMainLooper());
+        mHandler = new TintHandler(Looper.getMainLooper());
     }
 
     @Override
@@ -302,12 +567,19 @@
                             case System.DISPLAY_COLOR_MODE:
                                 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
                                 break;
-                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
                             case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
-                                onAccessibilityTransformChanged();
+                                onAccessibilityInversionChanged();
+                                onAccessibilityActivated();
+                                break;
+                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
+                                onAccessibilityDaltonizerChanged();
+                                onAccessibilityActivated();
+                                break;
+                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
+                                onAccessibilityDaltonizerChanged();
                                 break;
                             case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
-                                onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
+                                updateDisplayWhiteBalanceStatus();
                                 break;
                         }
                     }
@@ -333,6 +605,9 @@
         cr.registerContentObserver(
                 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
                 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(
+                Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
         cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
                 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
 
@@ -360,14 +635,9 @@
 
         if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
             // Prepare the display white balance transform matrix.
-            mDisplayWhiteBalanceTintController
-                    .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
-            if (mDisplayWhiteBalanceColorTemperature != null) {
-                mDisplayWhiteBalanceTintController
-                        .setMatrix(mDisplayWhiteBalanceColorTemperature);
-            }
+            mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
 
-            onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
+            updateDisplayWhiteBalanceStatus();
         }
     }
 
@@ -404,6 +674,8 @@
                 mNightDisplayAutoMode.onActivated(activated);
             }
 
+            updateDisplayWhiteBalanceStatus();
+
             applyTint(mNightDisplayTintController, false);
         }
     }
@@ -460,20 +732,49 @@
                 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
         mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
 
-        mDisplayWhiteBalanceTintController
-                .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
-        if (mDisplayWhiteBalanceColorTemperature != null) {
-            mDisplayWhiteBalanceTintController.setMatrix(mDisplayWhiteBalanceColorTemperature);
-        }
+        updateDisplayWhiteBalanceStatus();
 
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
     }
 
-    private void onAccessibilityTransformChanged() {
+    private void onAccessibilityActivated() {
         onDisplayColorModeChanged(mNightDisplayController.getColorMode());
     }
 
+    /**
+     * Apply the accessibility daltonizer transform based on the settings value.
+     */
+    private void onAccessibilityDaltonizerChanged() {
+        final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
+        final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+                AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
+                : AccessibilityManager.DALTONIZER_DISABLED;
+
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+        if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
+            // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
+            dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
+                    MATRIX_GRAYSCALE);
+            dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
+        } else {
+            dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
+            dtm.setDaltonizerMode(daltonizerMode);
+        }
+    }
+
+    /**
+     * Apply the accessibility inversion transform based on the settings value.
+     */
+    private void onAccessibilityInversionChanged() {
+        final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+        dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
+                enabled ? MATRIX_INVERT_COLOR : null);
+    }
 
     /**
      * Applies current color temperature matrix, or removes it if deactivated.
@@ -555,10 +856,15 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
-    private void onDisplayWhiteBalanceEnabled(boolean enabled) {
-        mDisplayWhiteBalanceTintController.setActivated(enabled);
-        if (mDisplayWhiteBalanceListener != null) {
-            mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(enabled);
+    private void updateDisplayWhiteBalanceStatus() {
+        boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
+        mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
+                !mNightDisplayTintController.isActivated() &&
+                DisplayTransformManager.needsLinearColorMatrix());
+        boolean activated = mDisplayWhiteBalanceTintController.isActivated();
+
+        if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
+            mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
         }
     }
 
@@ -596,6 +902,22 @@
         return LocalDateTime.MIN;
     }
 
+    private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
+        return mAppSaturationController
+                .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
+        pw.println("Night Display:");
+        if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
+            pw.println("    Activated: " + mNightDisplayTintController.isActivated());
+        } else {
+            pw.println("    Not available");
+        }
+        mAppSaturationController.dump(pw);
+    }
+
     private abstract class NightDisplayAutoMode {
 
         public abstract void onActivated(boolean activated);
@@ -840,6 +1162,12 @@
         }
 
         /**
+         * Dump debug information.
+         */
+        public void dump(PrintWriter pw) {
+        }
+
+        /**
          * Set up any constants needed for computing the matrix.
          */
         public abstract void setUp(Context context, boolean needsLinear);
@@ -873,11 +1201,10 @@
          */
         public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
             // Update the transform matrix even if it can't be applied.
-            mDisplayWhiteBalanceColorTemperature = cct;
             mDisplayWhiteBalanceTintController.setMatrix(cct);
 
             if (mDisplayWhiteBalanceTintController.isActivated()) {
-                applyTint(mDisplayWhiteBalanceTintController, true);
+                applyTint(mDisplayWhiteBalanceTintController, false);
                 return true;
             }
             return false;
@@ -890,6 +1217,20 @@
             mDisplayWhiteBalanceListener = listener;
             return mDisplayWhiteBalanceTintController.isActivated();
         }
+
+        public void dump(PrintWriter pw) {
+            mDisplayWhiteBalanceTintController.dump(pw);
+        }
+
+        /**
+         * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
+         * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
+         */
+        public boolean attachColorTransformController(String packageName, int uid,
+                WeakReference<ColorTransformController> controller) {
+            return mAppSaturationController
+                    .addColorTransformController(packageName, uid, controller);
+        }
     }
 
     /**
@@ -904,6 +1245,32 @@
         void onDisplayWhiteBalanceStatusChanged(boolean enabled);
     }
 
+    private final class TintHandler extends Handler {
+
+        TintHandler(Looper looper) {
+            super(looper, null, true /* async */);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_APPLY_GLOBAL_SATURATION:
+                    mGlobalSaturationTintController.setMatrix(msg.arg1);
+                    applyTint(mGlobalSaturationTintController, false);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Interface for applying transforms to a given AppWindow.
+     */
+    public interface ColorTransformController {
+
+        /** Apply the given saturation (grayscale) matrix to the associated AppWindow. */
+        void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
+    }
+
     private final class BinderService extends IColorDisplayManager.Stub {
 
         @Override
@@ -915,5 +1282,52 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public boolean setSaturationLevel(int level) {
+            final boolean hasTransformsPermission = getContext()
+                    .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+                    == PackageManager.PERMISSION_GRANTED;
+            final boolean hasLegacyPermission = getContext()
+                    .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
+                    == PackageManager.PERMISSION_GRANTED;
+            if (!hasTransformsPermission && !hasLegacyPermission) {
+                throw new SecurityException("Permission required to set display saturation level");
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
+                message.arg1 = level;
+                mHandler.sendMessage(message);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return true;
+        }
+
+        @Override
+        public boolean setAppSaturationLevel(String packageName, int level) {
+            getContext().enforceCallingPermission(
+                    Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+                    "Permission required to set display saturation level");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return setAppSaturationLevelInternal(packageName, level);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                dumpInternal(pw);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b1ba05c..cb3f91b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -95,6 +95,7 @@
 import com.android.server.UiThread;
 import com.android.server.wm.SurfaceAnimationThread;
 import com.android.server.wm.WindowManagerInternal;
+import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -724,27 +725,6 @@
         }
     }
 
-    private void setSaturationLevelInternal(float level) {
-        if (level < 0 || level > 1) {
-            throw new IllegalArgumentException("Saturation level must be between 0 and 1");
-        }
-        float[] matrix = (level == 1.0f ? null : computeSaturationMatrix(level));
-        DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-        dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION, matrix);
-    }
-
-    private static float[] computeSaturationMatrix(float saturation) {
-        float desaturation = 1.0f - saturation;
-        float[] luminance = {0.231f * desaturation, 0.715f * desaturation, 0.072f * desaturation};
-        float[] matrix = {
-            luminance[0] + saturation, luminance[0], luminance[0], 0,
-            luminance[1], luminance[1] + saturation, luminance[1], 0,
-            luminance[2], luminance[2], luminance[2] + saturation, 0,
-            0, 0, 0, 1
-        };
-        return matrix;
-    }
-
     private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
             IMediaProjection projection, int callingUid, String packageName, String name, int width,
             int height, int densityDpi, Surface surface, int flags, String uniqueId) {
@@ -812,6 +792,16 @@
         }
     }
 
+    private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) {
+        synchronized (mSyncRoot) {
+            if (mVirtualDisplayAdapter == null) {
+                return;
+            }
+
+            mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn);
+        }
+    }
+
     private void registerDefaultDisplayAdapters() {
         // Register default display adapters.
         synchronized (mSyncRoot) {
@@ -1480,6 +1470,13 @@
 
             pw.println();
             mPersistentDataStore.dump(pw);
+
+            final ColorDisplayServiceInternal cds = LocalServices.getService(
+                    ColorDisplayServiceInternal.class);
+            if (cds != null) {
+                pw.println();
+                cds.dump(pw);
+            }
         }
     }
 
@@ -1836,19 +1833,6 @@
         }
 
         @Override // Binder call
-        public void setSaturationLevel(float level) {
-            mContext.enforceCallingOrSelfPermission(
-                   Manifest.permission.CONTROL_DISPLAY_SATURATION,
-                   "Permission required to set display saturation level");
-            final long token = Binder.clearCallingIdentity();
-            try {
-                setSaturationLevelInternal(level);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override // Binder call
         public int createVirtualDisplay(IVirtualDisplayCallback callback,
                 IMediaProjection projection, String packageName, String name,
                 int width, int height, int densityDpi, Surface surface, int flags,
@@ -1956,6 +1940,16 @@
         }
 
         @Override // Binder call
+        public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setVirtualDisplayStateInternal(callback.asBinder(), isOn);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c9ed9f7..db3928e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -461,6 +461,8 @@
                         + initialLightSensorRate + ") to be less than or equal to "
                         + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
             }
+            int shortTermModelTimeout = resources.getInteger(
+                    com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
 
             mBrightnessMapper = BrightnessMappingStrategy.create(resources);
             if (mBrightnessMapper != null) {
@@ -470,7 +472,8 @@
                         mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                        screenBrightnessThresholds);
+                        screenBrightnessThresholds, shortTermModelTimeout,
+                        context.getPackageManager());
             } else {
                 mUseSoftwareAutoBrightnessConfig = false;
             }
@@ -523,9 +526,6 @@
     public void onSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         mBrightnessTracker.onSwitchUser(newUserId);
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.onSwitchUser(newUserId);
-        }
     }
 
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 5aa585f..1ca8dd3 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -147,6 +147,13 @@
         return device;
     }
 
+    void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) {
+        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+        if (device != null) {
+            device.setDisplayState(isOn);
+        }
+    }
+
     /**
      * Returns the next unique index for the uniqueIdPrefix
      */
@@ -206,6 +213,7 @@
         private int mPendingChanges;
         private int mUniqueIndex;
         private Display.Mode mMode;
+        private boolean mIsDisplayOn;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName,
@@ -226,6 +234,7 @@
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
             mUniqueIndex = uniqueIndex;
+            mIsDisplayOn = surface != null;
         }
 
         @Override
@@ -304,6 +313,14 @@
             }
         }
 
+        void setDisplayState(boolean isOn) {
+            if (mIsDisplayOn != isOn) {
+                mIsDisplayOn = isOn;
+                mInfo = null;
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+            }
+        }
+
         public void stopLocked() {
             setSurfaceLocked(null);
             mStopped = true;
@@ -375,7 +392,9 @@
                 mInfo.type = Display.TYPE_VIRTUAL;
                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
                         DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
-                mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
+
+                mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF;
+
                 mInfo.ownerUid = mOwnerUid;
                 mInfo.ownerPackageName = mOwnerPackageName;
             }
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index ba05b49..bffa8f4 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -18,13 +18,6 @@
 
 import static android.Manifest.permission.BIND_DREAM_SERVICE;
 
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.internal.util.DumpUtils;
-import com.android.server.FgThread;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-
-import android.Manifest;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -53,6 +46,12 @@
 import android.util.Slog;
 import android.view.Display;
 
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -84,6 +83,7 @@
     private boolean mCurrentDreamCanDoze;
     private boolean mCurrentDreamIsDozing;
     private boolean mCurrentDreamIsWaking;
+    private boolean mForceAmbientDisplayEnabled;
     private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
     private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
 
@@ -139,6 +139,7 @@
         pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
         pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
         pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
+        pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
         pw.println("mCurrentDreamDozeScreenState="
                 + Display.stateToString(mCurrentDreamDozeScreenState));
         pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness);
@@ -257,6 +258,16 @@
         }
     }
 
+    private void forceAmbientDisplayEnabledInternal(boolean enabled) {
+        if (DEBUG) {
+            Slog.d(TAG, "Force ambient display enabled: " + enabled);
+        }
+
+        synchronized (mLock) {
+            mForceAmbientDisplayEnabled = enabled;
+        }
+    }
+
     private ComponentName chooseDreamForUser(boolean doze, int userId) {
         if (doze) {
             ComponentName dozeComponent = getDozeComponent(userId);
@@ -328,7 +339,7 @@
     }
 
     private ComponentName getDozeComponent(int userId) {
-        if (mDozeConfig.enabled(userId)) {
+        if (mForceAmbientDisplayEnabled || mDozeConfig.enabled(userId)) {
             return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent());
         } else {
             return null;
@@ -631,6 +642,18 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
+
+        @Override // Binder call
+        public void forceAmbientDisplayEnabled(boolean enabled) {
+            checkPermission(android.Manifest.permission.DEVICE_POWER);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                forceAmbientDisplayEnabledInternal(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
     private final class LocalService extends DreamManagerInternal {
diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
index ed17de5..137833c 100644
--- a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
@@ -21,14 +21,15 @@
  * Feature action that handles Audio Return Channel initiated by AVR devices.
  */
 public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction {
-    // TODO(shubang): add tests
-
     // State in which waits for ARC response.
     private static final int STATE_WAITING_FOR_INITIATE_ARC_RESPONSE = 1;
     private static final int STATE_ARC_INITIATED = 2;
 
     // the required maximum response time specified in CEC 9.2
     private static final int TIMEOUT_MS = 1000;
+    private static final int MAX_RETRY_COUNT = 5;
+
+    private int mSendRequestActiveSourceRetryCount = 0;
 
     ArcInitiationActionFromAvr(HdmiCecLocalDevice source) {
         super(source);
@@ -50,13 +51,25 @@
         }
         switch (cmd.getOpcode()) {
             case Constants.MESSAGE_FEATURE_ABORT:
+                if ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_INITIATE_ARC) {
+                    audioSystem().setArcStatus(false);
+                    finish();
+                    return true;
+                } else {
+                    return false;
+                }
             case Constants.MESSAGE_REPORT_ARC_TERMINATED:
                 audioSystem().setArcStatus(false);
                 finish();
                 return true;
             case Constants.MESSAGE_REPORT_ARC_INITIATED:
                 mState = STATE_ARC_INITIATED;
-                finish();
+                if (audioSystem().getActiveSource().physicalAddress != getSourcePath()
+                        && audioSystem().isSystemAudioActivated()) {
+                    sendRequestActiveSource();
+                } else {
+                    finish();
+                }
                 return true;
         }
         return false;
@@ -91,4 +104,19 @@
         finish();
     }
 
+    protected void sendRequestActiveSource() {
+        sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()),
+                result -> {
+                    if (result != SendMessageResult.SUCCESS) {
+                        if (mSendRequestActiveSourceRetryCount < MAX_RETRY_COUNT) {
+                            mSendRequestActiveSourceRetryCount++;
+                            sendRequestActiveSource();
+                        } else {
+                            finish();
+                        }
+                    } else {
+                        finish();
+                    }
+                });
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
index 7e73321..eb7c0cd 100644
--- a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
@@ -50,6 +50,9 @@
             case Constants.MESSAGE_REPORT_ARC_TERMINATED:
                 mState = STATE_ARC_TERMINATED;
                 audioSystem().setArcStatus(false);
+                if (audioSystem().getLocalActivePort() == Constants.CEC_SWITCH_ARC) {
+                    audioSystem().routeToInputFromPortId(audioSystem().getRoutingPort());
+                }
                 finish();
                 return true;
         }
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index d154830..297a418 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -17,7 +17,9 @@
 package com.android.server.hdmi;
 
 import android.annotation.IntDef;
+import android.annotation.StringDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -221,6 +223,15 @@
     static final int AUDIO_CODEC_WMAPRO = 0xE; // Support WMA-Pro
     static final int AUDIO_CODEC_MAX = 0xF;
 
+    @StringDef({
+        AUDIO_DEVICE_ARC_IN,
+        AUDIO_DEVICE_SPDIF,
+    })
+    public @interface AudioDevice {}
+
+    static final String AUDIO_DEVICE_ARC_IN = "ARC_IN";
+    static final String AUDIO_DEVICE_SPDIF = "SPDIF";
+
     // Bit mask used to get the routing path of the top level device.
     // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
     static final int ROUTING_PATH_TOP_MASK = 0xF000;
@@ -255,6 +266,41 @@
     static final int USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 1;
     static final int NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 2;
 
+    // Port id to record local active port for Routing Control features
+    // They are used to map to corresponding Inputs
+    // Current interface is only implemented for specific device.
+    // Developers can add more port number and map them to corresponding inputs on demand.
+    @IntDef({
+        CEC_SWITCH_HOME,
+        CEC_SWITCH_HDMI1,
+        CEC_SWITCH_HDMI2,
+        CEC_SWITCH_HDMI3,
+        CEC_SWITCH_HDMI4,
+        CEC_SWITCH_HDMI5,
+        CEC_SWITCH_HDMI6,
+        CEC_SWITCH_HDMI7,
+        CEC_SWITCH_HDMI8,
+        CEC_SWITCH_ARC,
+        CEC_SWITCH_BLUETOOTH,
+        CEC_SWITCH_OPTICAL,
+        CEC_SWITCH_AUX
+    })
+    @interface LocalActivePort {}
+    static final int CEC_SWITCH_HOME = 0;
+    static final int CEC_SWITCH_HDMI1 = 1;
+    static final int CEC_SWITCH_HDMI2 = 2;
+    static final int CEC_SWITCH_HDMI3 = 3;
+    static final int CEC_SWITCH_HDMI4 = 4;
+    static final int CEC_SWITCH_HDMI5 = 5;
+    static final int CEC_SWITCH_HDMI6 = 6;
+    static final int CEC_SWITCH_HDMI7 = 7;
+    static final int CEC_SWITCH_HDMI8 = 8;
+    static final int CEC_SWITCH_ARC = 17;
+    static final int CEC_SWITCH_BLUETOOTH = 18;
+    static final int CEC_SWITCH_OPTICAL = 19;
+    static final int CEC_SWITCH_AUX = 20;
+    static final int CEC_SWITCH_PORT_MAX = 21;
+
     static final String PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM =
             "persist.sys.hdmi.addr.audiosystem";
     static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
@@ -272,6 +318,41 @@
     // TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default.
     static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
 
+    /**
+     * Property to save the ARC port id on system audio device.
+     * <p>When ARC is initiated, this port will be used to turn on ARC.
+     */
+    static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
+            "ro.hdmi.property_sytem_audio_device_arc_port";
+
+    /**
+     * Property to disable muting logic in System Audio Control handling. Default is true.
+     *
+     * <p>True means enabling muting logic.
+     * <p>False means never mute device.
+     */
+    static final String PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE =
+            "ro.hdmi.property_system_audio_mode_muting_enable";
+
+    /**
+     * When set to true the HdmiControlService will never request a Logical Address for the
+     * playback device type. Default is false.
+     *
+     * <p> This is useful when HDMI CEC multiple device types is not supported by the cec driver
+     */
+    static final String PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS =
+            "ro.hdmi.property_hdmi_cec_never_claim_playback_logical_address";
+
+    /**
+     * A comma separated list of logical addresses that HdmiControlService
+     * will never assign local CEC devices to.
+     *
+     * <p> This is useful when HDMI CEC hardware module can't assign multiple logical addresses
+     * in the range same range of 0-7 or 8-15.
+     */
+    static final String PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES =
+            "ro.hdmi.property_hdmi_cec_never_assign_logical_addresses";
+
     // Set to false to allow playback device to go to suspend mode even
     // when it's an active source. True by default.
     static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
@@ -299,7 +380,7 @@
      * <p>Default is true.
      */
     static final String PROPERTY_ARC_SUPPORT =
-        "persist.sys.hdmi.property_arc_support";
+            "persist.sys.hdmi.property_arc_support";
 
     /**
      * Property to save the audio port to switch to when system audio control is on.
@@ -309,14 +390,16 @@
      * <p>Default is ARC port.
      */
     static final String PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT =
-        "persist.sys.hdmi.property_sytem_audio_mode_audio_port";
+            "persist.sys.hdmi.property_sytem_audio_mode_audio_port";
 
     /**
-     * Property to save the ARC port id on system audio device.
-     * <p>When ARC is initiated, this port will be used to turn on ARC.
+     * Property to indicate if a CEC audio device should forward volume keys when system audio mode
+     * is off.
+     *
+     * <p>Default is false.
      */
-    static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
-        "persist.sys.hdmi.property_sytem_audio_device_arc_port";
+    static final String PROPERTY_CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF =
+            "persist.sys.hdmi.property_cec_audio_device_forward_volume_keys_system_audio_mode_off";
 
     /**
      * Property to strip local audio of amplifier and use local speaker
diff --git a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
index 0495ff8..7187319 100644
--- a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
+++ b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
@@ -50,8 +50,10 @@
             if (mState != STATE_WAITING_FOR_FEATURE_ABORT) {
                 return false;
             }
-            finishAction(false);
-            return true;
+            if ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE) {
+                finishAction(false);
+                return true;
+            }
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index b75e75f..df0dc77 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -16,6 +16,7 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.Slog;
 
@@ -28,7 +29,7 @@
 
 /**
  * Feature action that handles device discovery sequences.
- * Device discovery is launched when TV device is woken from "Standby" state
+ * Device discovery is launched when device is woken from "Standby" state
  * or enabled "Control for Hdmi" from disabled state.
  *
  * <p>Device discovery goes through the following steps.
@@ -51,6 +52,10 @@
     private static final int STATE_WAITING_FOR_OSD_NAME = 3;
     // State in which the action is waiting for gathering vendor id of non-local devices.
     private static final int STATE_WAITING_FOR_VENDOR_ID = 4;
+    // State in which the action is waiting for devices to be ready.
+    private static final int STATE_WAITING_FOR_DEVICES = 5;
+    // State in which the action is waiting for gathering power status of non-local devices.
+    private static final int STATE_WAITING_FOR_POWER = 6;
 
     /**
      * Interface used to report result of device discovery.
@@ -72,6 +77,7 @@
         private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
         private int mPortId = Constants.INVALID_PORT_ID;
         private int mVendorId = Constants.UNKNOWN_VENDOR_ID;
+        private int mPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
         private String mDisplayName = "";
         private int mDeviceType = HdmiDeviceInfo.DEVICE_INACTIVE;
 
@@ -81,7 +87,7 @@
 
         private HdmiDeviceInfo toHdmiDeviceInfo() {
             return new HdmiDeviceInfo(mLogicalAddress, mPhysicalAddress, mPortId, mDeviceType,
-                    mVendorId, mDisplayName);
+                    mVendorId, mDisplayName, mPowerStatus);
         }
     }
 
@@ -89,6 +95,20 @@
     private final DeviceDiscoveryCallback mCallback;
     private int mProcessedDeviceCount = 0;
     private int mTimeoutRetry = 0;
+    private boolean mIsTvDevice = localDevice().mService.isTvDevice();
+    private final int mDelayPeriod;
+
+    /**
+     * Constructor.
+     *
+     * @param source an instance of {@link HdmiCecLocalDevice}.
+     * @param delay delay action for this period between query Physical Address and polling
+     */
+    DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback, int delay) {
+        super(source);
+        mCallback = Preconditions.checkNotNull(callback);
+        mDelayPeriod = delay;
+    }
 
     /**
      * Constructor.
@@ -96,8 +116,7 @@
      * @param source an instance of {@link HdmiCecLocalDevice}.
      */
     DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
-        super(source);
-        mCallback = Preconditions.checkNotNull(callback);
+        this(source, callback, 0);
     }
 
     @Override
@@ -116,7 +135,11 @@
 
                 Slog.v(TAG, "Device detected: " + ackedAddress);
                 allocateDevices(ackedAddress);
-                startPhysicalAddressStage();
+                if (mDelayPeriod > 0) {
+                    startToDelayAction();
+                } else {
+                    startPhysicalAddressStage();
+                }
             }
         }, Constants.POLL_ITERATION_REVERSE_ORDER
             | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.DEVICE_POLLING_RETRY);
@@ -130,6 +153,13 @@
         }
     }
 
+    private void startToDelayAction() {
+        Slog.v(TAG, "Waiting for connected devices to be ready");
+        mState = STATE_WAITING_FOR_DEVICES;
+
+        checkAndProceedStage();
+    }
+
     private void startPhysicalAddressStage() {
         Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
@@ -158,6 +188,11 @@
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
     }
 
+    private void delayActionWithTimePeriod(int timeDelay) {
+        mActionTimer.clearTimerMessage();
+        addTimer(mState, timeDelay);
+    }
+
     private void startOsdNameStage() {
         Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
@@ -206,6 +241,29 @@
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
     }
 
+    private void startPowerStatusStage() {
+        Slog.v(TAG, "Start [Power Status Stage]:" + mDevices.size());
+        mProcessedDeviceCount = 0;
+        mState = STATE_WAITING_FOR_POWER;
+
+        checkAndProceedStage();
+    }
+
+    private void queryPowerStatus(int address) {
+        if (!verifyValidLogicalAddress(address)) {
+            checkAndProceedStage();
+            return;
+        }
+
+        mActionTimer.clearTimerMessage();
+
+        if (mayProcessMessageIfCached(address, Constants.MESSAGE_REPORT_POWER_STATUS)) {
+            return;
+        }
+        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address));
+        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+    }
+
     private boolean mayProcessMessageIfCached(int address, int opcode) {
         HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
         if (message != null) {
@@ -244,6 +302,16 @@
                     return true;
                 }
                 return false;
+            case STATE_WAITING_FOR_POWER:
+                if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) {
+                    handleReportPowerStatus(cmd);
+                    return true;
+                } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT)
+                        && ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_REPORT_POWER_STATUS)) {
+                    handleReportPowerStatus(cmd);
+                    return true;
+                }
+                return false;
             case STATE_WAITING_FOR_DEVICE_POLLING:
                 // Fall through.
             default:
@@ -265,16 +333,21 @@
         current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
         current.mPortId = getPortId(current.mPhysicalAddress);
         current.mDeviceType = params[2] & 0xFF;
+        current.mDisplayName = HdmiUtils.getDefaultDeviceName(current.mDeviceType);
 
-        tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
+        // TODO(amyjojo): check if non-TV device needs to update cec switch info.
+        // This is to manager CEC device separately in case they don't have address.
+        if (mIsTvDevice) {
+            tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
                     current.mPhysicalAddress);
-
+        }
         increaseProcessedDeviceCount();
         checkAndProceedStage();
     }
 
     private int getPortId(int physicalAddress) {
-        return tv().getPortId(physicalAddress);
+        return mIsTvDevice ? tv().getPortId(physicalAddress)
+            : source().getPortId(physicalAddress);
     }
 
     private void handleSetOsdName(HdmiCecMessage cmd) {
@@ -324,6 +397,26 @@
         checkAndProceedStage();
     }
 
+    private void handleReportPowerStatus(HdmiCecMessage cmd) {
+        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
+
+        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
+        if (current.mLogicalAddress != cmd.getSource()) {
+            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:"
+                    + cmd.getSource());
+            return;
+        }
+
+        if (cmd.getOpcode() != Constants.MESSAGE_FEATURE_ABORT) {
+            byte[] params = cmd.getParams();
+            int powerStatus = params[0] & 0xFF;
+            current.mPowerStatus = powerStatus;
+        }
+
+        increaseProcessedDeviceCount();
+        checkAndProceedStage();
+    }
+
     private void increaseProcessedDeviceCount() {
         mProcessedDeviceCount++;
         mTimeoutRetry = 0;
@@ -345,7 +438,9 @@
         mCallback.onDeviceDiscoveryDone(result);
         finish();
         // Process any commands buffered while device discovery action was in progress.
-        tv().processAllDelayedMessages();
+        if (mIsTvDevice) {
+            tv().processAllDelayedMessages();
+        }
     }
 
     private void checkAndProceedStage() {
@@ -365,6 +460,9 @@
                     startVendorIdStage();
                     return;
                 case STATE_WAITING_FOR_VENDOR_ID:
+                    startPowerStatusStage();
+                    return;
+                case STATE_WAITING_FOR_POWER:
                     wrapUpAndFinish();
                     return;
                 default:
@@ -378,6 +476,9 @@
     private void sendQueryCommand() {
         int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
         switch (mState) {
+            case STATE_WAITING_FOR_DEVICES:
+                delayActionWithTimePeriod(mDelayPeriod);
+                return;
             case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
                 queryPhysicalAddress(address);
                 return;
@@ -387,6 +488,9 @@
             case STATE_WAITING_FOR_VENDOR_ID:
                 queryVendorId(address);
                 return;
+            case STATE_WAITING_FOR_POWER:
+                queryPowerStatus(address);
+                return;
             default:
                 return;
         }
@@ -398,13 +502,24 @@
             return;
         }
 
+        if (mState == STATE_WAITING_FOR_DEVICES) {
+            startPhysicalAddressStage();
+            return;
+        }
         if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
             sendQueryCommand();
             return;
         }
         mTimeoutRetry = 0;
         Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
-        removeDevice(mProcessedDeviceCount);
+        if (mState != STATE_WAITING_FOR_POWER && mState != STATE_WAITING_FOR_OSD_NAME) {
+            // We don't need to remove the device info if the power status is unknown.
+            // Some device does not have preferred OSD name and does not respond to Give OSD name.
+            // Like LG TV. We can give it default device name and not remove it.
+            removeDevice(mProcessedDeviceCount);
+        } else {
+            increaseProcessedDeviceCount();
+        }
         checkAndProceedStage();
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index de6cdd4..86be585 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -22,20 +22,25 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
+import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import libcore.util.EmptyArray;
+
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.function.Predicate;
 import java.util.concurrent.ArrayBlockingQueue;
-import libcore.util.EmptyArray;
+import java.util.function.Predicate;
+
 import sun.util.locale.LanguageTag;
 
 /**
@@ -72,7 +77,7 @@
 
     private static final int NUM_LOGICAL_ADDRESS = 16;
 
-    private static final int MAX_CEC_MESSAGE_HISTORY = 20;
+    private static final int MAX_CEC_MESSAGE_HISTORY = 200;
 
     // Predicate for whether the given logical address is remote device's one or not.
     private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@@ -112,10 +117,18 @@
 
     private final NativeWrapper mNativeWrapperImpl;
 
+    /** List of logical addresses that should not be assigned to the current device.
+     *
+     * <p>Parsed from {@link Constants#PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES}
+     */
+    private final List<Integer> mNeverAssignLogicalAddresses;
+
     // Private constructor.  Use HdmiCecController.create().
     private HdmiCecController(HdmiControlService service, NativeWrapper nativeWrapper) {
         mService = service;
         mNativeWrapperImpl = nativeWrapper;
+        mNeverAssignLogicalAddresses = mService.getIntList(SystemProperties.get(
+            Constants.PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES));
     }
 
     /**
@@ -208,7 +221,8 @@
         for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
             int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
             if (curAddress != Constants.ADDR_UNREGISTERED
-                    && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
+                    && deviceType == HdmiUtils.getTypeFromAddress(curAddress)
+                    && !mNeverAssignLogicalAddresses.contains(curAddress)) {
                 boolean acked = false;
                 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
                     if (sendPollMessage(curAddress, curAddress, 1)) {
@@ -668,7 +682,7 @@
 
     void dump(final IndentingPrintWriter pw) {
         for (int i = 0; i < mLocalDevices.size(); ++i) {
-            pw.println("HdmiCecLocalDevice #" + i + ":");
+            pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
             pw.increaseIndent();
             mLocalDevices.valueAt(i).dump(pw);
             pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 11faa56..2da698b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -252,6 +252,10 @@
         return (HdmiCecLocalDevicePlayback) mSource;
     }
 
+    protected final HdmiCecLocalDeviceSource source() {
+        return (HdmiCecLocalDeviceSource) mSource;
+    }
+
     protected final HdmiCecLocalDeviceTv tv() {
         return (HdmiCecLocalDeviceTv) mSource;
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7860122..414f6bb 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -18,19 +18,24 @@
 
 import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.hdmi.Constants.LocalActivePort;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -56,6 +61,7 @@
     protected final int mDeviceType;
     protected int mAddress;
     protected int mPreferredAddress;
+    @GuardedBy("mLock")
     protected HdmiDeviceInfo mDeviceInfo;
     protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
     protected int mLastKeyRepeatCount = 0;
@@ -125,9 +131,6 @@
             return s.toString();
         }
     }
-    // Logical address of the active source.
-    @GuardedBy("mLock")
-    protected final ActiveSource mActiveSource = new ActiveSource();
 
     // Active routing path. Physical address of the active source but not all the time, such as
     // when the new active source does not claim itself to be one. Note that we don't keep
@@ -434,10 +437,14 @@
         return true;
     }
 
+    // Audio System device with no Playback device type
+    // needs to refactor this function if it's also a switch
     protected boolean handleRoutingChange(HdmiCecMessage message) {
         return false;
     }
 
+    // Audio System device with no Playback device type
+    // needs to refactor this function if it's also a switch
     protected boolean handleRoutingInformation(HdmiCecMessage message) {
         return false;
     }
@@ -706,16 +713,18 @@
         return mDeviceType;
     }
 
-    @ServiceThreadOnly
+    @GuardedBy("mLock")
     HdmiDeviceInfo getDeviceInfo() {
-        assertRunOnServiceThread();
-        return mDeviceInfo;
+        synchronized (mLock) {
+            return mDeviceInfo;
+        }
     }
 
-    @ServiceThreadOnly
+    @GuardedBy("mLock")
     void setDeviceInfo(HdmiDeviceInfo info) {
-        assertRunOnServiceThread();
-        mDeviceInfo = info;
+        synchronized (mLock) {
+            mDeviceInfo = info;
+        }
     }
 
     // Returns true if the logical address is same as the argument.
@@ -855,9 +864,7 @@
     }
 
     ActiveSource getActiveSource() {
-        synchronized (mLock) {
-            return mActiveSource;
-        }
+        return mService.getActiveSource();
     }
 
     void setActiveSource(ActiveSource newActive) {
@@ -869,10 +876,7 @@
     }
 
     void setActiveSource(int logicalAddress, int physicalAddress) {
-        synchronized (mLock) {
-            mActiveSource.logicalAddress = logicalAddress;
-            mActiveSource.physicalAddress = physicalAddress;
-        }
+        mService.setActiveSource(logicalAddress, physicalAddress);
         mService.setLastInputForMhl(Constants.INVALID_PORT_ID);
     }
 
@@ -910,6 +914,11 @@
         setActivePath(mService.portIdToPath(portId));
     }
 
+    // Returns the id of the port that the target device is connected to.
+    int getPortId(int physicalAddress) {
+        return mService.pathToPortId(physicalAddress);
+    }
+
     @ServiceThreadOnly
     HdmiCecMessageCache getCecMessageCache() {
         assertRunOnServiceThread();
@@ -1017,6 +1026,19 @@
         return Constants.ADDR_INVALID;
     }
 
+    @ServiceThreadOnly
+    void invokeCallback(IHdmiControlCallback callback, int result) {
+        assertRunOnServiceThread();
+        if (callback == null) {
+            return;
+        }
+        try {
+            callback.onComplete(result);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Invoking callback failed:" + e);
+        }
+    }
+
     void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildUserControlPressed(mAddress, targetAddress, cecKeycode));
@@ -1030,7 +1052,30 @@
         pw.println("mAddress: " + mAddress);
         pw.println("mPreferredAddress: " + mPreferredAddress);
         pw.println("mDeviceInfo: " + mDeviceInfo);
-        pw.println("mActiveSource: " + mActiveSource);
+        pw.println("mActiveSource: " + getActiveSource());
         pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
     }
+
+    /** Calculates the physical address for {@code activePortId}.
+     *
+     * <p>This method assumes current device physical address is valid.
+     * <p>If the current device is already the leaf of the whole CEC system
+     * and can't have devices under it, will return its own physical address.
+     *
+     * @param activePortId is the local active port Id
+     * @return the calculated physical address of the port
+     */
+    protected int getActivePathOnSwitchFromActivePortId(@LocalActivePort int activePortId) {
+        int myPhysicalAddress = mService.getPhysicalAddress();
+        int finalMask = activePortId << 8;
+        int mask;
+        for (mask = 0x0F00; mask > 0x000F;  mask >>= 4) {
+            if ((myPhysicalAddress & mask) == 0)  {
+                break;
+            } else {
+                finalMask >>= 4;
+            }
+        }
+        return finalMask | myPhysicalAddress;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 3845954..63214ed 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -20,22 +20,50 @@
 import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
 
 import android.annotation.Nullable;
+import android.content.Intent;
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.media.tv.TvContract;
 import android.os.SystemProperties;
+import android.provider.Settings.Global;
+import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.Constants.AudioCodec;
+import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+import com.android.server.hdmi.HdmiUtils.CodecSad;
+import com.android.server.hdmi.HdmiUtils.DeviceConfig;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
 
 /**
  * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
  * system.
  */
-public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
+public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
 
     private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
 
@@ -54,12 +82,183 @@
     // AVR as audio receiver.
     @ServiceThreadOnly private boolean mArcEstablished = false;
 
+    // If the current device uses TvInput for ARC. We assume all other inputs also use TvInput
+    // when ARC is using TvInput.
+    private boolean mArcIntentUsed = SystemProperties
+            .get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, "0").contains("tvinput");
+
+    // Keeps the mapping (HDMI port ID to TV input URI) to keep track of the TV inputs ready to
+    // accept input switching request from HDMI devices. Requests for which the corresponding
+    // input ID is not yet registered by TV input framework need to be buffered for delayed
+    // processing.
+    private final HashMap<Integer, String> mTvInputs = new HashMap<>();
+
+    // Copy of mDeviceInfos to guarantee thread-safety.
+    @GuardedBy("mLock")
+    private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
+
+    // Map-like container of all cec devices.
+    // device id is used as key of container.
+    private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
+
     protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
-        mSystemAudioControlFeatureEnabled = true;
-        // TODO(amyjojo) make System Audio Control controllable by users
-        /*mSystemAudioControlFeatureEnabled =
-        mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+        mRoutingControlFeatureEnabled =
+            mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
+        mSystemAudioControlFeatureEnabled =
+            mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+        // TODO(amyjojo): Maintain a portId to TvinputId map.
+        mTvInputs.put(2, "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5");
+        mTvInputs.put(4, "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6");
+        mTvInputs.put(1, "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7");
+    }
+
+    private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
+
+    /**
+     * Called when a device is newly added or a new device is detected or
+     * an existing device is updated.
+     *
+     * @param info device info of a new device.
+     */
+    @ServiceThreadOnly
+    final void addCecDevice(HdmiDeviceInfo info) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo old = addDeviceInfo(info);
+        if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
+            // The addition of the device itself should not be notified.
+            // Note that different logical address could still be the same local device.
+            return;
+        }
+        if (old == null) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        } else if (!old.equals(info)) {
+            invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        }
+    }
+
+    /**
+     * Called when a device is removed or removal of device is detected.
+     *
+     * @param address a logical address of a device to be removed
+     */
+    @ServiceThreadOnly
+    final void removeCecDevice(int address) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
+
+        mCecMessageCache.flushMessagesFrom(address);
+        invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+    }
+
+    /**
+     * Called when a device is updated.
+     *
+     * @param info device info of the updating device.
+     */
+    @ServiceThreadOnly
+    final void updateCecDevice(HdmiDeviceInfo info) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo old = addDeviceInfo(info);
+
+        if (old == null) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        } else if (!old.equals(info)) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+        }
+    }
+
+    /**
+    * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
+     * logical address as new device info's.
+     *
+     * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
+     * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
+     *         that has the same logical address as new one has.
+     */
+    @ServiceThreadOnly
+    @VisibleForTesting
+    protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
+        if (oldDeviceInfo != null) {
+            removeDeviceInfo(deviceInfo.getId());
+        }
+        mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
+        updateSafeDeviceInfoList();
+        return oldDeviceInfo;
+    }
+
+    /**
+     * Remove a device info corresponding to the given {@code logicalAddress}.
+     * It returns removed {@link HdmiDeviceInfo} if exists.
+     *
+     * @param id id of device to be removed
+     * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
+     */
+    @ServiceThreadOnly
+    private HdmiDeviceInfo removeDeviceInfo(int id) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
+        if (deviceInfo != null) {
+            mDeviceInfos.remove(id);
+        }
+        updateSafeDeviceInfoList();
+        return deviceInfo;
+    }
+
+    /**
+     * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
+     *
+     * @param logicalAddress logical address of the device to be retrieved
+     * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
+     *         Returns null if no logical address matched
+     */
+    @ServiceThreadOnly
+    HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
+        assertRunOnServiceThread();
+        return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
+    }
+
+    @ServiceThreadOnly
+    private void updateSafeDeviceInfoList() {
+        assertRunOnServiceThread();
+        List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
+        synchronized (mLock) {
+            mSafeAllDeviceInfos = copiedDevices;
+        }
+    }
+
+    @GuardedBy("mLock")
+    List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
+        ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
+        for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+            infoList.add(info);
+        }
+        return infoList;
+    }
+
+    private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
+        mService.invokeDeviceEventListeners(info, status);
+    }
+
+    @Override
+    @ServiceThreadOnly
+    void onHotplug(int portId, boolean connected) {
+        assertRunOnServiceThread();
+        if (connected) {
+            mService.wakeUp();
+        }
+        if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            mCecMessageCache.flushAll();
+        } else {
+            if (connected) {
+                launchDeviceDiscovery();
+            } else {
+                // TODO(amyjojo): remove device from mDeviceInfo
+            }
+        }
     }
 
     @Override
@@ -69,7 +268,7 @@
         mTvSystemAudioModeSupport = false;
         // Record the last state of System Audio Control before going to standby
         synchronized (mLock) {
-            SystemProperties.set(
+            mService.writeStringSetting(
                     Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
                     mSystemAudioActivated ? "true" : "false");
         }
@@ -80,6 +279,10 @@
     @ServiceThreadOnly
     protected void onAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
+        if (reason == mService.INITIATED_BY_ENABLE_CEC) {
+            mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(),
+                    getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST);
+        }
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                         mAddress, mService.getPhysicalAddress(), mDeviceType));
@@ -92,49 +295,119 @@
         boolean lastSystemAudioControlStatus =
                 SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
         systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+        clearDeviceInfoList();
+        launchDeviceDiscovery();
         startQueuedActions();
     }
 
+    @Override
+    protected int findKeyReceiverAddress() {
+        if (getActiveSource().isValid()) {
+            return getActiveSource().logicalAddress;
+        }
+        return Constants.ADDR_INVALID;
+    }
+
     @VisibleForTesting
     protected void systemAudioControlOnPowerOn(
             int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
         if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
                 || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
-                        && lastSystemAudioControlStatus)) {
+                && lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) {
             addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
         }
     }
 
-    @ServiceThreadOnly
-    protected boolean handleActiveSource(HdmiCecMessage message) {
-        assertRunOnServiceThread();
-        int logicalAddress = message.getSource();
-        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
-        ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
-        if (!mActiveSource.equals(activeSource)) {
-            setActiveSource(activeSource);
-        }
-        return true;
-    }
-
     @Override
     @ServiceThreadOnly
     protected int getPreferredAddress() {
         assertRunOnServiceThread();
         return SystemProperties.getInt(
-                Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, Constants.ADDR_UNREGISTERED);
+            Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, Constants.ADDR_UNREGISTERED);
     }
 
     @Override
     @ServiceThreadOnly
     protected void setPreferredAddress(int addr) {
         assertRunOnServiceThread();
-        SystemProperties.set(
+        mService.writeStringSetting(
                 Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr));
     }
 
     @Override
     @ServiceThreadOnly
+    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        int path = HdmiUtils.twoBytesToInt(message.getParams());
+        int address = message.getSource();
+        int type = message.getParams()[2];
+
+        // Ignore if [Device Discovery Action] is going on.
+        if (hasAction(DeviceDiscoveryAction.class)) {
+            Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
+            return true;
+        }
+
+        // Update the device info with TIF, note that the same device info could have added in
+        // device discovery and we do not want to override it with default OSD name. Therefore we
+        // need the following check to skip redundant device info updating.
+        HdmiDeviceInfo oldDevice = getCecDeviceInfo(address);
+        if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
+            addCecDevice(new HdmiDeviceInfo(
+                    address, path, mService.pathToPortId(path), type,
+                    Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address)));
+            // if we are adding a new device info, send out a give osd name command
+            // to update the name of the device in TIF
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
+            return true;
+        }
+
+        Slog.w(TAG, "Device info exists. Not updating on Physical Address.");
+        return true;
+    }
+
+    @Override
+    protected boolean handleReportPowerStatus(HdmiCecMessage command) {
+        int newStatus = command.getParams()[0] & 0xFF;
+        updateDevicePowerStatus(command.getSource(), newStatus);
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSetOsdName(HdmiCecMessage message) {
+        int source = message.getSource();
+        String osdName;
+        HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
+        // If the device is not in device list, ignore it.
+        if (deviceInfo == null) {
+            Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
+            return true;
+        }
+        try {
+            osdName = new String(message.getParams(), "US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
+            return true;
+        }
+
+        if (deviceInfo.getDisplayName().equals(osdName)) {
+            Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
+            return true;
+        }
+
+        Slog.d(TAG, "Updating device OSD name from "
+                + deviceInfo.getDisplayName()
+                + " to " + osdName);
+        updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
+                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
+                deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
     protected boolean handleReportAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // TODO(amyjojo): implement report audio status handler
@@ -173,8 +446,11 @@
     @ServiceThreadOnly
     protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
-        reportAudioStatus(message);
+        if (isSystemAudioControlFeatureEnabled()) {
+            reportAudioStatus(message.getSource());
+        } else {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+        }
         return true;
     }
 
@@ -192,7 +468,8 @@
     @ServiceThreadOnly
     protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
+        removeAction(ArcInitiationActionFromAvr.class);
+        if (!mService.readBooleanSetting(Constants.PROPERTY_ARC_SUPPORT, true)) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
         } else if (!isDirectConnectToTv()) {
             HdmiLogger.debug("AVR device is not directly connected with TV");
@@ -213,6 +490,7 @@
             HdmiLogger.debug("ARC is not established between TV and AVR device");
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
         } else {
+            removeAction(ArcTerminationActionFromAvr.class);
             addAndStartAction(new ArcTerminationActionFromAvr(this));
         }
         return true;
@@ -230,13 +508,35 @@
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
             return true;
         }
-        AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo();
-        if (deviceInfo == null) {
-            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
-            return true;
+
+        List<DeviceConfig> config = null;
+        File file = new File(SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH);
+        if (file.exists()) {
+            try {
+                InputStream in = new FileInputStream(file);
+                config = HdmiUtils.ShortAudioDescriptorXmlParser.parse(in);
+                in.close();
+            } catch (IOException e) {
+                Slog.e(TAG, "Error reading file: " + file, e);
+            } catch (XmlPullParserException e) {
+                Slog.e(TAG, "Unable to parse file: " + file, e);
+            }
         }
+
         @AudioCodec int[] audioFormatCodes = parseAudioFormatCodes(message.getParams());
-        byte[] sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes);
+        byte[] sadBytes;
+        if (config != null && config.size() > 0) {
+            sadBytes = getSupportedShortAudioDescriptorsFromConfig(config, audioFormatCodes);
+        } else {
+            AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo();
+            if (deviceInfo == null) {
+                mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+                return true;
+            }
+
+            sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes);
+        }
+
         if (sadBytes.length == 0) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
         } else {
@@ -249,14 +549,127 @@
 
     private byte[] getSupportedShortAudioDescriptors(
             AudioDeviceInfo deviceInfo, @AudioCodec int[] audioFormatCodes) {
-        // TODO(b/80297701) implement
-        return new byte[] {};
+        ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
+        for (@AudioCodec int audioFormatCode : audioFormatCodes) {
+            byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioFormatCode);
+            if (sad != null) {
+                if (sad.length == 3) {
+
+                    sads.add(sad);
+                } else {
+                    HdmiLogger.warning(
+                            "Dropping Short Audio Descriptor with length %d for requested codec %x",
+                            sad.length, audioFormatCode);
+                }
+            }
+        }
+        return getShortAudioDescriptorBytes(sads);
+    }
+
+    private byte[] getSupportedShortAudioDescriptorsFromConfig(
+            List<DeviceConfig> deviceConfig, @AudioCodec int[] audioFormatCodes) {
+        DeviceConfig deviceConfigToUse = null;
+        for (DeviceConfig device : deviceConfig) {
+            // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
+            if (device.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) {
+                deviceConfigToUse = device;
+                break;
+            }
+        }
+        if (deviceConfigToUse == null) {
+            // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
+            Slog.w(TAG, "sadConfig.xml does not have required device info for "
+                        + "VX_AUDIO_DEVICE_IN_HDMI_ARC");
+            return new byte[0];
+        }
+        HashMap<Integer, byte[]> map = new HashMap<>();
+        ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
+        for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) {
+            map.put(codecSad.audioCodec, codecSad.sad);
+        }
+        for (int i = 0; i < audioFormatCodes.length; i++) {
+            if (map.containsKey(audioFormatCodes[i])) {
+                byte[] sad = map.get(audioFormatCodes[i]);
+                if (sad != null && sad.length == 3) {
+                    sads.add(sad);
+                }
+            }
+        }
+        return getShortAudioDescriptorBytes(sads);
+    }
+
+    private byte[] getShortAudioDescriptorBytes(ArrayList<byte[]> sads) {
+        // Short Audio Descriptors are always 3 bytes long.
+        byte[] bytes = new byte[sads.size() * 3];
+        int index = 0;
+        for (byte[] sad : sads) {
+            System.arraycopy(sad, 0, bytes, index, 3);
+            index += 3;
+        }
+        return bytes;
+    }
+
+    /**
+     * Returns a 3 byte short audio descriptor as described in CEC 1.4 table 29 or null if the
+     * audioFormatCode is not supported.
+     */
+    @Nullable
+    private byte[] getSupportedShortAudioDescriptor(
+            AudioDeviceInfo deviceInfo, @AudioCodec int audioFormatCode) {
+        switch (audioFormatCode) {
+            case Constants.AUDIO_CODEC_NONE: {
+                return null;
+            }
+            case Constants.AUDIO_CODEC_LPCM: {
+                return getLpcmShortAudioDescriptor(deviceInfo);
+            }
+            // TODO(b/80297701): implement the rest of the codecs
+            case Constants.AUDIO_CODEC_DD:
+            case Constants.AUDIO_CODEC_MPEG1:
+            case Constants.AUDIO_CODEC_MP3:
+            case Constants.AUDIO_CODEC_MPEG2:
+            case Constants.AUDIO_CODEC_AAC:
+            case Constants.AUDIO_CODEC_DTS:
+            case Constants.AUDIO_CODEC_ATRAC:
+            case Constants.AUDIO_CODEC_ONEBITAUDIO:
+            case Constants.AUDIO_CODEC_DDP:
+            case Constants.AUDIO_CODEC_DTSHD:
+            case Constants.AUDIO_CODEC_TRUEHD:
+            case Constants.AUDIO_CODEC_DST:
+            case Constants.AUDIO_CODEC_WMAPRO:
+            default: {
+                return null;
+            }
+        }
+    }
+
+    @Nullable
+    private byte[] getLpcmShortAudioDescriptor(AudioDeviceInfo deviceInfo) {
+        // TODO(b/80297701): implement
+        return null;
     }
 
     @Nullable
     private AudioDeviceInfo getSystemAudioDeviceInfo() {
-        // TODO(b/80297701) implement
-        // Get the audio device used for system audio mode.
+        AudioManager audioManager = mService.getContext().getSystemService(AudioManager.class);
+        if (audioManager == null) {
+            HdmiLogger.error(
+                    "Error getting system audio device because AudioManager not available.");
+            return null;
+        }
+        AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+        HdmiLogger.debug("Found %d audio input devices", devices.length);
+        for (AudioDeviceInfo device : devices) {
+            HdmiLogger.debug("%s at port %s", device.getProductName(), device.getPort());
+            HdmiLogger.debug("Supported encodings are %s",
+                    Arrays.stream(device.getEncodings()).mapToObj(
+                            AudioFormat::toLogFriendlyEncoding
+                    ).collect(Collectors.joining(", ")));
+            // TODO(b/80297701) use the actual device type that system audio mode is connected to.
+            if (device.getType() == AudioDeviceInfo.TYPE_HDMI_ARC) {
+                return device;
+            }
+        }
         return null;
     }
 
@@ -266,7 +679,7 @@
         for (int i = 0; i < params.length; i++) {
             byte val = params[i];
             audioFormatCodes[i] =
-                    val >= 1 && val <= Constants.AUDIO_CODEC_MAX ? val : Constants.AUDIO_CODEC_NONE;
+                val >= 1 && val <= Constants.AUDIO_CODEC_MAX ? val : Constants.AUDIO_CODEC_NONE;
         }
         return audioFormatCodes;
     }
@@ -276,7 +689,23 @@
     protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
         assertRunOnServiceThread();
         boolean systemAudioStatusOn = message.getParams().length != 0;
-        if (!setSystemAudioMode(systemAudioStatusOn)) {
+        // Check if the request comes from a non-TV device.
+        // Need to check if TV supports System Audio Control
+        // if non-TV device tries to turn on the feature
+        if (message.getSource() != Constants.ADDR_TV) {
+            if (systemAudioStatusOn) {
+                handleSystemAudioModeOnFromNonTvDevice(message);
+                return true;
+            }
+        } else {
+            // If TV request the feature on
+            // cache TV supporting System Audio Control
+            // until Audio System loses its physical address.
+            setTvSystemAudioModeSupport(true);
+        }
+        // If TV or Audio System does not support the feature,
+        // will send abort command.
+        if (!checkSupportAndSetSystemAudioMode(systemAudioStatusOn)) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
             return true;
         }
@@ -291,7 +720,8 @@
     @ServiceThreadOnly
     protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+        if (!checkSupportAndSetSystemAudioMode(
+                HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
         }
         return true;
@@ -301,7 +731,8 @@
     @ServiceThreadOnly
     protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+        if (!checkSupportAndSetSystemAudioMode(
+                HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
         }
         return true;
@@ -333,23 +764,35 @@
     private void notifyArcStatusToAudioService(boolean enabled) {
         // Note that we don't set any name to ARC.
         mService.getAudioManager()
-                .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", "");
+            .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", "");
     }
 
-    private void reportAudioStatus(HdmiCecMessage message) {
+    void reportAudioStatus(int source) {
         assertRunOnServiceThread();
 
         int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
         boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
         int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        int minVolume = mService.getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC);
         int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+        HdmiLogger.debug("Reporting volume %i (%i-%i) as CEC volume %i", volume,
+                minVolume, maxVolume, scaledVolume);
 
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildReportAudioStatus(
-                        mAddress, message.getSource(), scaledVolume, mute));
+                        mAddress, source, scaledVolume, mute));
     }
 
-    protected boolean setSystemAudioMode(boolean newSystemAudioMode) {
+    /**
+     * Method to check if device support System Audio Control. If so, wake up device if necessary.
+     *
+     * <p> then call {@link #setSystemAudioMode(boolean)} to turn on or off System Audio Mode
+     * @param newSystemAudioMode turning feature on or off. True is on. False is off.
+     * @return true or false.
+     *
+     * <p>False when device does not support the feature. Otherwise returns true.
+     */
+    protected boolean checkSupportAndSetSystemAudioMode(boolean newSystemAudioMode) {
         if (!isSystemAudioControlFeatureEnabled()) {
             HdmiLogger.debug(
                     "Cannot turn "
@@ -361,15 +804,43 @@
         HdmiLogger.debug(
                 "System Audio Mode change[old:%b new:%b]",
                 mSystemAudioActivated, newSystemAudioMode);
-        // Wake up device if System Audio Control is turned on but device is still on standby
-        if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
+        // Wake up device if System Audio Control is turned on
+        if (newSystemAudioMode) {
             mService.wakeUp();
         }
+        setSystemAudioMode(newSystemAudioMode);
+        return true;
+    }
+
+    /**
+     * Real work to turn on or off System Audio Mode.
+     *
+     * Use {@link #checkSupportAndSetSystemAudioMode(boolean)}
+     * if trying to turn on or off the feature.
+     */
+    private void setSystemAudioMode(boolean newSystemAudioMode) {
         int targetPhysicalAddress = getActiveSource().physicalAddress;
-        if (newSystemAudioMode && !isPhysicalAddressMeOrBelow(targetPhysicalAddress)) {
+        int port = mService.pathToPortId(targetPhysicalAddress);
+        if (newSystemAudioMode && port >= 0) {
             switchToAudioInput();
         }
-        // TODO(b/80297700): Mute device when TV terminates the system audio control
+        // Mute device when feature is turned off and unmute device when feature is turned on.
+        // PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE is false when device never needs to be muted.
+        boolean currentMuteStatus =
+                mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
+        if (currentMuteStatus == newSystemAudioMode) {
+            if (mService.readBooleanSetting(
+                    Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
+                            || newSystemAudioMode) {
+                mService.getAudioManager()
+                        .adjustStreamVolume(
+                                AudioManager.STREAM_MUSIC,
+                                newSystemAudioMode
+                                        ? AudioManager.ADJUST_UNMUTE
+                                        : AudioManager.ADJUST_MUTE,
+                                0);
+            }
+        }
         updateAudioManagerForSystemAudio(newSystemAudioMode);
         synchronized (mLock) {
             if (mSystemAudioActivated != newSystemAudioMode) {
@@ -377,31 +848,19 @@
                 mService.announceSystemAudioModeChange(newSystemAudioMode);
             }
         }
-        return true;
-    }
-
-    /**
-     * Method to check if the target device belongs to the subtree of the current device or not.
-     *
-     * <p>Return true if it does or if the two devices share the same physical address.
-     *
-     * <p>This check assumes both device physical address and target address are valid.
-     *
-     * @param targetPhysicalAddress is the physical address of the target device
-     */
-    protected boolean isPhysicalAddressMeOrBelow(int targetPhysicalAddress) {
-        int myPhysicalAddress = mService.getPhysicalAddress();
-        int xor = targetPhysicalAddress ^ myPhysicalAddress;
-        // Return true if two addresses are the same
-        // or if they only differs for one byte, but not the first byte,
-        // and myPhysicalAddress is 0 after that byte
-        if (xor == 0
-                || ((xor & 0x0f00) == xor && (myPhysicalAddress & 0x0fff) == 0)
-                || ((xor & 0x00f0) == xor && (myPhysicalAddress & 0x00ff) == 0)
-                || ((xor & 0x000f) == xor && (myPhysicalAddress & 0x000f) == 0)) {
-            return true;
+        // Init arc whenever System Audio Mode is on
+        // Terminate arc when System Audio Mode is off
+        // Since some TVs don't request ARC on with System Audio Mode on request
+        if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
+                && isDirectConnectToTv()) {
+            if (newSystemAudioMode && !isArcEnabled()) {
+                removeAction(ArcInitiationActionFromAvr.class);
+                addAndStartAction(new ArcInitiationActionFromAvr(this));
+            } else if (!newSystemAudioMode && isArcEnabled()) {
+                removeAction(ArcTerminationActionFromAvr.class);
+                addAndStartAction(new ArcTerminationActionFromAvr(this));
+            }
         }
-        return false;
     }
 
     protected void switchToAudioInput() {
@@ -418,6 +877,13 @@
         HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
     }
 
+    void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
+        setSystemAudioControlFeatureEnabled(enabled);
+        if (enabled) {
+            addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+        }
+    }
+
     @ServiceThreadOnly
     void setSystemAudioControlFeatureEnabled(boolean enabled) {
         assertRunOnServiceThread();
@@ -426,6 +892,42 @@
         }
     }
 
+    @ServiceThreadOnly
+    void setRoutingControlFeatureEnables(boolean enabled) {
+        assertRunOnServiceThread();
+        synchronized (mLock) {
+            mRoutingControlFeatureEnabled = enabled;
+        }
+    }
+
+    @ServiceThreadOnly
+    void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        // TODO: validate port ID
+        if (portId == getLocalActivePort()) {
+            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+            return;
+        }
+        if (!mService.isControlEnabled()) {
+            setRoutingPort(portId);
+            setLocalActivePort(portId);
+            invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
+            return;
+        }
+        int oldPath = getRoutingPort() != Constants.CEC_SWITCH_HOME
+                ? mService.portIdToPath(getRoutingPort())
+                : getDeviceInfo().getPhysicalAddress();
+        int newPath = mService.portIdToPath(portId);
+        if (oldPath == newPath) {
+            return;
+        }
+        setRoutingPort(portId);
+        setLocalActivePort(portId);
+        HdmiCecMessage routingChange =
+                HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
+        mService.sendCecCommand(routingChange);
+    }
+
     boolean isSystemAudioControlFeatureEnabled() {
         synchronized (mLock) {
             return mSystemAudioControlFeatureEnabled;
@@ -445,7 +947,7 @@
             return;
         }
 
-        if (setSystemAudioMode(false)) {
+        if (checkSupportAndSetSystemAudioMode(false)) {
             // send <Set System Audio Mode> [“Off”]
             mService.sendCecCommand(
                     HdmiCecMessageBuilder.buildSetSystemAudioMode(
@@ -468,6 +970,7 @@
      * <p>The result of the query may be cached until Audio device type is put in standby or loses
      * its physical address.
      */
+    // TODO(amyjojo): making mTvSystemAudioModeSupport null originally and fix the logic.
     void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
         if (!mTvSystemAudioModeSupport) {
             addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
@@ -476,6 +979,35 @@
         }
     }
 
+    /**
+     * Handler of System Audio Mode Request on from non TV device
+     */
+    void handleSystemAudioModeOnFromNonTvDevice(HdmiCecMessage message) {
+        if (!isSystemAudioControlFeatureEnabled()) {
+            HdmiLogger.debug(
+                    "Cannot turn on" + "system audio mode "
+                            + "because the System Audio Control feature is disabled.");
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return;
+        }
+        // Wake up device
+        mService.wakeUp();
+        // Check if TV supports System Audio Control.
+        // Handle broadcasting setSystemAudioMode on or aborting message on callback.
+        queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
+            public void onResult(boolean supported) {
+                if (supported) {
+                    setSystemAudioMode(true);
+                    mService.sendCecCommand(
+                            HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                                    mAddress, Constants.ADDR_BROADCAST, true));
+                } else {
+                    mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+                }
+            }
+        });
+    }
+
     void setTvSystemAudioModeSupport(boolean supported) {
         mTvSystemAudioModeSupport = supported;
     }
@@ -486,4 +1018,195 @@
             return mArcEstablished;
         }
     }
+
+    @Override
+    protected void switchInputOnReceivingNewActivePath(int physicalAddress) {
+        int port = mService.pathToPortId(physicalAddress);
+        if (isSystemAudioActivated() && port < 0) {
+            // If system audio mode is on and the new active source is not under the current device,
+            // Will switch to ARC input.
+            // TODO(b/115637145): handle system aduio without ARC
+            routeToInputFromPortId(Constants.CEC_SWITCH_ARC);
+        } else if (mIsSwitchDevice && port >= 0) {
+            // If current device is a switch and the new active source is under it,
+            // will switch to the corresponding active path.
+            routeToInputFromPortId(port);
+        }
+    }
+
+    protected void routeToInputFromPortId(int portId) {
+        if (!isRoutingControlFeatureEnabled()) {
+            HdmiLogger.debug("Routing Control Feature is not enabled.");
+            return;
+        }
+        if (mArcIntentUsed) {
+            routeToTvInputFromPortId(portId);
+        } else {
+            // TODO(): implement input switching for devices not using TvInput.
+        }
+    }
+
+    protected void routeToTvInputFromPortId(int portId) {
+        if (portId < 0 || portId >= Constants.CEC_SWITCH_PORT_MAX) {
+            HdmiLogger.debug("Invalid port number for Tv Input switching.");
+            return;
+        }
+        // Wake up if the current device if ready to route.
+        mService.wakeUp();
+        if (portId == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
+            switchToHomeTvInput();
+        } else if (portId == Constants.CEC_SWITCH_ARC) {
+            switchToTvInput(SystemProperties.get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT));
+            setLocalActivePort(portId);
+            return;
+        } else {
+            String uri = mTvInputs.get(portId);
+            if (uri != null) {
+                switchToTvInput(mTvInputs.get(portId));
+            } else {
+                HdmiLogger.debug("Port number does not match any Tv Input.");
+                return;
+            }
+        }
+
+        setLocalActivePort(portId);
+        setRoutingPort(portId);
+    }
+
+    // For device to switch to specific TvInput with corresponding URI.
+    private void switchToTvInput(String uri) {
+        mService.getContext().startActivity(new Intent(Intent.ACTION_VIEW,
+                TvContract.buildChannelUriForPassthroughInput(uri))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    // For device using TvInput to switch to Home.
+    private void switchToHomeTvInput() {
+        Intent activityIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+                        | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                        | Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+        mService.getContext().startActivity(activityIntent);
+    }
+
+    @Override
+    protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
+        int port = mService.pathToPortId(physicalAddress);
+        // Routing change or information sent from switches under the current device can be ignored.
+        if (port > 0) {
+            return;
+        }
+        // When other switches route to some other devices not under the current device,
+        // check system audio mode status and do ARC switch if needed.
+        if (port < 0 && isSystemAudioActivated()) {
+            handleRoutingChangeAndInformationForSystemAudio();
+            return;
+        }
+        // When other switches route to the current device
+        // and the current device is also a switch.
+        if (port == 0) {
+            handleRoutingChangeAndInformationForSwitch(message);
+        }
+    }
+
+    // Handle the system audio(ARC) part of the logic on receiving routing change or information.
+    private void handleRoutingChangeAndInformationForSystemAudio() {
+        // TODO(b/115637145): handle system aduio without ARC
+        routeToInputFromPortId(Constants.CEC_SWITCH_ARC);
+    }
+
+    // Handle the routing control part of the logic on receiving routing change or information.
+    private void handleRoutingChangeAndInformationForSwitch(HdmiCecMessage message) {
+        if (getRoutingPort() == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
+            routeToInputFromPortId(Constants.CEC_SWITCH_HOME);
+            mService.setAndBroadcastActiveSourceFromOneDeviceType(
+                    message.getSource(), mService.getPhysicalAddress());
+            return;
+        }
+
+        int routingInformationPath = mService.portIdToPath(getRoutingPort());
+        // If current device is already the leaf of the whole HDMI system, will do nothing.
+        if (routingInformationPath == mService.getPhysicalAddress()) {
+            HdmiLogger.debug("Current device can't assign valid physical address"
+                    + "to devices under it any more. "
+                    + "It's physical address is " + routingInformationPath);
+            return;
+        }
+        // Otherwise will switch to the current active port and broadcast routing information.
+        mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingInformation(
+                mAddress, routingInformationPath));
+        routeToInputFromPortId(getRoutingPort());
+    }
+
+    protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+        HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
+        if (info == null) {
+            Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
+            return;
+        }
+
+        if (info.getDevicePowerStatus() == newPowerStatus) {
+            return;
+        }
+
+        HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
+        // addDeviceInfo replaces old device info with new one if exists.
+        addDeviceInfo(newInfo);
+
+        invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+    }
+
+    @ServiceThreadOnly
+    private void launchDeviceDiscovery() {
+        assertRunOnServiceThread();
+        if (hasAction(DeviceDiscoveryAction.class)) {
+            Slog.i(TAG, "Device Discovery Action is in progress. Restarting.");
+            removeAction(DeviceDiscoveryAction.class);
+        }
+        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
+                new DeviceDiscoveryCallback() {
+                    @Override
+                    public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
+                        for (HdmiDeviceInfo info : deviceInfos) {
+                            addCecDevice(info);
+                        }
+                    }
+                });
+        addAndStartAction(action);
+    }
+
+    // Clear all device info.
+    @ServiceThreadOnly
+    private void clearDeviceInfoList() {
+        assertRunOnServiceThread();
+        for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
+            if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
+                continue;
+            }
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+        }
+        mDeviceInfos.clear();
+        updateSafeDeviceInfoList();
+    }
+
+    @Override
+    protected void dump(IndentingPrintWriter pw) {
+        pw.println("HdmiCecLocalDeviceAudioSystem:");
+        pw.increaseIndent();
+        pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
+        pw.println("isRoutingFeatureEnabled " + isRoutingControlFeatureEnabled());
+        pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
+        pw.println("mTvSystemAudioModeSupport: " + mTvSystemAudioModeSupport);
+        pw.println("mArcEstablished: " + mArcEstablished);
+        pw.println("mArcIntentUsed: " + mArcIntentUsed);
+        pw.println("mRoutingPort: " + getRoutingPort());
+        pw.println("mLocalActivePort: " + getLocalActivePort());
+        HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs);
+        HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
+        pw.decreaseIndent();
+        super.dump(pw);
+    }
+
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d45b00b..7a0c279 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,11 +21,11 @@
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.provider.Settings.Global;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.LocalePicker.LocaleInfo;
 import com.android.internal.util.IndentingPrintWriter;
@@ -35,12 +35,10 @@
 import java.util.List;
 import java.util.Locale;
 
-import java.util.List;
-
 /**
  * Represent a logical device of type Playback residing in Android system.
  */
-final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
     private static final String TAG = "HdmiCecLocalDevicePlayback";
 
     private static final boolean WAKE_ON_HOTPLUG =
@@ -49,8 +47,6 @@
     private static final boolean SET_MENU_LANGUAGE =
             SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false);
 
-    private boolean mIsActiveSource = false;
-
     // Used to keep the device awake while it is the active source. For devices that
     // cannot wake up via CEC commands, this address the inconvenience of having to
     // turn them on. True by default, and can be disabled (i.e. device can go to sleep
@@ -62,6 +58,11 @@
     // If true, turn off TV upon standby. False by default.
     private boolean mAutoTvOff;
 
+    // Local active port number used for Routing Control.
+    // Default 0 means HOME is the current active path. Temp solution only.
+    // TODO(amyjojo): adding system constants for input ports to TIF mapping.
+    private int mLocalActivePath = 0;
+
     HdmiCecLocalDevicePlayback(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
 
@@ -76,6 +77,10 @@
     @ServiceThreadOnly
     protected void onAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
+        if (reason == mService.INITIATED_BY_ENABLE_CEC) {
+            mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(),
+                    getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST);
+        }
         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                 mAddress, mService.getPhysicalAddress(), mDeviceType));
         mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
@@ -95,30 +100,11 @@
     @ServiceThreadOnly
     protected void setPreferredAddress(int addr) {
         assertRunOnServiceThread();
-        SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
+        mService.writeStringSetting(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
                 String.valueOf(addr));
     }
 
     @ServiceThreadOnly
-    void oneTouchPlay(IHdmiControlCallback callback) {
-        assertRunOnServiceThread();
-        List<OneTouchPlayAction> actions = getActions(OneTouchPlayAction.class);
-        if (!actions.isEmpty()) {
-            Slog.i(TAG, "oneTouchPlay already in progress");
-            actions.get(0).addCallback(callback);
-            return;
-        }
-        OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
-                callback);
-        if (action == null) {
-            Slog.w(TAG, "Cannot initiate oneTouchPlay");
-            invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
-            return;
-        }
-        addAndStartAction(action);
-    }
-
-    @ServiceThreadOnly
     void queryDisplayStatus(IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class);
@@ -137,16 +123,6 @@
         addAndStartAction(action);
     }
 
-    @ServiceThreadOnly
-    private void invokeCallback(IHdmiControlCallback callback, int result) {
-        assertRunOnServiceThread();
-        try {
-            callback.onComplete(result);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Invoking callback failed:" + e);
-        }
-    }
-
     @Override
     @ServiceThreadOnly
     void onHotplug(int portId, boolean connected) {
@@ -189,7 +165,8 @@
     }
 
     @ServiceThreadOnly
-    void setActiveSource(boolean on) {
+    @VisibleForTesting
+    void setIsActiveSource(boolean on) {
         assertRunOnServiceThread();
         mIsActiveSource = on;
         if (on) {
@@ -227,21 +204,6 @@
         return !getWakeLock().isHeld();
     }
 
-    @Override
-    @ServiceThreadOnly
-    protected boolean handleActiveSource(HdmiCecMessage message) {
-        assertRunOnServiceThread();
-        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
-        mayResetActiveSource(physicalAddress);
-        return true;  // Broadcast message.
-    }
-
-    private void mayResetActiveSource(int physicalAddress) {
-        if (physicalAddress != mService.getPhysicalAddress()) {
-            setActiveSource(false);
-        }
-    }
-
     @ServiceThreadOnly
     protected boolean handleUserControlPressed(HdmiCecMessage message) {
         assertRunOnServiceThread();
@@ -250,42 +212,7 @@
     }
 
     @Override
-    @ServiceThreadOnly
-    protected boolean handleSetStreamPath(HdmiCecMessage message) {
-        assertRunOnServiceThread();
-        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
-        maySetActiveSource(physicalAddress);
-        maySendActiveSource(message.getSource());
-        wakeUpIfActiveSource();
-        return true;  // Broadcast message.
-    }
-
-    // Samsung model we tested sends <Routing Change> and <Request Active Source>
-    // in a row, and then changes the input to the internal source if there is no
-    // <Active Source> in response. To handle this, we'll set ActiveSource aggressively.
-    @Override
-    @ServiceThreadOnly
-    protected boolean handleRoutingChange(HdmiCecMessage message) {
-        assertRunOnServiceThread();
-        int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
-        maySetActiveSource(newPath);
-        return true;  // Broadcast message.
-    }
-
-    @Override
-    @ServiceThreadOnly
-    protected boolean handleRoutingInformation(HdmiCecMessage message) {
-        assertRunOnServiceThread();
-        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
-        maySetActiveSource(physicalAddress);
-        return true;  // Broadcast message.
-    }
-
-    private void maySetActiveSource(int physicalAddress) {
-        setActiveSource(physicalAddress == mService.getPhysicalAddress());
-    }
-
-    private void wakeUpIfActiveSource() {
+    protected void wakeUpIfActiveSource() {
         if (!mIsActiveSource) {
             return;
         }
@@ -296,7 +223,8 @@
         }
     }
 
-    private void maySendActiveSource(int dest) {
+    @Override
+    protected void maySendActiveSource(int dest) {
         if (mIsActiveSource) {
             mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
                     mAddress, mService.getPhysicalAddress()));
@@ -306,14 +234,6 @@
         }
     }
 
-    @Override
-    @ServiceThreadOnly
-    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
-        assertRunOnServiceThread();
-        maySendActiveSource(message.getSource());
-        return true;  // Broadcast message.
-    }
-
     @ServiceThreadOnly
     protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
         assertRunOnServiceThread();
@@ -361,16 +281,6 @@
 
     @Override
     @ServiceThreadOnly
-    protected void sendStandby(int deviceId) {
-        assertRunOnServiceThread();
-
-        // Playback device can send <Standby> to TV only. Ignore the parameter.
-        int targetAddress = Constants.ADDR_TV;
-        mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress));
-    }
-
-    @Override
-    @ServiceThreadOnly
     protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
         super.disableDevice(initiatedByCec, callback);
 
@@ -379,10 +289,20 @@
             mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
                     mAddress, mService.getPhysicalAddress()));
         }
-        setActiveSource(false);
+        setIsActiveSource(false);
         checkIfPendingActionsCleared();
     }
 
+    private void routeToPort(int portId) {
+        // TODO(AMYJOJO): route to specific input of the port
+        mLocalActivePath = portId;
+    }
+
+    @VisibleForTesting
+    protected int getLocalActivePath() {
+        return mLocalActivePath;
+    }
+
     @Override
     protected void dump(final IndentingPrintWriter pw) {
         super.dump(pw);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
new file mode 100644
index 0000000..ae008b4
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.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 com.android.server.hdmi;
+
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.Constants.LocalActivePort;
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+
+import java.util.List;
+
+/**
+ * Represent a logical source device residing in Android system.
+ */
+abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
+
+    private static final String TAG = "HdmiCecLocalDeviceSource";
+
+    // Indicate if current device is Active Source or not
+    @VisibleForTesting
+    protected boolean mIsActiveSource = false;
+
+    // Device has cec switch functionality or not.
+    // Default is false.
+    protected boolean mIsSwitchDevice = SystemProperties.getBoolean(
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+
+    // Routing port number used for Routing Control.
+    // This records the default routing port or the previous valid routing port.
+    // Default is HOME input.
+    // Note that we don't save active path here because for source device,
+    // new Active Source physical address might not match the active path
+    @GuardedBy("mLock")
+    @LocalActivePort
+    private int mRoutingPort = Constants.CEC_SWITCH_HOME;
+
+    // This records the current input of the device.
+    // When device is switched to ARC input, mRoutingPort does not record it
+    // since it's not an HDMI port used for Routing Control.
+    // mLocalActivePort will record whichever input we switch to to keep tracking on
+    // the current input status of the device.
+    // This can help prevent duplicate switching and provide status information.
+    @GuardedBy("mLock")
+    @LocalActivePort
+    protected int mLocalActivePort = Constants.CEC_SWITCH_HOME;
+
+    // Whether the Routing Coutrol feature is enabled or not. False by default.
+    @GuardedBy("mLock")
+    protected boolean mRoutingControlFeatureEnabled;
+
+    protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) {
+        super(service, deviceType);
+    }
+
+    @Override
+    @ServiceThreadOnly
+    void onHotplug(int portId, boolean connected) {
+        assertRunOnServiceThread();
+        if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            mCecMessageCache.flushAll();
+        }
+        // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
+        if (connected) {
+            mService.wakeUp();
+        }
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected void sendStandby(int deviceId) {
+        assertRunOnServiceThread();
+
+        // Send standby to TV only for now
+        int targetAddress = Constants.ADDR_TV;
+        mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress));
+    }
+
+    @ServiceThreadOnly
+    void oneTouchPlay(IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        List<OneTouchPlayAction> actions = getActions(OneTouchPlayAction.class);
+        if (!actions.isEmpty()) {
+            Slog.i(TAG, "oneTouchPlay already in progress");
+            actions.get(0).addCallback(callback);
+            return;
+        }
+        OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
+                callback);
+        if (action == null) {
+            Slog.w(TAG, "Cannot initiate oneTouchPlay");
+            invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
+            return;
+        }
+        addAndStartAction(action);
+    }
+
+    @ServiceThreadOnly
+    protected boolean handleActiveSource(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        int logicalAddress = message.getSource();
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+        ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
+        if (!getActiveSource().equals(activeSource)) {
+            setActiveSource(activeSource);
+        }
+        setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
+        updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
+        if (isRoutingControlFeatureEnabled()) {
+            switchInputOnReceivingNewActivePath(physicalAddress);
+        }
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        maySendActiveSource(message.getSource());
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSetStreamPath(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+        // If current device is the target path, set to Active Source.
+        // If the path is under the current device, should switch
+        if (physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) {
+            setAndBroadcastActiveSource(message, physicalAddress);
+        }
+        switchInputOnReceivingNewActivePath(physicalAddress);
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleRoutingChange(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!isRoutingControlFeatureEnabled()) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
+        }
+        int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
+        // if the current device is a pure playback device
+        if (!mIsSwitchDevice
+                && newPath == mService.getPhysicalAddress()
+                && mService.isPlaybackDevice()) {
+            setAndBroadcastActiveSource(message, newPath);
+        }
+        handleRoutingChangeAndInformation(newPath, message);
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleRoutingInformation(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!isRoutingControlFeatureEnabled()) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
+        }
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+        // if the current device is a pure playback device
+        if (!mIsSwitchDevice
+                && physicalAddress == mService.getPhysicalAddress()
+                && mService.isPlaybackDevice()) {
+            setAndBroadcastActiveSource(message, physicalAddress);
+        }
+        handleRoutingChangeAndInformation(physicalAddress, message);
+        return true;
+    }
+
+    // Method to switch Input with the new Active Path.
+    // All the devices with Switch functionality should implement this.
+    protected void switchInputOnReceivingNewActivePath(int physicalAddress) {
+        // do nothing
+    }
+
+    // Source device with Switch functionality should implement this method.
+    // TODO(): decide which type will handle the routing when multi device type is supported
+    protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
+        // do nothing
+    }
+
+    // Update the power status of the devices connected to the current device.
+    // This only works if the current device is a switch and keeps tracking the device info
+    // of the device connected to it.
+    protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+        // do nothing
+    }
+
+    // Active source claiming needs to be handled in Service
+    // since service can decide who will be the active source when the device supports
+    // multiple device types in this method.
+    // This method should only be called when the device can be the active source.
+    protected void setAndBroadcastActiveSource(HdmiCecMessage message, int physicalAddress) {
+        mService.setAndBroadcastActiveSource(
+                physicalAddress, getDeviceInfo().getDeviceType(), message.getSource());
+    }
+
+    @ServiceThreadOnly
+    void setIsActiveSource(boolean on) {
+        assertRunOnServiceThread();
+        mIsActiveSource = on;
+    }
+
+    protected void wakeUpIfActiveSource() {
+        if (!mIsActiveSource) {
+            return;
+        }
+        // Wake up the device
+        mService.wakeUp();
+        return;
+    }
+
+    protected void maySendActiveSource(int dest) {
+        if (mIsActiveSource) {
+            mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
+                    mAddress, mService.getPhysicalAddress()));
+        }
+    }
+
+    /**
+     * Set {@link #mRoutingPort} to a specific {@link LocalActivePort} to record the current active
+     * CEC Routing Control related port.
+     *
+     * @param portId The portId of the new routing port.
+     */
+    @VisibleForTesting
+    protected void setRoutingPort(@LocalActivePort int portId) {
+        synchronized (mLock) {
+            mRoutingPort = portId;
+        }
+    }
+
+    /**
+     * Get {@link #mRoutingPort}. This is useful when the device needs to route to the last valid
+     * routing port.
+     */
+    @LocalActivePort
+    protected int getRoutingPort() {
+        synchronized (mLock) {
+            return mRoutingPort;
+        }
+    }
+
+    /**
+     * Get {@link #mLocalActivePort}. This is useful when device needs to know the current active
+     * port.
+     */
+    @LocalActivePort
+    protected int getLocalActivePort() {
+        synchronized (mLock) {
+            return mLocalActivePort;
+        }
+    }
+
+    /**
+     * Set {@link #mLocalActivePort} to a specific {@link LocalActivePort} to record the current
+     * active port.
+     *
+     * <p>It does not have to be a Routing Control related port. For example it can be
+     * set to {@link Constants#CEC_SWITCH_ARC} but this port is System Audio related.
+     *
+     * @param activePort The portId of the new active port.
+     */
+    protected void setLocalActivePort(@LocalActivePort int activePort) {
+        synchronized (mLock) {
+            mLocalActivePort = activePort;
+        }
+    }
+
+    boolean isRoutingControlFeatureEnabled() {
+        synchronized (mLock) {
+            return mRoutingControlFeatureEnabled;
+        }
+    }
+
+    // Check if the device is trying to switch to the same input that is active right now.
+    // This can help avoid redundant port switching.
+    protected boolean isSwitchingToTheSameInput(@LocalActivePort int activePort) {
+        return activePort == getLocalActivePort();
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 25ca278..a8c4350 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -41,17 +41,18 @@
 import android.media.AudioSystem;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvInputManager.TvInputCallback;
-import android.os.RemoteException;
 import android.provider.Settings.Global;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -307,7 +308,7 @@
     private void handleSelectInternalSource() {
         assertRunOnServiceThread();
         // Seq #18
-        if (mService.isControlEnabled() && mActiveSource.logicalAddress != mAddress) {
+        if (mService.isControlEnabled() && getActiveSource().logicalAddress != mAddress) {
             updateActiveSource(mAddress, mService.getPhysicalAddress());
             if (mSkipRoutingControl) {
                 mSkipRoutingControl = false;
@@ -329,7 +330,7 @@
     void updateActiveSource(ActiveSource newActive) {
         assertRunOnServiceThread();
         // Seq #14
-        if (mActiveSource.equals(newActive)) {
+        if (getActiveSource().equals(newActive)) {
             return;
         }
         setActiveSource(newActive);
@@ -345,10 +346,6 @@
         }
     }
 
-    int getPortId(int physicalAddress) {
-        return mService.pathToPortId(physicalAddress);
-    }
-
     /**
      * Returns the previous port id kept to handle input switching on <Inactive Source>.
      */
@@ -402,7 +399,7 @@
             invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
             return;
         }
-        mActiveSource.invalidate();
+        getActiveSource().invalidate();
         if (!mService.isControlEnabled()) {
             setActivePortId(portId);
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
@@ -452,17 +449,6 @@
         return Constants.ADDR_INVALID;
     }
 
-    private static void invokeCallback(IHdmiControlCallback callback, int result) {
-        if (callback == null) {
-            return;
-        }
-        try {
-            callback.onComplete(result);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Invoking callback failed:" + e);
-        }
-    }
-
     @Override
     @ServiceThreadOnly
     protected boolean handleActiveSource(HdmiCecMessage message) {
@@ -518,7 +504,7 @@
         } else {
             // No HDMI port to switch to was found. Notify the input change listers to
             // switch to the lastly shown internal input.
-            mActiveSource.invalidate();
+            getActiveSource().invalidate();
             setActivePath(Constants.INVALID_PHYSICAL_ADDRESS);
             mService.invokeInputChangeListener(HdmiDeviceInfo.INACTIVE_DEVICE);
         }
@@ -685,7 +671,7 @@
         byte[] params = message.getParams();
         int currentPath = HdmiUtils.twoBytesToInt(params);
         if (HdmiUtils.isAffectingActiveRoutingPath(getActivePath(), currentPath)) {
-            mActiveSource.invalidate();
+            getActiveSource().invalidate();
             removeAction(RoutingControlAction.class);
             int newPath = HdmiUtils.twoBytesToInt(params, 2);
             addAndStartAction(new RoutingControlAction(this, newPath, true, null));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c005615..f8b3962 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.annotation.Nullable;
+
 import libcore.util.EmptyArray;
 
 import java.util.Arrays;
@@ -111,12 +112,11 @@
     @Override
     public String toString() {
         StringBuffer s = new StringBuffer();
-        s.append(String.format("<%s> src: %d, dst: %d",
-                opcodeToString(mOpcode), mSource, mDestination));
+        s.append(String.format("<%s> %X%X:%02X",
+                opcodeToString(mOpcode), mSource, mDestination, mOpcode));
         if (mParams.length > 0) {
-            s.append(", params:");
             for (byte data : mParams) {
-                s.append(String.format(" %02X", data));
+                s.append(String.format(":%02X", data));
             }
         }
         return s.toString();
@@ -133,7 +133,7 @@
             case Constants.MESSAGE_TUNER_STEP_DECREMENT:
                 return "Tuner Step Decrement";
             case Constants.MESSAGE_TUNER_DEVICE_STATUS:
-                return "Tuner Device Staus";
+                return "Tuner Device Status";
             case Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS:
                 return "Give Tuner Device Status";
             case Constants.MESSAGE_RECORD_ON:
@@ -207,7 +207,7 @@
             case Constants.MESSAGE_DEVICE_VENDOR_ID:
                 return "Device Vendor Id";
             case Constants.MESSAGE_VENDOR_COMMAND:
-                return "Vendor Commandn";
+                return "Vendor Command";
             case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN:
                 return "Vendor Remote Button Down";
             case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP:
@@ -215,7 +215,7 @@
             case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
                 return "Give Device Vendor Id";
             case Constants.MESSAGE_MENU_REQUEST:
-                return "Menu REquest";
+                return "Menu Request";
             case Constants.MESSAGE_MENU_STATUS:
                 return "Menu Status";
             case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
@@ -247,7 +247,7 @@
             case Constants.MESSAGE_SET_EXTERNAL_TIMER:
                 return "Set External Timer";
             case Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR:
-                return "Repot Short Audio Descriptor";
+                return "Report Short Audio Descriptor";
             case Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR:
                 return "Request Short Audio Descriptor";
             case Constants.MESSAGE_INITIATE_ARC:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 941c321..3f949ba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -365,6 +365,20 @@
     }
 
     /**
+     * Build &lt;Routing Information&gt; command.
+     *
+     * <p>This is a broadcast message sent to all devices on the bus.
+     *
+     * @param src source address of command
+     * @param physicalAddress physical address of the new active routing path
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildRoutingInformation(int src, int physicalAddress) {
+        return buildCommand(src, Constants.ADDR_BROADCAST,
+            Constants.MESSAGE_ROUTING_INFORMATION, physicalAddressToParam(physicalAddress));
+    }
+
+    /**
      * Build &lt;Give Device Power Status&gt; command.
      *
      * @param src source address of command
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index f468c0b..46219d5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,12 +18,16 @@
 
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
+
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
 import static com.android.server.hdmi.Constants.DISABLED;
 import static com.android.server.hdmi.Constants.ENABLED;
 import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
 import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
 import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
 import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL;
+import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY;
 
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -67,6 +71,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
@@ -76,6 +81,9 @@
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
+
+import libcore.util.EmptyArray;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -85,7 +93,6 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import libcore.util.EmptyArray;
 
 /**
  * Provides a service for sending and processing HDMI control messages,
@@ -136,6 +143,14 @@
     static final int STANDBY_SCREEN_OFF = 0;
     static final int STANDBY_SHUTDOWN = 1;
 
+    // Logical address of the active source.
+    @GuardedBy("mLock")
+    protected final ActiveSource mActiveSource = new ActiveSource();
+
+    private static final boolean isHdmiCecNeverClaimPlaybackLogicAddr =
+            SystemProperties.getBoolean(
+                    Constants.PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS, false);
+
     /**
      * Interface to report send result.
      */
@@ -171,9 +186,10 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             assertRunOnServiceThread();
+            boolean isReboot = SystemProperties.get(SHUTDOWN_ACTION_PROPERTY).contains("1");
             switch (intent.getAction()) {
                 case Intent.ACTION_SCREEN_OFF:
-                    if (isPowerOnOrTransient()) {
+                    if (isPowerOnOrTransient() && !isReboot) {
                         onStandby(STANDBY_SCREEN_OFF);
                     }
                     break;
@@ -189,7 +205,7 @@
                     }
                     break;
                 case Intent.ACTION_SHUTDOWN:
-                    if (isPowerOnOrTransient()) {
+                    if (isPowerOnOrTransient() && !isReboot) {
                         onStandby(STANDBY_SHUTDOWN);
                     }
                     break;
@@ -332,6 +348,10 @@
     @Nullable
     private Looper mIoLooper;
 
+    // Thread safe physical address
+    @GuardedBy("mLock")
+    private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
+
     // Last input port before switching to the MHL port. Should switch back to this port
     // when the mobile device sends the request one touch play with off.
     // Gets invalidated if we go to other port/input.
@@ -409,7 +429,7 @@
         mSettingsObserver = new SettingsObserver(mHandler);
     }
 
-    private static List<Integer> getIntList(String string) {
+    protected static List<Integer> getIntList(String string) {
         ArrayList<Integer> list = new ArrayList<>();
         TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
         splitter.setString(string);
@@ -551,7 +571,8 @@
                 Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                 Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                 Global.MHL_INPUT_SWITCHING_ENABLED,
-                Global.MHL_POWER_CHARGE_ENABLED
+                Global.MHL_POWER_CHARGE_ENABLED,
+                Global.HDMI_CEC_SWITCH_ENABLED
         };
         for (String s : settings) {
             resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
@@ -592,6 +613,24 @@
                     if (isTvDeviceEnabled()) {
                         tv().setSystemAudioControlFeatureEnabled(enabled);
                     }
+                    if (isAudioSystemDevice()) {
+                        if (audioSystem() == null) {
+                            Slog.e(TAG, "Audio System device has not registered yet."
+                                    + " Can't turn system audio mode on.");
+                            break;
+                        }
+                        audioSystem().onSystemAduioControlFeatureSupportChanged(enabled);
+                    }
+                    break;
+                case Global.HDMI_CEC_SWITCH_ENABLED:
+                    if (isAudioSystemDevice()) {
+                        if (audioSystem() == null) {
+                            Slog.w(TAG, "Switch device has not registered yet."
+                                    + " Can't turn routing on.");
+                            break;
+                        }
+                        audioSystem().setRoutingControlFeatureEnables(enabled);
+                    }
                     break;
                 case Global.MHL_INPUT_SWITCHING_ENABLED:
                     setMhlInputChangeEnabled(enabled);
@@ -607,6 +646,7 @@
         return enabled ? ENABLED : DISABLED;
     }
 
+    @VisibleForTesting
     boolean readBooleanSetting(String key, boolean defVal) {
         ContentResolver cr = getContext().getContentResolver();
         return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
@@ -617,6 +657,11 @@
         Global.putInt(cr, key, toInt(value));
     }
 
+    void writeStringSetting(String key, String value) {
+        ContentResolver cr = getContext().getContentResolver();
+        Global.putString(cr, key, value);
+    }
+
     private void initializeCec(int initiatedBy) {
         mAddressAllocated = false;
         mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
@@ -630,6 +675,10 @@
         // A container for [Device type, Local device info].
         ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
         for (int type : mLocalDevices) {
+            if (type == HdmiDeviceInfo.DEVICE_PLAYBACK
+                    && isHdmiCecNeverClaimPlaybackLogicAddr) {
+                continue;
+            }
             HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
             if (localDevice == null) {
                 localDevice = HdmiCecLocalDevice.create(this, type);
@@ -717,6 +766,10 @@
         assertRunOnServiceThread();
         HdmiPortInfo[] cecPortInfo = null;
 
+        synchronized (mLock) {
+            mPhysicalAddress = getPhysicalAddress();
+        }
+
         // CEC HAL provides majority of the info while MHL does only MHL support flag for
         // each port. Return empty array if CEC HAL didn't provide the info.
         if (mCecController != null) {
@@ -738,6 +791,9 @@
         mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
         mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
 
+        if (mMhlController == null) {
+            return;
+        }
         HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
         ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
         for (HdmiPortInfo info : mhlPortInfo) {
@@ -792,13 +848,34 @@
     }
 
     /**
-     * Returns the id of HDMI port located at the top of the hierarchy of
-     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
-     * the port id to be returned is the ID associated with the port address
-     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
+     * Returns the id of HDMI port located at the current device that runs this method.
+     *
+     * For TV with physical address 0x0000, target device 0x1120, we want port physical address
+     * 0x1000 to get the correct port id from {@link #mPortIdMap}. For device with Physical Address
+     * 0x2000, target device 0x2420, we want port address 0x24000 to get the port id.
+     *
+     * <p>Return {@link Constants#INVALID_PORT_ID} if target device does not connect to.
+     *
+     * @param path the target device's physical address.
+     * @return the id of the port that the target device eventually connects to
+     * on the current device.
      */
     int pathToPortId(int path) {
-        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
+        int mask = 0xF000;
+        int finalMask = 0xF000;
+        int physicalAddress;
+        synchronized (mLock) {
+            physicalAddress = mPhysicalAddress;
+        }
+        int maskedAddress = physicalAddress;
+
+        while (maskedAddress != 0) {
+            maskedAddress = physicalAddress & mask;
+            finalMask |= mask;
+            mask >>= 4;
+        }
+
+        int portAddress = path & finalMask;
         return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
     }
 
@@ -991,9 +1068,18 @@
     void onHotplug(int portId, boolean connected) {
         assertRunOnServiceThread();
 
-        if (connected && !isTvDevice()) {
+        if (connected && !isTvDevice()
+                && getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            if (isSwitchDevice()) {
+                initPortInfo();
+                HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
+            }
             ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
             for (int type : mLocalDevices) {
+                if (type == HdmiDeviceInfo.DEVICE_PLAYBACK
+                        && isHdmiCecNeverClaimPlaybackLogicAddr) {
+                    continue;
+                }
                 HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
                 if (localDevice == null) {
                     localDevice = HdmiCecLocalDevice.create(this, type);
@@ -1088,7 +1174,7 @@
         String displayName = Build.MODEL;
         return new HdmiDeviceInfo(logicalAddress,
                 getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
-                getVendorId(), displayName);
+                getVendorId(), displayName, powerStatus);
     }
 
     @ServiceThreadOnly
@@ -1307,6 +1393,33 @@
             HdmiCecLocalDeviceTv tv = tv();
             if (tv == null) {
                 Slog.w(TAG, "Local tv device not available");
+                if (isPlaybackDevice()) {
+                    // if playback device itself is the active source,
+                    // return its own device info.
+                    if (playback() != null && playback().mIsActiveSource) {
+                        return playback().getDeviceInfo();
+                    }
+                    // Otherwise get the active source and look for it from the device list
+                    ActiveSource activeSource = mActiveSource;
+                    // If the active source is not set yet, return null
+                    if (!activeSource.isValid()) {
+                        return null;
+                    }
+                    if (audioSystem() != null) {
+                        HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+                        for (HdmiDeviceInfo info : audioSystem.getSafeCecDevicesLocked()) {
+                            if (info.getLogicalAddress() == activeSource.logicalAddress) {
+                                return info;
+                            }
+                        }
+                    }
+                    // If the device info is not in the list yet, return a device info with minimum
+                    // information from mActiveSource.
+                    return new HdmiDeviceInfo(activeSource.logicalAddress,
+                        activeSource.physicalAddress, pathToPortId(activeSource.physicalAddress),
+                        HdmiUtils.getTypeFromAddress(activeSource.logicalAddress), 0,
+                        HdmiUtils.getDefaultDeviceName(activeSource.logicalAddress));
+                }
                 return null;
             }
             ActiveSource activeSource = tv.getActiveSource();
@@ -1373,17 +1486,24 @@
                         return;
                     }
                     HdmiCecLocalDeviceTv tv = tv();
-                    if (tv == null) {
-                        if (!mAddressAllocated) {
-                            mSelectRequestBuffer.set(SelectRequestBuffer.newPortSelect(
-                                    HdmiControlService.this, portId, callback));
-                            return;
-                        }
-                        Slog.w(TAG, "Local tv device not available");
-                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
+                    if (tv != null) {
+                        tv.doManualPortSwitching(portId, callback);
                         return;
                     }
-                    tv.doManualPortSwitching(portId, callback);
+                    HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+                    if (audioSystem != null) {
+                        audioSystem.doManualPortSwitching(portId, callback);
+                        return;
+                    }
+
+                    if (!mAddressAllocated) {
+                        mSelectRequestBuffer.set(SelectRequestBuffer.newPortSelect(
+                                HdmiControlService.this, portId, callback));
+                        return;
+                    }
+                    Slog.w(TAG, "Local device not available");
+                    invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
+                    return;
                 }
             });
         }
@@ -1469,12 +1589,20 @@
 
         @Override
         public boolean getSystemAudioMode() {
+            // TODO(shubang): handle getSystemAudioMode() for all device types
             enforceAccessPermission();
             HdmiCecLocalDeviceTv tv = tv();
-            if (tv == null) {
-                return false;
+            HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+            return (tv != null && tv.isSystemAudioActivated())
+                    || (audioSystem != null && audioSystem.isSystemAudioActivated());
+        }
+
+        @Override
+        public int getPhysicalAddress() {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                return mPhysicalAddress;
             }
-            return tv.isSystemAudioActivated();
         }
 
         @Override
@@ -1534,14 +1662,62 @@
         public List<HdmiDeviceInfo> getDeviceList() {
             enforceAccessPermission();
             HdmiCecLocalDeviceTv tv = tv();
-            synchronized (mLock) {
-                return (tv == null)
+            if (tv != null) {
+                synchronized (mLock) {
+                    return tv.getSafeCecDevicesLocked();
+                }
+            } else {
+                HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+                synchronized (mLock) {
+                    return (audioSystem == null)
                         ? Collections.<HdmiDeviceInfo>emptyList()
-                        : tv.getSafeCecDevicesLocked();
+                        : audioSystem.getSafeCecDevicesLocked();
+                }
             }
         }
 
         @Override
+        public void powerOffRemoteDevice(int logicalAddress, int powerStatus) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    Slog.w(TAG, "Device "
+                            + logicalAddress + " power status is " + powerStatus
+                            + " before standby command sent out");
+                    sendCecCommand(HdmiCecMessageBuilder.buildStandby(
+                            getRemoteControlSourceAddress(), logicalAddress));
+                }
+            });
+        }
+
+        @Override
+        public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {
+            // TODO(amyjojo): implement the method
+        }
+
+        @Override
+        // TODO(AMYJOJO): add a result callback
+        public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+                            getRemoteControlSourceAddress(), physicalAddress);
+                    if (pathToPortId(physicalAddress) != Constants.INVALID_PORT_ID) {
+                        if (getSwitchDevice() != null) {
+                            getSwitchDevice().handleSetStreamPath(setStreamPath);
+                        } else {
+                            Slog.e(TAG, "Can't get the correct local device to handle routing.");
+                        }
+                    }
+                    sendCecCommand(setStreamPath);
+                }
+            });
+        }
+
+        @Override
         public void setSystemAudioVolume(final int oldIndex, final int newIndex,
                 final int maxIndex) {
             enforceAccessPermission();
@@ -1642,6 +1818,9 @@
                     }
                     HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
                     if (device == null) {
+                        device = audioSystem();
+                    }
+                    if (device == null) {
                         Slog.w(TAG, "Local device not available");
                         return;
                     }
@@ -1777,14 +1956,32 @@
                         Slog.w(TAG, "audio system is not in system audio mode");
                         return;
                     }
-                    int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+                    audioSystem().reportAudioStatus(Constants.ADDR_TV);
+                }
+            });
+        }
 
-                    sendCecCommand(HdmiCecMessageBuilder
-                            .buildReportAudioStatus(
-                                    device.getDeviceInfo().getLogicalAddress(),
-                                    Constants.ADDR_TV,
-                                    scaledVolume,
-                                    isMute));
+        @Override
+        public void setSystemAudioModeOnForAudioOnlySource() {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (!isAudioSystemDevice()) {
+                        Slog.e(TAG, "Not an audio system device. Won't set system audio mode on");
+                        return;
+                    }
+                    if (audioSystem() == null) {
+                        Slog.e(TAG, "Audio System local device is not registered");
+                        return;
+                    }
+                    if (!audioSystem().checkSupportAndSetSystemAudioMode(true)) {
+                        Slog.e(TAG, "System Audio Mode is not supported.");
+                        return;
+                    }
+                    sendCecCommand(
+                            HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                                    audioSystem().mAddress, Constants.ADDR_BROADCAST, true));
                 }
             });
         }
@@ -1794,36 +1991,64 @@
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
 
-            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
             pw.println("mProhibitMode: " + mProhibitMode);
-            if (mCecController != null) {
-                pw.println("mCecController: ");
-                pw.increaseIndent();
-                mCecController.dump(pw);
-                pw.decreaseIndent();
-            }
+            pw.println("mPowerStatus: " + mPowerStatus);
+
+            // System settings
+            pw.println("System_settings:");
+            pw.increaseIndent();
+            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
+            pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
+            pw.decreaseIndent();
 
             pw.println("mMhlController: ");
             pw.increaseIndent();
             mMhlController.dump(pw);
             pw.decreaseIndent();
 
-            pw.println("mPortInfo: ");
-            pw.increaseIndent();
-            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
-                pw.println("- " + hdmiPortInfo);
+            HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo);
+            if (mCecController != null) {
+                pw.println("mCecController: ");
+                pw.increaseIndent();
+                mCecController.dump(pw);
+                pw.decreaseIndent();
             }
-            pw.decreaseIndent();
-            pw.println("mPowerStatus: " + mPowerStatus);
         }
     }
 
+    // Get the source address to send out commands to devices connected to the current device
+    // when other services interact with HdmiControlService.
+    private int getRemoteControlSourceAddress() {
+        if (isAudioSystemDevice()) {
+            return audioSystem().getDeviceInfo().getLogicalAddress();
+        } else if (isPlaybackDevice()) {
+            return playback().getDeviceInfo().getLogicalAddress();
+        }
+        return ADDR_UNREGISTERED;
+    }
+
+    // Get the switch device to do CEC routing control
+    @Nullable
+    private HdmiCecLocalDeviceSource getSwitchDevice() {
+        if (isAudioSystemDevice()) {
+            return audioSystem();
+        }
+        if (isPlaybackDevice()) {
+            return playback();
+        }
+        return null;
+    }
+
     @ServiceThreadOnly
     private void oneTouchPlay(final IHdmiControlCallback callback) {
         assertRunOnServiceThread();
-        HdmiCecLocalDevicePlayback source = playback();
+        HdmiCecLocalDeviceSource source = playback();
         if (source == null) {
-            Slog.w(TAG, "Local playback device not available");
+            source = audioSystem();
+        }
+
+        if (source == null) {
+            Slog.w(TAG, "Local source device not available");
             invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
             return;
         }
@@ -2086,11 +2311,20 @@
         return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
     }
 
+    boolean isPlaybackDevice() {
+        return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_PLAYBACK);
+    }
+
+    boolean isSwitchDevice() {
+        return SystemProperties.getBoolean(
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+    }
+
     boolean isTvDeviceEnabled() {
         return isTvDevice() && tv() != null;
     }
 
-    private HdmiCecLocalDevicePlayback playback() {
+    protected HdmiCecLocalDevicePlayback playback() {
         return (HdmiCecLocalDevicePlayback)
                 mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
     }
@@ -2461,6 +2695,77 @@
         setLastInputForMhl(Constants.INVALID_PORT_ID);
     }
 
+    ActiveSource getActiveSource() {
+        synchronized (mLock) {
+            return mActiveSource;
+        }
+    }
+
+    void setActiveSource(int logicalAddress, int physicalAddress) {
+        synchronized (mLock) {
+            mActiveSource.logicalAddress = logicalAddress;
+            mActiveSource.physicalAddress = physicalAddress;
+        }
+    }
+
+    // This method should only be called when the device can be the active source
+    // and all the device types call into this method.
+    // For example, when receiving broadcast messages, all the device types will call this
+    // method but only one of them will be the Active Source.
+    protected void setAndBroadcastActiveSource(
+            int physicalAddress, int deviceType, int source) {
+        // If the device has both playback and audio system logical addresses,
+        // playback will claim active source. Otherwise audio system will.
+        if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
+            HdmiCecLocalDevicePlayback playback = playback();
+            playback.setIsActiveSource(true);
+            playback.wakeUpIfActiveSource();
+            playback.maySendActiveSource(source);
+            setActiveSource(playback.mAddress, physicalAddress);
+        }
+
+        if (deviceType == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
+            HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+            if (playback() != null) {
+                audioSystem.setIsActiveSource(false);
+            } else {
+                audioSystem.setIsActiveSource(true);
+                audioSystem.wakeUpIfActiveSource();
+                audioSystem.maySendActiveSource(source);
+                setActiveSource(audioSystem.mAddress, physicalAddress);
+            }
+        }
+    }
+
+    // This method should only be called when the device can be the active source
+    // and only one of the device types calls into this method.
+    // For example, when receiving One Touch Play, only playback device handles it
+    // and this method updates Active Source in all the device types sharing the same
+    // Physical Address.
+    protected void setAndBroadcastActiveSourceFromOneDeviceType(
+            int sourceAddress, int physicalAddress) {
+        // If the device has both playback and audio system logical addresses,
+        // playback will claim active source. Otherwise audio system will.
+        HdmiCecLocalDevicePlayback playback = playback();
+        HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+        if (playback != null) {
+            playback.setIsActiveSource(true);
+            playback.wakeUpIfActiveSource();
+            playback.maySendActiveSource(sourceAddress);
+            if (audioSystem != null) {
+                audioSystem.setIsActiveSource(false);
+            }
+            setActiveSource(playback.mAddress, physicalAddress);
+        } else {
+            if (audioSystem != null) {
+                audioSystem.setIsActiveSource(true);
+                audioSystem.wakeUpIfActiveSource();
+                audioSystem.maySendActiveSource(sourceAddress);
+                setActiveSource(audioSystem.mAddress, physicalAddress);
+            }
+        }
+    }
+
     @ServiceThreadOnly
     void setLastInputForMhl(int portId) {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 2a8117f..e44f1d1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -16,19 +16,35 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.Xml;
 
+import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.hdmi.Constants.AudioCodec;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * Various utilities to handle HDMI CEC messages.
  */
 final class HdmiUtils {
 
+    private static final String TAG = "HdmiUtils";
+
     private static final int[] ADDRESS_TO_TYPE = {
         HdmiDeviceInfo.DEVICE_TV,  // ADDR_TV
         HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_1
@@ -65,6 +81,12 @@
         "Secondary_TV",
     };
 
+    /**
+     * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)}
+     */
+    static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
+    static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
+
     private HdmiUtils() { /* cannot be instantiated */ }
 
     /**
@@ -317,4 +339,339 @@
                 info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
                 info.getVendorId(), info.getDisplayName(), newPowerStatus);
     }
+
+    /**
+     * Dump a {@link SparseArray} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        key = value
+     *        key = value
+     *        ...
+     * </pre>
+     */
+    static <T> void dumpSparseArray(IndentingPrintWriter pw, String name,
+            SparseArray<T> sparseArray) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        int size = sparseArray.size();
+        for (int i = 0; i < size; i++) {
+            int key = sparseArray.keyAt(i);
+            T value = sparseArray.get(key);
+            pw.printPair(Integer.toString(key), value);
+            pw.println();
+        }
+        pw.decreaseIndent();
+    }
+
+    private static void printWithTrailingColon(IndentingPrintWriter pw, String name) {
+        pw.println(name.endsWith(":") ? name : name.concat(":"));
+    }
+
+    /**
+     * Dump a {@link Map} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        key = value
+     *        key = value
+     *        ...
+     * </pre>
+     */
+    static <K, V> void dumpMap(IndentingPrintWriter pw, String name, Map<K, V> map) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        for (Map.Entry<K, V> entry: map.entrySet()) {
+            pw.printPair(entry.getKey().toString(), entry.getValue());
+            pw.println();
+        }
+        pw.decreaseIndent();
+    }
+
+    /**
+     * Dump a {@link Map} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        value
+     *        value
+     *        ...
+     * </pre>
+     */
+    static <T> void dumpIterable(IndentingPrintWriter pw, String name, Iterable<T> values) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        for (T value : values) {
+            pw.println(value);
+        }
+        pw.decreaseIndent();
+    }
+
+    /**
+     * Method to parse target physical address to the port number on the current device.
+     *
+     * <p>This check assumes target address is valid.
+     *
+     * @param targetPhysicalAddress is the physical address of the target device
+     * @param myPhysicalAddress is the physical address of the current device
+     * @return
+     * If the target device is under the current device, return the port number of current device
+     * that the target device is connected to. This also applies to the devices that are indirectly
+     * connected to the current device.
+     *
+     * <p>If the target device has the same physical address as the current device, return
+     * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
+     *
+     * <p>If the target device is not under the current device, return
+     * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
+     */
+    public static int getLocalPortFromPhysicalAddress(
+            int targetPhysicalAddress, int myPhysicalAddress) {
+        if (myPhysicalAddress == targetPhysicalAddress) {
+            return TARGET_SAME_PHYSICAL_ADDRESS;
+        }
+
+        int mask = 0xF000;
+        int finalMask = 0xF000;
+        int maskedAddress = myPhysicalAddress;
+
+        while (maskedAddress != 0) {
+            maskedAddress = myPhysicalAddress & mask;
+            finalMask |= mask;
+            mask >>= 4;
+        }
+
+        int portAddress = targetPhysicalAddress & finalMask;
+        if ((portAddress & (finalMask << 4)) != myPhysicalAddress) {
+            return TARGET_NOT_UNDER_LOCAL_DEVICE;
+        }
+
+        mask <<= 4;
+        int port = portAddress & mask;
+        while ((port >> 4) != 0) {
+            port >>= 4;
+        }
+        return port;
+    }
+
+    public static class ShortAudioDescriptorXmlParser {
+        // We don't use namespaces
+        private static final String NS = null;
+
+        // return a list of devices config
+        public static List<DeviceConfig> parse(InputStream in)
+                throws XmlPullParserException, IOException {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+            parser.setInput(in, null);
+            parser.nextTag();
+            return readDevices(parser);
+        }
+
+        private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException();
+            }
+            int depth = 1;
+            while (depth != 0) {
+                switch (parser.next()) {
+                    case XmlPullParser.END_TAG:
+                        depth--;
+                        break;
+                    case XmlPullParser.START_TAG:
+                        depth++;
+                        break;
+                }
+            }
+        }
+
+        private static List<DeviceConfig> readDevices(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            List<DeviceConfig> devices = new ArrayList<>();
+
+            parser.require(XmlPullParser.START_TAG, NS, "config");
+            while (parser.next() != XmlPullParser.END_TAG) {
+                if (parser.getEventType() != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                String name = parser.getName();
+                // Starts by looking for the device tag
+                if (name.equals("device")) {
+                    String deviceType = parser.getAttributeValue(null, "type");
+                    DeviceConfig config = null;
+                    if (deviceType != null) {
+                        config = readDeviceConfig(parser, deviceType);
+                    }
+                    if (config != null) {
+                        devices.add(config);
+                    }
+                } else {
+                    skip(parser);
+                }
+            }
+            return devices;
+        }
+
+        // Processes device tags in the config.
+        @Nullable
+        private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType)
+                throws XmlPullParserException, IOException {
+            List<CodecSad> codecSads = new ArrayList<>();
+            int format;
+            byte[] descriptor;
+
+            parser.require(XmlPullParser.START_TAG, NS, "device");
+            while (parser.next() != XmlPullParser.END_TAG) {
+                if (parser.getEventType() != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                String tagName = parser.getName();
+
+                // Starts by looking for the supportedFormat tag
+                if (tagName.equals("supportedFormat")) {
+                    String codecAttriValue = parser.getAttributeValue(null, "format");
+                    String sadAttriValue = parser.getAttributeValue(null, "descriptor");
+                    format = (codecAttriValue) == null
+                            ? Constants.AUDIO_CODEC_NONE : formatNameToNum(codecAttriValue);
+                    descriptor = readSad(sadAttriValue);
+                    if (format != Constants.AUDIO_CODEC_NONE && descriptor != null) {
+                        codecSads.add(new CodecSad(format, descriptor));
+                    }
+                    parser.nextTag();
+                    parser.require(XmlPullParser.END_TAG, NS, "supportedFormat");
+                } else {
+                    skip(parser);
+                }
+            }
+            if (codecSads.size() == 0) {
+                return null;
+            }
+            return new DeviceConfig(deviceType, codecSads);
+        }
+
+        // Processes sad attribute in the supportedFormat.
+        @Nullable
+        private static byte[] readSad(String sad) {
+            if (sad == null || sad.length() == 0) {
+                return null;
+            }
+            byte[] sadBytes = HexDump.hexStringToByteArray(sad);
+            if (sadBytes.length != 3) {
+                Slog.w(TAG, "SAD byte array length is not 3. Length = " + sadBytes.length);
+                return null;
+            }
+            return sadBytes;
+        }
+
+        @AudioCodec
+        private static int formatNameToNum(String codecAttriValue) {
+            switch (codecAttriValue) {
+                case "AUDIO_FORMAT_NONE":
+                    return Constants.AUDIO_CODEC_NONE;
+                case "AUDIO_FORMAT_LPCM":
+                    return Constants.AUDIO_CODEC_LPCM;
+                case "AUDIO_FORMAT_DD":
+                    return Constants.AUDIO_CODEC_DD;
+                case "AUDIO_FORMAT_MPEG1":
+                    return Constants.AUDIO_CODEC_MPEG1;
+                case "AUDIO_FORMAT_MP3":
+                    return Constants.AUDIO_CODEC_MP3;
+                case "AUDIO_FORMAT_MPEG2":
+                    return Constants.AUDIO_CODEC_MPEG2;
+                case "AUDIO_FORMAT_AAC":
+                    return Constants.AUDIO_CODEC_AAC;
+                case "AUDIO_FORMAT_DTS":
+                    return Constants.AUDIO_CODEC_DTS;
+                case "AUDIO_FORMAT_ATRAC":
+                    return Constants.AUDIO_CODEC_ATRAC;
+                case "AUDIO_FORMAT_ONEBITAUDIO":
+                    return Constants.AUDIO_CODEC_ONEBITAUDIO;
+                case "AUDIO_FORMAT_DDP":
+                    return Constants.AUDIO_CODEC_DDP;
+                case "AUDIO_FORMAT_DTSHD":
+                    return Constants.AUDIO_CODEC_DTSHD;
+                case "AUDIO_FORMAT_TRUEHD":
+                    return Constants.AUDIO_CODEC_TRUEHD;
+                case "AUDIO_FORMAT_DST":
+                    return Constants.AUDIO_CODEC_DST;
+                case "AUDIO_FORMAT_WMAPRO":
+                    return Constants.AUDIO_CODEC_WMAPRO;
+                case "AUDIO_FORMAT_MAX":
+                    return Constants.AUDIO_CODEC_MAX;
+                default:
+                    return Constants.AUDIO_CODEC_NONE;
+            }
+        }
+    }
+
+    // Device configuration of its supported Codecs and their Short Audio Descriptors.
+    public static class DeviceConfig {
+        /** Name of the device. Should be {@link Constants.AudioDevice}. **/
+        public final String name;
+        /** List of a {@link CodecSad}. **/
+        public final List<CodecSad> supportedCodecs;
+
+        public DeviceConfig(String name, List<CodecSad> supportedCodecs) {
+            this.name = name;
+            this.supportedCodecs = supportedCodecs;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof DeviceConfig) {
+                DeviceConfig that = (DeviceConfig) obj;
+                return that.name.equals(this.name)
+                    && that.supportedCodecs.equals(this.supportedCodecs);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                name,
+                supportedCodecs.hashCode());
+        }
+    }
+
+    // Short Audio Descriptor of a specific Codec
+    public static class CodecSad {
+        /** Audio Codec. Should be {@link Constants.AudioCodec}. **/
+        public final int audioCodec;
+        /**
+         * Three-byte Short Audio Descriptor. See HDMI Specification 1.4b CEC 13.15.3 and
+         * ANSI-CTA-861-F-FINAL 7.5.2 Audio Data Block for more details.
+         */
+        public final byte[] sad;
+
+        public CodecSad(int audioCodec, byte[] sad) {
+            this.audioCodec = audioCodec;
+            this.sad = sad;
+        }
+
+        public CodecSad(int audioCodec, String sad) {
+            this.audioCodec = audioCodec;
+            this.sad = HexDump.hexStringToByteArray(sad);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof CodecSad) {
+                CodecSad that = (CodecSad) obj;
+                return that.audioCodec == this.audioCodec
+                    && Arrays.equals(that.sad, this.sad);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                audioCodec,
+                Arrays.hashCode(sad));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 5c66316..354d8d1 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -16,8 +16,8 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -55,7 +55,7 @@
     private int mPowerStatusCounter = 0;
 
     // Factory method. Ensures arguments are valid.
-    static OneTouchPlayAction create(HdmiCecLocalDevicePlayback source,
+    static OneTouchPlayAction create(HdmiCecLocalDeviceSource source,
             int targetAddress, IHdmiControlCallback callback) {
         if (source == null || callback == null) {
             Slog.e(TAG, "Wrong arguments");
@@ -83,9 +83,20 @@
     }
 
     private void broadcastActiveSource() {
-        sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
-        // Because only playback device can create this action, it's safe to cast.
-        playback().setActiveSource(true);
+        // Because only source device can create this action, it's safe to cast.
+        HdmiCecLocalDeviceSource source = source();
+        source.mService.setAndBroadcastActiveSourceFromOneDeviceType(
+                mTargetAddress, getSourcePath());
+        // Set local active port to HOME when One Touch Play.
+        // Active Port and Current Input are handled by the switch functionality device.
+        if (source.mService.audioSystem() != null) {
+            source = source.mService.audioSystem();
+        }
+        if (source.getLocalActivePort() != Constants.CEC_SWITCH_HOME) {
+            source.switchInputOnReceivingNewActivePath(getSourcePath());
+        }
+        source.setRoutingPort(Constants.CEC_SWITCH_HOME);
+        source.setLocalActivePort(Constants.CEC_SWITCH_HOME);
     }
 
     private void queryDevicePowerStatus() {
diff --git a/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java b/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java
index 75986c7..ba16260 100644
--- a/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java
+++ b/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java
@@ -56,6 +56,10 @@
             return mService.tv();
         }
 
+        protected HdmiCecLocalDeviceAudioSystem audioSystem() {
+            return mService.audioSystem();
+        }
+
         protected boolean isLocalDeviceReady() {
             if (tv() == null) {
                 Slog.e(TAG, "Local tv device not available");
@@ -105,7 +109,15 @@
         public void process() {
             if (isLocalDeviceReady()) {
                 Slog.v(TAG, "calling delayed portSelect id:" + mId);
-                tv().doManualPortSwitching(mId, mCallback);
+                HdmiCecLocalDeviceTv tv = tv();
+                if (tv != null) {
+                    tv.doManualPortSwitching(mId, mCallback);
+                    return;
+                }
+                HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+                if (audioSystem != null) {
+                    audioSystem.doManualPortSwitching(mId, mCallback);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
index 2fdcb51..b6ebcd7c 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
@@ -16,6 +16,7 @@
 package com.android.server.hdmi;
 
 import android.hardware.tv.cec.V1_0.SendMessageResult;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
@@ -40,7 +41,7 @@
 
     @Override
     boolean start() {
-        if (audioSystem().mActiveSource.physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+        if (audioSystem().getActiveSource().physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
             mState = STATE_WAITING_FOR_ACTIVE_SOURCE;
             addTimer(mState, HdmiConfig.TIMEOUT_MS);
             sendRequestActiveSource();
@@ -59,10 +60,8 @@
                     return false;
                 }
                 mActionTimer.clearTimerMessage();
-                int physicalAddress = HdmiUtils.twoBytesToInt(cmd.getParams());
-                if (physicalAddress != getSourcePath()) {
-                    audioSystem().setActiveSource(cmd.getSource(), physicalAddress);
-                }
+                // Broadcast message is also handled by other device types
+                audioSystem().handleActiveSource(cmd);
                 mState = STATE_WAITING_FOR_TV_SUPPORT;
                 queryTvSystemAudioModeSupport();
                 return true;
@@ -91,7 +90,7 @@
                             mSendRequestActiveSourceRetryCount++;
                             sendRequestActiveSource();
                         } else {
-                            audioSystem().setSystemAudioMode(false);
+                            audioSystem().checkSupportAndSetSystemAudioMode(false);
                             finish();
                         }
                     }
@@ -106,7 +105,7 @@
                             mSendSetSystemAudioModeRetryCount++;
                             sendSetSystemAudioMode(on, dest);
                         } else {
-                            audioSystem().setSystemAudioMode(false);
+                            audioSystem().checkSupportAndSetSystemAudioMode(false);
                             finish();
                         }
                     }
@@ -115,7 +114,16 @@
 
     private void handleActiveSourceTimeout() {
         HdmiLogger.debug("Cannot get active source.");
-        audioSystem().setSystemAudioMode(false);
+        // If not able to find Active Source and the current device has playbcak functionality,
+        // claim Active Source and start to query TV system audio mode support.
+        if (audioSystem().mService.isPlaybackDevice()) {
+            audioSystem().mService.setAndBroadcastActiveSourceFromOneDeviceType(
+                    Constants.ADDR_BROADCAST, getSourcePath());
+            mState = STATE_WAITING_FOR_TV_SUPPORT;
+            queryTvSystemAudioModeSupport();
+        } else {
+            audioSystem().checkSupportAndSetSystemAudioMode(false);
+        }
         finish();
     }
 
@@ -123,12 +131,12 @@
         audioSystem().queryTvSystemAudioModeSupport(
                 supported -> {
                     if (supported) {
-                        if (audioSystem().setSystemAudioMode(true)) {
+                        if (audioSystem().checkSupportAndSetSystemAudioMode(true)) {
                             sendSetSystemAudioMode(true, Constants.ADDR_BROADCAST);
                         }
                         finish();
                     } else {
-                        audioSystem().setSystemAudioMode(false);
+                        audioSystem().checkSupportAndSetSystemAudioMode(false);
                         finish();
                     }
                 });
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index a3ebe24..8e26097 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -274,10 +274,14 @@
         }
 
         synchronized (mLock) {
-            final S service = getServiceForUserLocked(userId);
-            if (service != null) {
-                service.setTemporaryServiceLocked(componentName, durationMs);
+            final S oldService = peekServiceForUserLocked(userId);
+            if (oldService != null) {
+                oldService.removeSelfFromCacheLocked();
             }
+            mServiceNameResolver.setTemporaryService(userId, componentName, durationMs);
+
+            // Must update the service on cache so its initialization code is triggered
+            updateCachedServiceLocked(userId);
         }
     }
 
@@ -500,6 +504,7 @@
     protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
         boolean realDebug = debug;
         boolean realVerbose = verbose;
+        final String prefix2 = "    ";
 
         try {
             // Temporarily turn on full logging;
@@ -510,6 +515,13 @@
             if (mServiceNameResolver != null) {
                 pw.print(prefix); pw.print("Name resolver: ");
                 mServiceNameResolver.dumpShort(pw); pw.println();
+                final UserManager um = getContext().getSystemService(UserManager.class);
+                final List<UserInfo> users = um.getUsers();
+                for (int i = 0; i < users.size(); i++) {
+                    final int userId = users.get(i).id;
+                    pw.print(prefix2); pw.print(userId); pw.print(": ");
+                    mServiceNameResolver.dumpShort(pw, userId); pw.println();
+                }
             }
             pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
             pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
@@ -522,7 +534,6 @@
                 pw.println("none");
             } else {
                 pw.println(size);
-                final String prefix2 = "    ";
                 for (int i = 0; i < size; i++) {
                     pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
                     final S service = mServicesCache.valueAt(i);
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 556e489..cd9ebcd 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -214,7 +214,7 @@
 
     /**
      * Gets the current name of the service, which is either the default service or the
-     *  {@link #setTemporaryServiceLocked(String, int) temporary one}.
+     *  {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
      */
     protected final @Nullable String getComponentNameLocked() {
         return mMaster.mServiceNameResolver.getServiceName(mUserId);
@@ -228,17 +228,6 @@
     }
 
     /**
-     * Temporary sets the service implementation.
-     *
-     * @param componentName name of the new component
-     * @param durationMs how long the change will be valid (the service will be automatically reset
-     * to the default component after this timeout expires).
-     */
-    protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
-        mMaster.mServiceNameResolver.setTemporaryService(mUserId, componentName, durationMs);
-    }
-
-    /**
      * Resets the temporary service implementation to the default component.
      */
     protected final void resetTemporaryServiceLocked() {
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 7027246..7f198ac 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -159,7 +159,7 @@
 
     @Override
     public String toString() {
-        return "FrameworkResourcesServiceNamer: temps=" + mTemporaryServiceNames;
+        return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
     }
 
     // TODO(b/117779333): support proto
diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
index bcaa918..cac7f53 100644
--- a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
@@ -56,4 +56,9 @@
     public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
         pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
     }
+
+    @Override
+    public String toString() {
+        return "SecureSettingsServiceNameResolver[" + mProperty + "]";
+    }
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d96b6cb..669ff2b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -138,6 +138,9 @@
     private final Context mContext;
     private final InputManagerHandler mHandler;
 
+    // Context cache used for loading pointer resources.
+    private Context mDisplayContext;
+
     private final File mDoubleTouchGestureEnableFile;
 
     private WindowManagerCallbacks mWindowManagerCallbacks;
@@ -213,8 +216,6 @@
     private static native void nativeSetFocusedApplication(long ptr,
             int displayId, InputApplicationHandle application);
     private static native void nativeSetFocusedDisplay(long ptr, int displayId);
-    private static native boolean nativeTransferTouchFocus(long ptr,
-            InputChannel fromChannel, InputChannel toChannel);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
     private static native void nativeSetShowTouches(long ptr, boolean enabled);
     private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -1485,29 +1486,6 @@
         nativeSetSystemUiVisibility(mPtr, visibility);
     }
 
-    /**
-     * Atomically transfers touch focus from one window to another as identified by
-     * their input channels.  It is possible for multiple windows to have
-     * touch focus if they support split touch dispatch
-     * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
-     * method only transfers touch focus of the specified window without affecting
-     * other windows that may also have touch focus at the same time.
-     * @param fromChannel The channel of a window that currently has touch focus.
-     * @param toChannel The channel of the window that should receive touch focus in
-     * place of the first.
-     * @return True if the transfer was successful.  False if the window with the
-     * specified channel did not actually have touch focus at the time of the request.
-     */
-    public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
-        if (fromChannel == null) {
-            throw new IllegalArgumentException("fromChannel must not be null.");
-        }
-        if (toChannel == null) {
-            throw new IllegalArgumentException("toChannel must not be null.");
-        }
-        return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
-    }
-
     @Override // Binder call
     public void tryPointerSpeed(int speed) {
         if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
@@ -1785,15 +1763,17 @@
     }
 
     // Native callback
-    private void notifyFocusChanged(IBinder token) {
-        if (mFocusedWindow != token) {
-            if (mFocusedWindowHasCapture) {
-                setPointerCapture(false);
+    private void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
+        if (mFocusedWindow != null) {
+            if (mFocusedWindow.asBinder() == newToken) {
+                Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
+                        + mFocusedWindow);
+                return;
             }
-            if (token instanceof IWindow) {
-                mFocusedWindow = (IWindow) token;
-            }
+            setPointerCapture(false);
         }
+
+        mFocusedWindow = IWindow.Stub.asInterface(newToken);
     }
 
     // Native callback.
@@ -1946,8 +1926,30 @@
     }
 
     // Native callback.
-    private PointerIcon getPointerIcon() {
-        return PointerIcon.getDefaultIcon(mContext);
+    private PointerIcon getPointerIcon(int displayId) {
+        return PointerIcon.getDefaultIcon(getContextForDisplay(displayId));
+    }
+
+    private Context getContextForDisplay(int displayId) {
+        if (mDisplayContext != null && mDisplayContext.getDisplay().getDisplayId() == displayId) {
+            return mDisplayContext;
+        }
+
+        if (mContext.getDisplay().getDisplayId() == displayId) {
+            mDisplayContext = mContext;
+            return mDisplayContext;
+        }
+
+        // Create and cache context for non-default display.
+        final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        final Display display = displayManager.getDisplay(displayId);
+        mDisplayContext = mContext.createDisplayContext(display);
+        return mDisplayContext;
+    }
+
+    // Native callback.
+    private int getPointerDisplayId() {
+        return mWindowManagerCallbacks.getPointerDisplayId();
     }
 
     // Native callback.
@@ -2017,6 +2019,8 @@
                 KeyEvent event, int policyFlags);
 
         public int getPointerLayer();
+
+        public int getPointerDisplayId();
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 3264790..d4b8eb2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -16,7 +16,15 @@
 
 package com.android.server.inputmethod;
 
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
+import android.view.inputmethod.InputMethodInfo;
+
+import com.android.server.LocalServices;
+
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Input method manager local system service interface.
@@ -39,9 +47,25 @@
     public abstract void startVrInputMethodNoCheck(ComponentName componentName);
 
     /**
+     * Returns the list of installed input methods for the specified user.
+     *
+     * @param userId The user ID to be queried.
+     * @return A list of {@link InputMethodInfo}.  VR-only IMEs are already excluded.
+     */
+    public abstract List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
+
+    /**
+     * Returns the list of installed input methods that are enabled for the specified user.
+     *
+     * @param userId The user ID to be queried.
+     * @return A list of {@link InputMethodInfo} that are enabled for {@code userId}.
+     */
+    public abstract List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId);
+
+    /**
      * Fake implementation of {@link InputMethodManagerInternal}.  All the methods do nothing.
      */
-    public static final InputMethodManagerInternal NOP =
+    private static final InputMethodManagerInternal NOP =
             new InputMethodManagerInternal() {
                 @Override
                 public void setInteractive(boolean interactive) {
@@ -54,5 +78,25 @@
                 @Override
                 public void startVrInputMethodNoCheck(ComponentName componentName) {
                 }
+
+                @Override
+                public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+                    return Collections.emptyList();
+                }
+
+                @Override
+                public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+                    return Collections.emptyList();
+                }
             };
+
+    /**
+     * @return Global instance if exists.  Otherwise, a dummy no-op instance.
+     */
+    @NonNull
+    public static InputMethodManagerInternal get() {
+        final InputMethodManagerInternal instance =
+                LocalServices.getService(InputMethodManagerInternal.class);
+        return instance != null ? instance : NOP;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 5a7739c..7788d26 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.inputmethod.InputMethodSystemProperty.PER_PROFILE_IME_ENABLED;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -88,6 +89,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -291,6 +293,8 @@
                 new DebugFlag("debug.optimize_startinput", false);
     }
 
+    @UserIdInt
+    private int mLastSwitchUserId;
 
     final Context mContext;
     final Resources mRes;
@@ -306,6 +310,7 @@
     private final HardKeyboardListener mHardKeyboardListener;
     private final AppOpsManager mAppOpsManager;
     private final UserManager mUserManager;
+    private final UserManagerInternal mUserManagerInternal;
 
     // All known input methods.  mMethodMap also serves as the global
     // lock for this class.
@@ -1402,6 +1407,7 @@
         }, true /*asyncHandler*/);
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mHardKeyboardListener = new HardKeyboardListener();
         mHasFeature = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_INPUT_METHODS);
@@ -1436,6 +1442,8 @@
             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
         }
 
+        mLastSwitchUserId = userId;
+
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
                 mRes, context.getContentResolver(), mMethodMap, mMethodList, userId, !mSystemReady);
@@ -1484,7 +1492,7 @@
         // If the system is not ready or the device is not yed unlocked by the user, then we use
         // copy-on-write settings.
         final boolean useCopyOnWriteSettings =
-                !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId);
+                !mSystemReady || !mUserManagerInternal.isUserUnlockingOrUnlocked(newUserId);
         mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
         updateCurrentProfileIds();
         // Additional subtypes should be reset when the user is changed
@@ -1523,6 +1531,8 @@
 
         if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
                 + " selectedIme=" + mSettings.getSelectedInputMethod());
+
+        mLastSwitchUserId = newUserId;
     }
 
     void updateCurrentProfileIds() {
@@ -1555,7 +1565,7 @@
                 mLastSystemLocales = mRes.getConfiguration().getLocales();
                 final int currentUserId = mSettings.getCurrentUserId();
                 mSettings.switchCurrentUser(currentUserId,
-                        !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
+                        !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId));
                 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
                 mNotificationManager = mContext.getSystemService(NotificationManager.class);
                 mStatusBar = statusBar;
@@ -1597,7 +1607,7 @@
     // 1) it comes from the system process
     // 2) the calling process' user id is identical to the current user id IMMS thinks.
     @GuardedBy("mMethodMap")
-    private boolean calledFromValidUserLocked() {
+    private boolean calledFromValidUserLocked(boolean allowCrossProfileAccess) {
         final int uid = Binder.getCallingUid();
         final int userId = UserHandle.getUserId(uid);
         if (DEBUG) {
@@ -1607,7 +1617,13 @@
                     + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
                     + InputMethodUtils.getApiCallStack());
         }
-        if (uid == Process.SYSTEM_UID || mSettings.isCurrentProfile(userId)) {
+        if (uid == Process.SYSTEM_UID) {
+            return true;
+        }
+        if (userId == mSettings.getCurrentUserId()) {
+            return true;
+        }
+        if (allowCrossProfileAccess && mSettings.isCurrentProfile(userId)) {
             return true;
         }
 
@@ -1668,40 +1684,93 @@
 
     @Override
     public List<InputMethodInfo> getInputMethodList() {
-        return getInputMethodList(false /* isVrOnly */);
+        final int callingUserId = UserHandle.getCallingUserId();
+        synchronized (mMethodMap) {
+            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+                    mSettings.getCurrentUserId(), null);
+            if (resolvedUserIds.length != 1) {
+                return Collections.emptyList();
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return getInputMethodListLocked(false /* isVrOnly */, resolvedUserIds[0]);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
     @Override
     public List<InputMethodInfo> getVrInputMethodList() {
-        return getInputMethodList(true /* isVrOnly */);
-    }
-
-    private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) {
+        final int callingUserId = UserHandle.getCallingUserId();
         synchronized (mMethodMap) {
-            // TODO: Make this work even for non-current users?
-            if (!calledFromValidUserLocked()) {
+            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+                    mSettings.getCurrentUserId(), null);
+            if (resolvedUserIds.length != 1) {
                 return Collections.emptyList();
             }
-            ArrayList<InputMethodInfo> methodList = new ArrayList<>();
-            for (InputMethodInfo info : mMethodList) {
-
-                if (info.isVrOnly() == isVrOnly) {
-                    methodList.add(info);
-                }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return getInputMethodListLocked(true /* isVrOnly */, resolvedUserIds[0]);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
-            return methodList;
         }
     }
 
     @Override
     public List<InputMethodInfo> getEnabledInputMethodList() {
+        final int callingUserId = UserHandle.getCallingUserId();
         synchronized (mMethodMap) {
-            // TODO: Make this work even for non-current users?
-            if (!calledFromValidUserLocked()) {
+            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+                    mSettings.getCurrentUserId(), null);
+            if (resolvedUserIds.length != 1) {
                 return Collections.emptyList();
             }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return getEnabledInputMethodListLocked(resolvedUserIds[0]);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @GuardedBy("mMethodMap")
+    private List<InputMethodInfo> getInputMethodListLocked(boolean isVrOnly,
+            @UserIdInt int userId) {
+        final ArrayList<InputMethodInfo> methodList;
+        if (userId == mSettings.getCurrentUserId()) {
+            // Create a copy.
+            methodList = new ArrayList<>(mMethodList);
+        } else {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            methodList = new ArrayList<>();
+            final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+                    new ArrayMap<>();
+            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+                    methodList);
+        }
+        methodList.removeIf(imi -> imi.isVrOnly() != isVrOnly);
+        return methodList;
+    }
+
+    @GuardedBy("mMethodMap")
+    private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
+        if (userId == mSettings.getCurrentUserId()) {
             return mSettings.getEnabledInputMethodListLocked();
         }
+        final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+        final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+        final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+                new ArrayMap<>();
+        AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+        queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+                methodList);
+        final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
+                mContext.getContentResolver(), methodMap, methodList, userId, true);
+        return settings.getEnabledInputMethodListLocked();
     }
 
     /**
@@ -1711,11 +1780,27 @@
     @Override
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
             boolean allowsImplicitlySelectedSubtypes) {
+        final int callingUserId = UserHandle.getCallingUserId();
         synchronized (mMethodMap) {
-            // TODO: Make this work even for non-current users?
-            if (!calledFromValidUserLocked()) {
+            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+                    mSettings.getCurrentUserId(), null);
+            if (resolvedUserIds.length != 1) {
                 return Collections.emptyList();
             }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return getEnabledInputMethodSubtypeListLocked(imiId,
+                        allowsImplicitlySelectedSubtypes, resolvedUserIds[0]);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @GuardedBy("mMethodMap")
+    private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
+            boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) {
+        if (userId == mSettings.getCurrentUserId()) {
             final InputMethodInfo imi;
             if (imiId == null && mCurMethodId != null) {
                 imi = mMethodMap.get(mCurMethodId);
@@ -1728,6 +1813,21 @@
             return mSettings.getEnabledInputMethodSubtypeListLocked(
                     mContext, imi, allowsImplicitlySelectedSubtypes);
         }
+        final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+        final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+        final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+                new ArrayMap<>();
+        AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+        queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+                methodList);
+        final InputMethodInfo imi = methodMap.get(imiId);
+        if (imi == null) {
+            return Collections.emptyList();
+        }
+        final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
+                mContext.getContentResolver(), methodMap, methodList, userId, true);
+        return settings.getEnabledInputMethodSubtypeListLocked(
+                mContext, imi, allowsImplicitlySelectedSubtypes);
     }
 
     /**
@@ -2428,7 +2528,7 @@
     @Override
     public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return;
             }
             final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
@@ -2444,7 +2544,7 @@
     @Override
     public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return false;
             }
             final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
@@ -2611,7 +2711,7 @@
             ResultReceiver resultReceiver) {
         int uid = Binder.getCallingUid();
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return false;
             }
             final long ident = Binder.clearCallingIdentity();
@@ -2696,7 +2796,7 @@
             ResultReceiver resultReceiver) {
         int uid = Binder.getCallingUid();
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return false;
             }
             final long ident = Binder.clearCallingIdentity();
@@ -2803,10 +2903,28 @@
             @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
             IInputContext inputContext, @MissingMethodFlags int missingMethods,
             int unverifiedTargetSdkVersion) {
+        final int callingUserId = UserHandle.getCallingUserId();
+        final int userId;
+        if (PER_PROFILE_IME_ENABLED && attribute != null && attribute.targetInputMethodUser != null
+                && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
+            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "Using EditorInfo.user requires INTERACT_ACROSS_USERS_FULL.");
+            userId = attribute.targetInputMethodUser.getIdentifier();
+            if (!mUserManagerInternal.isUserRunning(userId)) {
+                // There is a chance that we hit here because of race condition.  Let's just return
+                // an error code instead of crashing the caller process, which at least has
+                // INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important process.
+                Slog.e(TAG, "User #" + userId + " is not running.");
+                return InputBindResult.INVALID_USER;
+            }
+        } else {
+            userId = callingUserId;
+        }
         InputBindResult res = null;
         synchronized (mMethodMap) {
             // Needs to check the validity before clearing calling identity
-            final boolean calledFromValidUser = calledFromValidUserLocked();
+            // Note that cross-profile access is always allowed here to allow profile-switching.
+            final boolean calledFromValidUser = calledFromValidUserLocked(true);
             final int windowDisplayId =
                     mWindowManagerInternal.getDisplayIdForWindow(windowToken);
             final long ident = Binder.clearCallingIdentity();
@@ -2858,6 +2976,10 @@
                     return InputBindResult.INVALID_USER;
                 }
 
+                if (PER_PROFILE_IME_ENABLED && userId != mSettings.getCurrentUserId()) {
+                    switchUserLocked(userId);
+                }
+
                 if (mCurFocusedWindow == windowToken) {
                     if (DEBUG) {
                         Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
@@ -3026,7 +3148,7 @@
     public void showInputMethodPickerFromClient(
             IInputMethodClient client, int auxiliarySubtypeMode) {
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return;
             }
             if(!canShowInputMethodPickerLocked(client)) {
@@ -3097,7 +3219,7 @@
             IInputMethodClient client, String inputMethodId) {
         synchronized (mMethodMap) {
             // TODO(yukawa): Should we verify the display ID?
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return;
             }
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
@@ -3212,7 +3334,7 @@
     @Override
     public InputMethodSubtype getLastInputMethodSubtype() {
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return null;
             }
             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
@@ -3250,7 +3372,7 @@
             }
         }
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return;
             }
             if (!mSystemReady) {
@@ -4096,7 +4218,7 @@
     public InputMethodSubtype getCurrentInputMethodSubtype() {
         synchronized (mMethodMap) {
             // TODO: Make this work even for non-current users?
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return null;
             }
             return getCurrentInputMethodSubtypeLocked();
@@ -4146,7 +4268,7 @@
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
         synchronized (mMethodMap) {
             // TODO: Make this work even for non-current users?
-            if (!calledFromValidUserLocked()) {
+            if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
                 return false;
             }
             if (subtype != null && mCurMethodId != null) {
@@ -4161,6 +4283,18 @@
         }
     }
 
+    private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
+        synchronized (mMethodMap) {
+            return getInputMethodListLocked(false, userId);
+        }
+    }
+
+    private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
+        synchronized (mMethodMap) {
+            return getEnabledInputMethodListLocked(userId);
+        }
+    }
+
     private static final class LocalServiceImpl extends InputMethodManagerInternal {
         @NonNull
         private final InputMethodManagerService mService;
@@ -4186,6 +4320,16 @@
         public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
             mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
         }
+
+        @Override
+        public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+            return mService.getInputMethodListAsUser(userId);
+        }
+
+        @Override
+        public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+            return mService.getEnabledInputMethodListAsUser(userId);
+        }
     }
 
     @BinderThread
@@ -4424,6 +4568,10 @@
                 return refreshDebugProperties();
             }
 
+            if ("get-last-switch-user-id".equals(cmd)) {
+                return mService.getLastSwitchUserId(this);
+            }
+
             // For existing "adb shell ime <command>".
             if ("ime".equals(cmd)) {
                 final String imeCommand = getNextArg();
@@ -4516,6 +4664,15 @@
     // ----------------------------------------------------------------------
     // Shell command handlers:
 
+    @BinderThread
+    @ShellCommandResult
+    private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) {
+        synchronized (mMethodMap) {
+            shellCommand.getOutPrintWriter().println(mLastSwitchUserId);
+            return ShellCommandResult.SUCCESS;
+        }
+    }
+
     /**
      * Handles {@code adb shell ime list}.
      * @param shellCommand {@link ShellCommand} object that is handling this command.
@@ -4526,6 +4683,7 @@
     private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) {
         boolean all = false;
         boolean brief = false;
+        int userIdToBeResolved = UserHandle.USER_CURRENT;
         while (true) {
             final String nextOption = shellCommand.getNextOption();
             if (nextOption == null) {
@@ -4538,19 +4696,34 @@
                 case "-s":
                     brief = true;
                     break;
+                case "-u":
+                case "--user":
+                    userIdToBeResolved = UserHandle.parseUserArg(shellCommand.getNextArgRequired());
+                    break;
             }
         }
-        final List<InputMethodInfo> methods = all ?
-                getInputMethodList() : getEnabledInputMethodList();
-        final PrintWriter pr = shellCommand.getOutPrintWriter();
-        final Printer printer = x -> pr.println(x);
-        final int N = methods.size();
-        for (int i = 0; i < N; ++i) {
-            if (brief) {
-                pr.println(methods.get(i).getId());
-            } else {
-                pr.print(methods.get(i).getId()); pr.println(":");
-                methods.get(i).dump(printer, "  ");
+        synchronized (mMethodMap) {
+            final PrintWriter pr = shellCommand.getOutPrintWriter();
+            final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
+                    mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
+            for (int userId : userIds) {
+                final List<InputMethodInfo> methods = all
+                        ? getInputMethodListLocked(false, userId)
+                        : getEnabledInputMethodListLocked(userId);
+                if (userIds.length > 1) {
+                    pr.print("User #");
+                    pr.print(userId);
+                    pr.println(":");
+                }
+                for (InputMethodInfo info : methods) {
+                    if (brief) {
+                        pr.println(info.getId());
+                    } else {
+                        pr.print(info.getId());
+                        pr.println(":");
+                        info.dump(pr::println, "  ");
+                    }
+                }
             }
         }
         return ShellCommandResult.SUCCESS;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 1137bf9..88d1a9c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -29,21 +29,27 @@
 import android.os.Build;
 import android.os.LocaleList;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.IntArray;
 import android.util.Pair;
 import android.util.Printer;
 import android.util.Slog;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSystemProperty;
 import android.view.textservice.SpellCheckerInfo;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.StartInputFlags;
+import com.android.server.LocalServices;
 import com.android.server.textservices.TextServicesManagerInternal;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedHashSet;
@@ -1286,4 +1292,62 @@
         return true;
     }
 
+    /**
+     * Converts a user ID, which can be a pseudo user ID such as {@link UserHandle#USER_ALL} to a
+     * list of real user IDs.
+     *
+     * <p>This method also converts profile user ID to profile parent user ID unless
+     * {@link InputMethodSystemProperty#PER_PROFILE_IME_ENABLED} is {@code true}.</p>
+     *
+     * @param userIdToBeResolved A user ID. Two pseudo user ID {@link UserHandle#USER_CURRENT} and
+     *                           {@link UserHandle#USER_ALL} are also supported
+     * @param currentUserId A real user ID, which will be used when {@link UserHandle#USER_CURRENT}
+     *                      is specified in {@code userIdToBeResolved}.
+     * @param warningWriter A {@link PrintWriter} to output some debug messages. {@code null} if
+     *                      no debug message is required.
+     * @return An integer array that contain user IDs.
+     */
+    static int[] resolveUserId(@UserIdInt int userIdToBeResolved,
+            @UserIdInt int currentUserId, @Nullable PrintWriter warningWriter) {
+        final UserManagerInternal userManagerInternal =
+                LocalServices.getService(UserManagerInternal.class);
+
+        if (userIdToBeResolved == UserHandle.USER_ALL) {
+            if (InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
+                return userManagerInternal.getUserIds();
+            }
+            final IntArray result = new IntArray();
+            for (int userId : userManagerInternal.getUserIds()) {
+                final int parentUserId = userManagerInternal.getProfileParentId(userId);
+                if (result.indexOf(parentUserId) < 0) {
+                    result.add(parentUserId);
+                }
+            }
+            return result.toArray();
+        }
+
+        final int sourceUserId;
+        if (userIdToBeResolved == UserHandle.USER_CURRENT) {
+            sourceUserId = currentUserId;
+        } else if (userIdToBeResolved < 0) {
+            if (warningWriter != null) {
+                warningWriter.print("Pseudo user ID ");
+                warningWriter.print(userIdToBeResolved);
+                warningWriter.println(" is not supported.");
+            }
+            return new int[]{};
+        } else if (userManagerInternal.exists(userIdToBeResolved)) {
+            sourceUserId = userIdToBeResolved;
+        } else {
+            if (warningWriter != null) {
+                warningWriter.print("User #");
+                warningWriter.print(userIdToBeResolved);
+                warningWriter.println(" does not exit.");
+            }
+            return new int[]{};
+        }
+        final int resolvedUserId = InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
+                ? sourceUserId : userManagerInternal.getProfileParentId(sourceUserId);
+        return new int[]{resolvedUserId};
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 98ed3ea..f304ceb 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -163,6 +163,18 @@
                         public void startVrInputMethodNoCheck(ComponentName componentName) {
                             reportNotSupported();
                         }
+
+                        @Override
+                        public List<InputMethodInfo> getInputMethodListAsUser(
+                                @UserIdInt int userId) {
+                            return userIdToInputMethodInfoMapper.getAsList(userId);
+                        }
+
+                        @Override
+                        public List<InputMethodInfo> getEnabledInputMethodListAsUser(
+                                @UserIdInt int userId) {
+                            return userIdToInputMethodInfoMapper.getAsList(userId);
+                        }
                     });
         }
 
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
index 827c0f1..4d9b5f5 100644
--- a/services/core/java/com/android/server/job/JobConcurrencyManager.java
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -18,16 +18,34 @@
 
 import android.app.ActivityManager;
 import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.StatLogger;
+import com.android.server.job.JobSchedulerService.Constants;
 import com.android.server.job.controllers.JobStatus;
 import com.android.server.job.controllers.StateController;
 
 import java.util.Iterator;
 import java.util.List;
 
+/**
+ * This class decides, given the various configuration and the system status, how many more jobs
+ * can start.
+ */
 class JobConcurrencyManager {
     private static final String TAG = JobSchedulerService.TAG;
     private static final boolean DEBUG = JobSchedulerService.DEBUG;
@@ -35,6 +53,16 @@
     private final Object mLock;
     private final JobSchedulerService mService;
     private final JobSchedulerService.Constants mConstants;
+    private final Context mContext;
+    private final Handler mHandler;
+
+    private PowerManager mPowerManager;
+
+    private boolean mCurrentInteractiveState;
+    private boolean mEffectiveInteractiveState;
+
+    private long mLastScreenOnRealtime;
+    private long mLastScreenOffRealtime;
 
     private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
 
@@ -50,10 +78,193 @@
 
     int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
 
+    /** Max job counts according to the current system state. */
+    private JobSchedulerService.MaxJobCounts mMaxJobCounts;
+
+    private final JobCountTracker mJobCountTracker = new JobCountTracker();
+
+    /** Current memory trim level. */
+    private int mLastMemoryTrimLevel;
+
+    /** Used to throttle heavy API calls. */
+    private long mNextSystemStateRefreshTime;
+    private static final int SYSTEM_STATE_REFRESH_MIN_INTERVAL = 1000;
+
+    private final StatLogger mStatLogger = new StatLogger(new String[]{
+            "assignJobsToContexts",
+            "refreshSystemState",
+    });
+
+    interface Stats {
+        int ASSIGN_JOBS_TO_CONTEXTS = 0;
+        int REFRESH_SYSTEM_STATE = 1;
+
+        int COUNT = REFRESH_SYSTEM_STATE + 1;
+    }
+
     JobConcurrencyManager(JobSchedulerService service) {
         mService = service;
         mLock = mService.mLock;
         mConstants = service.mConstants;
+        mContext = service.getContext();
+
+        mHandler = BackgroundThread.getHandler();
+    }
+
+    public void onSystemReady() {
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+
+        final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        mContext.registerReceiver(mReceiver, filter);
+
+        onInteractiveStateChanged(mPowerManager.isInteractive());
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_SCREEN_ON:
+                    onInteractiveStateChanged(true);
+                    break;
+                case Intent.ACTION_SCREEN_OFF:
+                    onInteractiveStateChanged(false);
+                    break;
+            }
+        }
+    };
+
+    /**
+     * Called when the screen turns on / off.
+     */
+    private void onInteractiveStateChanged(boolean interactive) {
+        synchronized (mLock) {
+            if (mCurrentInteractiveState == interactive) {
+                return;
+            }
+            mCurrentInteractiveState = interactive;
+            if (DEBUG) {
+                Slog.d(TAG, "Interactive: " + interactive);
+            }
+
+            final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+            if (interactive) {
+                mLastScreenOnRealtime = now;
+                mEffectiveInteractiveState = true;
+
+                mHandler.removeCallbacks(mRampUpForScreenOff);
+            } else {
+                mLastScreenOffRealtime = now;
+
+                // Set mEffectiveInteractiveState to false after the delay, when we may increase
+                // the concurrency.
+                // We don't need a wakeup alarm here. When there's a pending job, there should
+                // also be jobs running too, meaning the device should be awake.
+
+                // Note: we can't directly do postDelayed(this::rampUpForScreenOn), because
+                // we need the exact same instance for removeCallbacks().
+                mHandler.postDelayed(mRampUpForScreenOff,
+                        mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue());
+            }
+        }
+    }
+
+    private final Runnable mRampUpForScreenOff = this::rampUpForScreenOff;
+
+    /**
+     * Called in {@link Constants#SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS} after
+     * the screen turns off, in order to increase concurrency.
+     */
+    private void rampUpForScreenOff() {
+        synchronized (mLock) {
+            // Make sure the screen has really been off for the configured duration.
+            // (There could be a race.)
+            if (!mEffectiveInteractiveState) {
+                return;
+            }
+            if (mLastScreenOnRealtime > mLastScreenOffRealtime) {
+                return;
+            }
+            final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+            if ((mLastScreenOffRealtime
+                    + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue())
+                    > now) {
+                return;
+            }
+
+            mEffectiveInteractiveState = false;
+
+            if (DEBUG) {
+                Slog.d(TAG, "Ramping up concurrency");
+            }
+
+            mService.maybeRunPendingJobsLocked();
+        }
+    }
+
+    private boolean isFgJob(JobStatus job) {
+        return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP;
+    }
+
+    @GuardedBy("mLock")
+    private void refreshSystemStateLocked() {
+        final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis();
+
+        // Only refresh the information every so often.
+        if (nowUptime < mNextSystemStateRefreshTime) {
+            return;
+        }
+
+        final long start = mStatLogger.getTime();
+        mNextSystemStateRefreshTime = nowUptime + SYSTEM_STATE_REFRESH_MIN_INTERVAL;
+
+        mLastMemoryTrimLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+        try {
+            mLastMemoryTrimLevel = ActivityManager.getService().getMemoryTrimLevel();
+        } catch (RemoteException e) {
+        }
+
+        mStatLogger.logDurationStat(Stats.REFRESH_SYSTEM_STATE, start);
+    }
+
+    @GuardedBy("mLock")
+    private void updateMaxCountsLocked() {
+        refreshSystemStateLocked();
+
+        if (mEffectiveInteractiveState) {
+            // Screen on
+            switch (mLastMemoryTrimLevel) {
+                case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_MODERATE;
+                    break;
+                case ProcessStats.ADJ_MEM_FACTOR_LOW:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_LOW;
+                    break;
+                case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_CRITICAL;
+                    break;
+                default:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_NORMAL;
+                    break;
+            }
+        } else {
+            // Screen off
+            switch (mLastMemoryTrimLevel) {
+                case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_MODERATE;
+                    break;
+                case ProcessStats.ADJ_MEM_FACTOR_LOW:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_LOW;
+                    break;
+                case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_CRITICAL;
+                    break;
+                default:
+                    mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_NORMAL;
+                    break;
+            }
+        }
     }
 
     /**
@@ -62,7 +273,17 @@
      * run higher priority ones.
      * Lock on mJobs before calling this function.
      */
+    @GuardedBy("mLock")
     void assignJobsToContextsLocked() {
+        final long start = mStatLogger.getTime();
+
+        assignJobsToContextsInternalLocked();
+
+        mStatLogger.logDurationStat(Stats.ASSIGN_JOBS_TO_CONTEXTS, start);
+    }
+
+    @GuardedBy("mLock")
+    private void assignJobsToContextsInternalLocked() {
         if (DEBUG) {
             Slog.d(TAG, printPendingQueueLocked());
         }
@@ -72,60 +293,63 @@
         final List<JobServiceContext> activeServices = mService.mActiveServices;
         final List<StateController> controllers = mService.mControllers;
 
-        int memLevel;
-        try {
-            memLevel = ActivityManager.getService().getMemoryTrimLevel();
-        } catch (RemoteException e) {
-            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
-        }
-        switch (memLevel) {
-            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
-                mService.mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
-                break;
-            case ProcessStats.ADJ_MEM_FACTOR_LOW:
-                mService.mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
-                break;
-            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
-                mService.mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
-                break;
-            default:
-                mService.mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
-                break;
-        }
+        updateMaxCountsLocked();
 
         // To avoid GC churn, we recycle the arrays.
         JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
         boolean[] slotChanged = mRecycledSlotChanged;
         int[] preferredUidForContext = mRecycledPreferredUidForContext;
 
-        int numTotalRunningJobs = 0;
-        int numForegroundJobs = 0;
+
+        // Initialize the work variables and also count running jobs.
+        mJobCountTracker.reset(
+                mMaxJobCounts.getTotalMax(),
+                mMaxJobCounts.getMaxBg(),
+                mMaxJobCounts.getMinBg());
+
         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
             final JobServiceContext js = mService.mActiveServices.get(i);
             final JobStatus status = js.getRunningJobLocked();
+
             if ((contextIdToJobMap[i] = status) != null) {
-                numTotalRunningJobs++;
-                if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
-                    numForegroundJobs++;
-                }
+                mJobCountTracker.incrementRunningJobCount(isFgJob(status));
             }
+
             slotChanged[i] = false;
             preferredUidForContext[i] = js.getPreferredUid();
         }
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
         }
-        for (int i=0; i<pendingJobs.size(); i++) {
-            final JobStatus nextPending = pendingJobs.get(i);
+
+        // Next, update the job priorities, and also count the pending FG / BG jobs.
+        for (int i = 0; i < pendingJobs.size(); i++) {
+            final JobStatus pending = pendingJobs.get(i);
 
             // If job is already running, go to next job.
+            int jobRunningContext = findJobContextIdFromMap(pending, contextIdToJobMap);
+            if (jobRunningContext != -1) {
+                continue;
+            }
+
+            final int priority = mService.evaluateJobPriorityLocked(pending);
+            pending.lastEvaluatedPriority = priority;
+
+            mJobCountTracker.incrementPendingJobCount(isFgJob(pending));
+        }
+
+        mJobCountTracker.onCountDone();
+
+        for (int i = 0; i < pendingJobs.size(); i++) {
+            final JobStatus nextPending = pendingJobs.get(i);
+
+            // Unfortunately we need to repeat this relatively expensive check.
             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
             if (jobRunningContext != -1) {
                 continue;
             }
 
-            final int priority = mService.evaluateJobPriorityLocked(nextPending);
-            nextPending.lastEvaluatedPriority = priority;
+            final boolean isPendingFg = isFgJob(nextPending);
 
             // Find an available slot for nextPending. The context should be available OR
             // it should have lowest priority among all running jobs
@@ -137,18 +361,10 @@
                 JobStatus job = contextIdToJobMap[j];
                 int preferredUid = preferredUidForContext[j];
                 if (job == null) {
-                    final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs;
-                    final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP)
-                            && (numForegroundJobs < mConstants.FG_JOB_COUNT);
                     final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
                             || (preferredUid == JobServiceContext.NO_PREFERRED_UID);
 
-                    // TODO: The following check is slightly wrong.
-                    // Depending on how the pending jobs are sorted, we sometimes cap the total
-                    // job count at mMaxActiveJobs (when all jobs are FG jobs), or
-                    // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs
-                    // and then FG_JOB_COUNT FG jobs.)
-                    if ((totalCountOk || fgCountOk) && preferredUidOkay) {
+                    if (preferredUidOkay && mJobCountTracker.canJobStart(isPendingFg)) {
                         // This slot is free, and we haven't yet hit the limit on
                         // concurrent jobs...  we can just throw the job in to here.
                         selectedContextId = j;
@@ -183,16 +399,18 @@
             }
             if (startingJob) {
                 // Increase the counters when we're going to start a job.
-                numTotalRunningJobs++;
-                if (priority >= JobInfo.PRIORITY_TOP_APP) {
-                    numForegroundJobs++;
-                }
+                mJobCountTracker.onStartingNewJob(isPendingFg);
             }
         }
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
         }
-        tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs);
+
+        mJobCountTracker.logStatus();
+
+        tracker.noteConcurrency(mJobCountTracker.getTotalRunningJobCountToNote(),
+                mJobCountTracker.getFgRunningJobCountToNote());
+
         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
             boolean preservePreferredUid = false;
             if (slotChanged[i]) {
@@ -228,7 +446,7 @@
         }
     }
 
-    int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
+    private static int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
         for (int i=0; i<map.length; i++) {
             if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
                 return i;
@@ -237,6 +455,7 @@
         return -1;
     }
 
+    @GuardedBy("mLock")
     private String printPendingQueueLocked() {
         StringBuilder s = new StringBuilder("Pending queue: ");
         Iterator<JobStatus> it = mService.mPendingJobs.iterator();
@@ -251,7 +470,7 @@
         return s.toString();
     }
 
-    private String printContextIdToJobMap(JobStatus[] map, String initial) {
+    private static String printContextIdToJobMap(JobStatus[] map, String initial) {
         StringBuilder s = new StringBuilder(initial + ": ");
         for (int i=0; i<map.length; i++) {
             s.append("(")
@@ -262,4 +481,222 @@
         return s.toString();
     }
 
+
+    public void dumpLocked(IndentingPrintWriter pw) {
+        final long now = System.currentTimeMillis();
+        final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        pw.println("Concurrency:");
+
+        pw.increaseIndent();
+        try {
+            pw.print("Screen state: current ");
+            pw.print(mCurrentInteractiveState ? "ON" : "OFF");
+            pw.print("  effective ");
+            pw.print(mEffectiveInteractiveState ? "ON" : "OFF");
+            pw.println();
+
+            pw.print("Last screen ON : ");
+            TimeUtils.dumpTimeWithDelta(pw, now - nowRealtime + mLastScreenOnRealtime, now);
+            pw.println();
+
+            pw.print("Last screen OFF: ");
+            TimeUtils.dumpTimeWithDelta(pw, now - nowRealtime + mLastScreenOffRealtime, now);
+            pw.println();
+
+            pw.println();
+
+            pw.println("Current max jobs:");
+            pw.println("  ");
+            pw.println(mJobCountTracker);
+
+            pw.println();
+
+            pw.print("mLastMemoryTrimLevel: ");
+            pw.print(mLastMemoryTrimLevel);
+            pw.println();
+
+            mStatLogger.dump(pw);
+        } finally {
+            pw.decreaseIndent();
+        }
+    }
+
+    public void dumpProtoLocked(ProtoOutputStream proto) {
+        // TODO Implement it.
+    }
+
+    /**
+     * This class decides, taking into account {@link #mMaxJobCounts} and how many jos are running /
+     * pending, how many more job can start.
+     *
+     * Extracted for testing and logging.
+     */
+    @VisibleForTesting
+    static class JobCountTracker {
+        private int mConfigNumTotalMaxJobs;
+        private int mConfigNumMaxBgJobs;
+        private int mConfigNumMinBgJobs;
+
+        private int mNumRunningFgJobs;
+        private int mNumRunningBgJobs;
+
+        private int mNumPendingFgJobs;
+        private int mNumPendingBgJobs;
+
+        private int mNumStartingFgJobs;
+        private int mNumStartingBgJobs;
+
+        private int mNumReservedForBg;
+        private int mNumActualMaxFgJobs;
+        private int mNumActualMaxBgJobs;
+
+        void reset(int numTotalMaxJobs, int numMaxBgJobs, int numMinBgJobs) {
+            mConfigNumTotalMaxJobs = numTotalMaxJobs;
+            mConfigNumMaxBgJobs = numMaxBgJobs;
+            mConfigNumMinBgJobs = numMinBgJobs;
+
+            mNumRunningFgJobs = 0;
+            mNumRunningBgJobs = 0;
+
+            mNumPendingFgJobs = 0;
+            mNumPendingBgJobs = 0;
+
+            mNumStartingFgJobs = 0;
+            mNumStartingBgJobs = 0;
+
+            mNumReservedForBg = 0;
+            mNumActualMaxFgJobs = 0;
+            mNumActualMaxBgJobs = 0;
+        }
+
+        void incrementRunningJobCount(boolean isFg) {
+            if (isFg) {
+                mNumRunningFgJobs++;
+            } else {
+                mNumRunningBgJobs++;
+            }
+        }
+
+        void incrementPendingJobCount(boolean isFg) {
+            if (isFg) {
+                mNumPendingFgJobs++;
+            } else {
+                mNumPendingBgJobs++;
+            }
+        }
+
+        void onStartingNewJob(boolean isFg) {
+            if (isFg) {
+                mNumStartingFgJobs++;
+            } else {
+                mNumStartingBgJobs++;
+            }
+        }
+
+        void onCountDone() {
+            // Note some variables are used only here but are made class members in order to have
+            // them on logcat / dumpsys.
+
+            // How many slots should we allocate to BG jobs at least?
+            // That's basically "getMinBg()", but if there are less jobs, decrease it.
+            // (e.g. even if min-bg is 2, if there's only 1 running+pending job, this has to be 1.)
+            final int reservedForBg = Math.min(
+                    mConfigNumMinBgJobs,
+                    mNumRunningBgJobs + mNumPendingBgJobs);
+
+            // However, if there are FG jobs already running, we have to adjust it.
+            mNumReservedForBg = Math.min(reservedForBg,
+                    mConfigNumTotalMaxJobs - mNumRunningFgJobs);
+
+            // Max FG is [total - [number needed for BG jobs]]
+            // [number needed for BG jobs] is the bigger one of [running BG] or [reserved BG]
+            final int maxFg =
+                    mConfigNumTotalMaxJobs - Math.max(mNumRunningBgJobs, mNumReservedForBg);
+
+            // The above maxFg is the theoretical max. If there are less FG jobs, the actual
+            // max FG will be lower accordingly.
+            mNumActualMaxFgJobs = Math.min(
+                    maxFg,
+                    mNumRunningFgJobs + mNumPendingFgJobs);
+
+            // Max BG is [total - actual max FG], but cap at [config max BG].
+            final int maxBg = Math.min(
+                    mConfigNumMaxBgJobs,
+                    mConfigNumTotalMaxJobs - mNumActualMaxFgJobs);
+
+            // If there are less BG jobs than maxBg, then reduce the actual max BG accordingly.
+            // This isn't needed for the logic to work, but this will give consistent output
+            // on logcat and dumpsys.
+            mNumActualMaxBgJobs = Math.min(
+                    maxBg,
+                    mNumRunningBgJobs + mNumPendingBgJobs);
+        }
+
+        boolean canJobStart(boolean isFg) {
+            if (isFg) {
+                return mNumRunningFgJobs + mNumStartingFgJobs < mNumActualMaxFgJobs;
+            } else {
+                return mNumRunningBgJobs + mNumStartingBgJobs < mNumActualMaxBgJobs;
+            }
+        }
+
+        public int getNumStartingFgJobs() {
+            return mNumStartingFgJobs;
+        }
+
+        public int getNumStartingBgJobs() {
+            return mNumStartingBgJobs;
+        }
+
+        int getTotalRunningJobCountToNote() {
+            return mNumRunningFgJobs + mNumRunningBgJobs
+                    + mNumStartingFgJobs + mNumStartingBgJobs;
+        }
+
+        int getFgRunningJobCountToNote() {
+            return mNumRunningFgJobs + mNumStartingFgJobs;
+        }
+
+        void logStatus() {
+            if (DEBUG) {
+                Slog.d(TAG, "assignJobsToContexts: " + this);
+            }
+        }
+
+        public String toString() {
+            final int totalFg = mNumRunningFgJobs + mNumStartingFgJobs;
+            final int totalBg = mNumRunningBgJobs + mNumStartingBgJobs;
+            return String.format(
+                    "Config={tot=%d bg min/max=%d/%d}"
+                            + " Running: %d / %d (%d)"
+                            + " Pending: %d / %d (%d)"
+                            + " Actual max: %d%s / %d%s (%d%s)"
+                            + " Starting: %d / %d (%d)"
+                            + " Total: %d%s / %d%s (%d%s)",
+                    mConfigNumTotalMaxJobs,
+                    mConfigNumMinBgJobs,
+                    mConfigNumMaxBgJobs,
+
+                    mNumRunningFgJobs, mNumRunningBgJobs,
+                    mNumRunningFgJobs + mNumRunningBgJobs,
+
+                    mNumPendingFgJobs, mNumPendingBgJobs,
+                    mNumPendingFgJobs + mNumPendingBgJobs,
+
+                    mNumActualMaxFgJobs, (totalFg <= mConfigNumTotalMaxJobs) ? "" : "*",
+                    mNumActualMaxBgJobs, (totalBg <= mConfigNumMaxBgJobs) ? "" : "*",
+
+                    mNumActualMaxFgJobs + mNumActualMaxBgJobs,
+                    (mNumActualMaxFgJobs + mNumActualMaxBgJobs <= mConfigNumTotalMaxJobs)
+                            ? "" : "*",
+
+                    mNumStartingFgJobs, mNumStartingBgJobs, mNumStartingFgJobs + mNumStartingBgJobs,
+
+                    totalFg, (totalFg <= mNumActualMaxFgJobs) ? "" : "*",
+                    totalBg, (totalBg <= mNumActualMaxBgJobs) ? "" : "*",
+                    totalFg + totalBg, (totalFg + totalBg <= mConfigNumTotalMaxJobs) ? "" : "*"
+            );
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 3f9d928..cc0ac9a 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -224,12 +224,6 @@
     volatile boolean mInParole;
 
     /**
-     * Current limit on the number of concurrent JobServiceContext entries we want to
-     * keep actively running a job.
-     */
-    int mMaxActiveJobs = 1;
-
-    /**
      * A mapping of which uids are currently in the foreground to their effective priority.
      */
     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
@@ -332,6 +326,71 @@
         }
     }
 
+    static class MaxJobCounts {
+        private final KeyValueListParser.IntValue mTotal;
+        private final KeyValueListParser.IntValue mMaxBg;
+        private final KeyValueListParser.IntValue mMinBg;
+
+        MaxJobCounts(int totalDefault, String totalKey,
+                int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) {
+            mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
+            mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault);
+            mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault);
+        }
+
+        public void parse(KeyValueListParser parser) {
+            mTotal.parse(parser);
+            mMaxBg.parse(parser);
+            mMinBg.parse(parser);
+
+            if (mTotal.getValue() < 1) {
+                mTotal.setValue(1);
+            } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) {
+                mTotal.setValue(MAX_JOB_CONTEXTS_COUNT);
+            }
+
+            if (mMaxBg.getValue() < 1) {
+                mMaxBg.setValue(1);
+            } else if (mMaxBg.getValue() > mTotal.getValue()) {
+                mMaxBg.setValue(mTotal.getValue());
+            }
+            if (mMinBg.getValue() < 0) {
+                mMinBg.setValue(0);
+            } else {
+                if (mMinBg.getValue() > mMaxBg.getValue()) {
+                    mMinBg.setValue(mMaxBg.getValue());
+                }
+                if (mMinBg.getValue() >= mTotal.getValue()) {
+                    mMinBg.setValue(mTotal.getValue() - 1);
+                }
+            }
+        }
+
+        public int getTotalMax() {
+            return mTotal.getValue();
+        }
+
+        public int getMaxBg() {
+            return mMaxBg.getValue();
+        }
+
+        public int getMinBg() {
+            return mMinBg.getValue();
+        }
+
+        public void dump(PrintWriter pw, String prefix) {
+            mTotal.dump(pw, prefix);
+            mMaxBg.dump(pw, prefix);
+            mMinBg.dump(pw, prefix);
+        }
+
+        public void dumpProto(ProtoOutputStream proto, long tagTotal, long tagBg) {
+            mTotal.dumpProto(proto, tagTotal);
+            mMaxBg.dumpProto(proto, tagBg);
+            mMinBg.dumpProto(proto, tagBg);
+        }
+    }
+
     /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
@@ -348,11 +407,14 @@
         private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
-        private static final String KEY_FG_JOB_COUNT = "fg_job_count";
-        private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
-        private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
-        private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
-        private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
+
+        // The following values used to be used on P and below. Do not reuse them.
+        private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count";
+        private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
+        private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
+        private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
+        private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
+
         private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
                 = "max_standard_reschedule_count";
         private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
@@ -391,11 +453,6 @@
         private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
-        private static final int DEFAULT_FG_JOB_COUNT = 4;
-        private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
-        private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
-        private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
-        private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
         private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
         private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
@@ -468,30 +525,53 @@
          * This is the job execution factor that is considered to be moderate use of the system.
          */
         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
-        /**
-         * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
-         */
-        int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
-        /**
-         * The maximum number of background jobs we allow when the system is in a normal
-         * memory state.
-         */
-        int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
-        /**
-         * The maximum number of background jobs we allow when the system is in a moderate
-         * memory state.
-         */
-        int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
-        /**
-         * The maximum number of background jobs we allow when the system is in a low
-         * memory state.
-         */
-        int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
-        /**
-         * The maximum number of background jobs we allow when the system is in a critical
-         * memory state.
-         */
-        int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
+
+        // Max job counts for screen on / off, for each memory trim level.
+        final MaxJobCounts MAX_JOB_COUNTS_ON_NORMAL = new MaxJobCounts(
+                8, "max_job_total_on_normal",
+                6, "max_job_max_bg_on_normal",
+                2, "max_job_min_bg_on_normal");
+
+        final MaxJobCounts MAX_JOB_COUNTS_ON_MODERATE = new MaxJobCounts(
+                8, "max_job_total_on_moderate",
+                4, "max_job_max_bg_on_moderate",
+                2, "max_job_min_bg_on_moderate");
+
+        final MaxJobCounts MAX_JOB_COUNTS_ON_LOW = new MaxJobCounts(
+                5, "max_job_total_on_low",
+                1, "max_job_max_bg_on_low",
+                1, "max_job_min_bg_on_low");
+
+        final MaxJobCounts MAX_JOB_COUNTS_ON_CRITICAL = new MaxJobCounts(
+                5, "max_job_total_on_critical",
+                1, "max_job_max_bg_on_critical",
+                1, "max_job_min_bg_on_critical");
+
+        final MaxJobCounts MAX_JOB_COUNTS_OFF_NORMAL = new MaxJobCounts(
+                10, "max_job_total_off_normal",
+                6, "max_job_max_bg_off_normal",
+                2, "max_job_min_bg_off_normal");
+
+        final MaxJobCounts MAX_JOB_COUNTS_OFF_MODERATE = new MaxJobCounts(
+                10, "max_job_total_off_moderate",
+                4, "max_job_max_bg_off_moderate",
+                2, "max_job_min_bg_off_moderate");
+
+        final MaxJobCounts MAX_JOB_COUNTS_OFF_LOW = new MaxJobCounts(
+                5, "max_job_total_off_low",
+                1, "max_job_max_bg_off_low",
+                1, "max_job_min_bg_off_low");
+
+        final MaxJobCounts MAX_JOB_COUNTS_OFF_CRITICAL = new MaxJobCounts(
+                5, "max_job_total_off_critical",
+                1, "max_job_max_bg_off_critical",
+                1, "max_job_min_bg_off_critical");
+
+        /** Wait for this long after screen off before increasing the job concurrency. */
+        final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
+                new KeyValueListParser.IntValue(
+                        "screen_off_job_concurrency_increase_delay_ms", 30_000);
+
         /**
          * The maximum number of times we allow a job to have itself rescheduled before
          * giving up on it, for standard jobs.
@@ -566,7 +646,7 @@
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
@@ -574,7 +654,7 @@
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
@@ -582,7 +662,7 @@
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
@@ -590,7 +670,7 @@
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
@@ -631,28 +711,17 @@
                     DEFAULT_HEAVY_USE_FACTOR);
             MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
                     DEFAULT_MODERATE_USE_FACTOR);
-            FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
-                    DEFAULT_FG_JOB_COUNT);
-            BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
-                    DEFAULT_BG_NORMAL_JOB_COUNT);
-            if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
-                BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
-            }
-            BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
-                    DEFAULT_BG_MODERATE_JOB_COUNT);
-            if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
-                BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
-            }
-            BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
-                    DEFAULT_BG_LOW_JOB_COUNT);
-            if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
-                BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
-            }
-            BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
-                    DEFAULT_BG_CRITICAL_JOB_COUNT);
-            if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
-                BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
-            }
+
+            MAX_JOB_COUNTS_ON_NORMAL.parse(mParser);
+            MAX_JOB_COUNTS_ON_MODERATE.parse(mParser);
+            MAX_JOB_COUNTS_ON_LOW.parse(mParser);
+            MAX_JOB_COUNTS_ON_CRITICAL.parse(mParser);
+
+            MAX_JOB_COUNTS_OFF_NORMAL.parse(mParser);
+            MAX_JOB_COUNTS_OFF_MODERATE.parse(mParser);
+            MAX_JOB_COUNTS_OFF_LOW.parse(mParser);
+            MAX_JOB_COUNTS_OFF_CRITICAL.parse(mParser);
+
             MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
                     DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
             MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
@@ -712,11 +781,17 @@
             pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
             pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
             pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
-            pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
-            pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
-            pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
-            pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
-            pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
+
+            MAX_JOB_COUNTS_ON_NORMAL.dump(pw, "");
+            MAX_JOB_COUNTS_ON_MODERATE.dump(pw, "");
+            MAX_JOB_COUNTS_ON_LOW.dump(pw, "");
+            MAX_JOB_COUNTS_ON_CRITICAL.dump(pw, "");
+
+            MAX_JOB_COUNTS_OFF_NORMAL.dump(pw, "");
+            MAX_JOB_COUNTS_OFF_MODERATE.dump(pw, "");
+            MAX_JOB_COUNTS_OFF_LOW.dump(pw, "");
+            MAX_JOB_COUNTS_OFF_CRITICAL.dump(pw, "");
+
             pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
             pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
             pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
@@ -762,11 +837,9 @@
             proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
             proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
             proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
-            proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
-            proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
-            proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
-            proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
-            proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
+
+            // TODO Dump max job counts.
+
             proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
             proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
             proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
@@ -1458,6 +1531,9 @@
             } catch (RemoteException e) {
                 // ignored; both services live in system_server
             }
+
+            mConcurrencyManager.onSystemReady();
+
             // Remove any jobs that are not associated with any of the current users.
             cancelJobsForNonExistentUsers();
             // Register thermal callback
@@ -1585,7 +1661,7 @@
      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
      * specify an override deadline on a failed job (the failed job will run even though it's not
      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
-     * ready job with {@link JobStatus#numFailures} > 0 will be executed.
+     * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed.
      *
      * @param failureToReschedule Provided job status that we will reschedule.
      * @return A newly instantiated JobStatus with the same constraints as the last job except
@@ -2367,7 +2443,7 @@
      * A controller can force a job into the pending queue even if it's already running, but
      * here is where we decide whether to actually execute it.
      */
-    private void maybeRunPendingJobsLocked() {
+    void maybeRunPendingJobsLocked() {
         if (DEBUG) {
             Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
         }
@@ -3380,9 +3456,12 @@
                 pw.println();
                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
                 pw.print("mReportedActive="); pw.println(mReportedActive);
-                pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
             }
             pw.println();
+
+            mConcurrencyManager.dumpLocked(pw);
+
+            pw.println();
             pw.print("PersistStats: ");
             pw.println(mJobs.getPersistStats());
         }
@@ -3534,8 +3613,8 @@
             if (filterUid == -1) {
                 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
                 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
-                proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
             }
+            mConcurrencyManager.dumpProtoLocked(proto);
         }
 
         proto.flush();
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index ac2dbdf..c16d1b4 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -26,7 +26,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
+import android.app.IUidObserver;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
@@ -38,12 +41,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -69,6 +74,11 @@
  * bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately
  * applied to it.
  *
+ * Jobs are throttled while an app is not in a foreground state. All jobs are allowed to run
+ * freely when an app enters the foreground state and are restricted when the app leaves the
+ * foreground state. However, jobs that are started while the app is in the TOP state are not
+ * restricted regardless of the app's state change.
+ *
  * Test: atest com.android.server.job.controllers.QuotaControllerTest
  */
 public final class QuotaController extends StateController {
@@ -97,6 +107,12 @@
             data.put(packageName, obj);
         }
 
+        public void clear() {
+            for (int i = 0; i < mData.size(); ++i) {
+                mData.valueAt(i).clear();
+            }
+        }
+
         /** Removes all the data for the user, if there was any. */
         public void delete(int userId) {
             mData.delete(userId);
@@ -119,6 +135,11 @@
             return null;
         }
 
+        /** @see SparseArray#indexOfKey */
+        public int indexOfKey(int userId) {
+            return mData.indexOfKey(userId);
+        }
+
         /** Returns the userId at the given index. */
         public int keyAt(int index) {
             return mData.keyAt(index);
@@ -294,6 +315,17 @@
     /** Cached calculation results for each app, with the standby buckets as the array indices. */
     private final UserPackageMap<ExecutionStats[]> mExecutionStatsCache = new UserPackageMap<>();
 
+    /** List of UIDs currently in the foreground. */
+    private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+
+    /**
+     * List of jobs that started while the UID was in the TOP state. There will be no more than
+     * 16 ({@link JobSchedulerService.MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
+     * fine.
+     */
+    private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
+
+    private final ActivityManagerInternal mActivityManagerInternal;
     private final AlarmManager mAlarmManager;
     private final ChargingTracker mChargeTracker;
     private final Handler mHandler;
@@ -343,6 +375,29 @@
                 }
             };
 
+    private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+        @Override
+        public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+            mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget();
+        }
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) {
+        }
+
+        @Override
+        public void onUidActive(int uid) {
+        }
+
+        @Override
+        public void onUidIdle(int uid, boolean disabled) {
+        }
+
+        @Override
+        public void onUidCachedChanged(int uid, boolean cached) {
+        }
+    };
+
     /**
      * The rolling window size for each standby bucket. Within each window, an app will have 10
      * minutes to run its jobs.
@@ -363,12 +418,15 @@
     private static final int MSG_CLEAN_UP_SESSIONS = 1;
     /** Check if a package is now within its quota. */
     private static final int MSG_CHECK_PACKAGE = 2;
+    /** Process state for a UID has changed. */
+    private static final int MSG_UID_PROCESS_STATE_CHANGED = 3;
 
     public QuotaController(JobSchedulerService service) {
         super(service);
         mHandler = new QcHandler(mContext.getMainLooper());
         mChargeTracker = new ChargingTracker();
         mChargeTracker.startTracking();
+        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
         // Set up the app standby bucketing tracker
@@ -376,6 +434,14 @@
                 UsageStatsManagerInternal.class);
         usageStats.addAppIdleStateChangeListener(new StandbyTracker());
 
+        try {
+            ActivityManager.getService().registerUidObserver(mUidObserver,
+                    ActivityManager.UID_OBSERVER_PROCSTATE,
+                    ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, null);
+        } catch (RemoteException e) {
+            // ignored; both services live in system_server
+        }
+
         onConstantsUpdatedLocked();
     }
 
@@ -399,11 +465,15 @@
         if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
         final int userId = jobStatus.getSourceUserId();
         final String packageName = jobStatus.getSourcePackageName();
+        final int uid = jobStatus.getSourceUid();
         Timer timer = mPkgTimers.get(userId, packageName);
         if (timer == null) {
-            timer = new Timer(userId, packageName);
+            timer = new Timer(uid, userId, packageName);
             mPkgTimers.add(userId, packageName, timer);
         }
+        if (mActivityManagerInternal.getUidProcessState(uid) == ActivityManager.PROCESS_STATE_TOP) {
+            mTopStartedJobs.add(jobStatus);
+        }
         timer.startTrackingJob(jobStatus);
     }
 
@@ -421,6 +491,7 @@
             if (jobs != null) {
                 jobs.remove(jobStatus);
             }
+            mTopStartedJobs.remove(jobStatus);
         }
     }
 
@@ -511,6 +582,7 @@
             mInQuotaAlarmListeners.delete(userId, packageName);
         }
         mExecutionStatsCache.delete(userId, packageName);
+        mForegroundUids.delete(uid);
     }
 
     @Override
@@ -522,6 +594,20 @@
         mExecutionStatsCache.delete(userId);
     }
 
+    private boolean isUidInForeground(int uid) {
+        if (UserHandle.isCore(uid)) {
+            return true;
+        }
+        synchronized (mLock) {
+            return mForegroundUids.get(uid);
+        }
+    }
+
+    /** @return true if the job was started while the app was in the TOP state. */
+    private boolean isTopStartedJob(@NonNull final JobStatus jobStatus) {
+        return mTopStartedJobs.contains(jobStatus);
+    }
+
     /**
      * Returns an appropriate standby bucket for the job, taking into account any standby
      * exemptions.
@@ -537,9 +623,15 @@
 
     private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
         final int standbyBucket = getEffectiveStandbyBucket(jobStatus);
-        // Jobs for the active app should always be able to run.
-        return jobStatus.uidActive || isWithinQuotaLocked(
-                jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
+        Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+        // A job is within quota if one of the following is true:
+        //   1. it was started while the app was in the TOP state
+        //   2. the app is currently in the foreground
+        //   3. the app overall is within its quota
+        return isTopStartedJob(jobStatus)
+                || isUidInForeground(jobStatus.getSourceUid())
+                || isWithinQuotaLocked(
+                      jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
     }
 
     private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
@@ -800,7 +892,7 @@
         final boolean isCharging = mChargeTracker.isCharging();
         if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
         // Deal with Timers first.
-        mPkgTimers.forEach((t) -> t.onChargingChanged(nowElapsed, isCharging));
+        mPkgTimers.forEach((t) -> t.onStateChanged(nowElapsed, isCharging));
         // Now update jobs.
         maybeUpdateAllConstraintsLocked();
     }
@@ -837,10 +929,15 @@
         boolean changed = false;
         for (int i = jobs.size() - 1; i >= 0; --i) {
             final JobStatus js = jobs.valueAt(i);
-            if (js.uidActive) {
-                // Jobs for the active app should always be able to run.
+            if (isTopStartedJob(js)) {
+                // Job was started while the app was in the TOP state so we should allow it to
+                // finish.
                 changed |= js.setQuotaConstraintSatisfied(true);
-            } else if (realStandbyBucket == getEffectiveStandbyBucket(js)) {
+            } else if (realStandbyBucket != ACTIVE_INDEX
+                    && realStandbyBucket == getEffectiveStandbyBucket(js)) {
+                // An app in the ACTIVE bucket may be out of quota while the job could be in quota
+                // for some reason. Therefore, avoid setting the real value here and check each job
+                // individually.
                 changed |= js.setQuotaConstraintSatisfied(realInQuota);
             } else {
                 // This job is somehow exempted. Need to determine its own quota status.
@@ -854,7 +951,7 @@
             maybeScheduleStartAlarmLocked(userId, packageName, realStandbyBucket);
         } else {
             QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
-            if (alarmListener != null) {
+            if (alarmListener != null && alarmListener.isWaiting()) {
                 mAlarmManager.cancel(alarmListener);
                 // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
                 alarmListener.setTriggerTime(0);
@@ -863,6 +960,56 @@
         return changed;
     }
 
+    private class UidConstraintUpdater implements Consumer<JobStatus> {
+        private final UserPackageMap<Integer> mToScheduleStartAlarms = new UserPackageMap<>();
+        public boolean wasJobChanged;
+
+        @Override
+        public void accept(JobStatus jobStatus) {
+            wasJobChanged |= jobStatus.setQuotaConstraintSatisfied(isWithinQuotaLocked(jobStatus));
+            final int userId = jobStatus.getSourceUserId();
+            final String packageName = jobStatus.getSourcePackageName();
+            final int realStandbyBucket = jobStatus.getStandbyBucket();
+            if (isWithinQuotaLocked(userId, packageName, realStandbyBucket)) {
+                QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+                if (alarmListener != null && alarmListener.isWaiting()) {
+                    mAlarmManager.cancel(alarmListener);
+                    // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
+                    alarmListener.setTriggerTime(0);
+                }
+            } else {
+                mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
+            }
+        }
+
+        void postProcess() {
+            for (int u = 0; u < mToScheduleStartAlarms.numUsers(); ++u) {
+                final int userId = mToScheduleStartAlarms.keyAt(u);
+                for (int p = 0; p < mToScheduleStartAlarms.numPackagesForUser(userId); ++p) {
+                    final String packageName = mToScheduleStartAlarms.keyAt(u, p);
+                    final int standbyBucket = mToScheduleStartAlarms.get(userId, packageName);
+                    maybeScheduleStartAlarmLocked(userId, packageName, standbyBucket);
+                }
+            }
+        }
+
+        void reset() {
+            wasJobChanged = false;
+            mToScheduleStartAlarms.clear();
+        }
+    }
+
+    private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater();
+
+    private boolean maybeUpdateConstraintForUidLocked(final int uid) {
+        mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints);
+
+        mUpdateUidConstraints.postProcess();
+        boolean changed = mUpdateUidConstraints.wasJobChanged;
+        mUpdateUidConstraints.reset();
+        return changed;
+    }
+
     /**
      * Maybe schedule a non-wakeup alarm for the next time this package will have quota to run
      * again. This should only be called if the package is already out of quota.
@@ -1052,6 +1199,7 @@
 
     private final class Timer {
         private final Package mPkg;
+        private final int mUid;
 
         // List of jobs currently running for this app that started when the app wasn't in the
         // foreground.
@@ -1059,16 +1207,18 @@
         private long mStartTimeElapsed;
         private int mBgJobCount;
 
-        Timer(int userId, String packageName) {
+        Timer(int uid, int userId, String packageName) {
             mPkg = new Package(userId, packageName);
+            mUid = uid;
         }
 
         void startTrackingJob(@NonNull JobStatus jobStatus) {
-            if (jobStatus.uidActive) {
-                // We intentionally don't pay attention to fg state changes after a job has started.
+            if (isTopStartedJob(jobStatus)) {
+                // We intentionally don't pay attention to fg state changes after a TOP job has
+                // started.
                 if (DEBUG) {
                     Slog.v(TAG,
-                            "Timer ignoring " + jobStatus.toShortString() + " because uidActive");
+                            "Timer ignoring " + jobStatus.toShortString() + " because isTop");
                 }
                 return;
             }
@@ -1076,7 +1226,7 @@
             synchronized (mLock) {
                 // Always track jobs, even when charging.
                 mRunningBgJobs.add(jobStatus);
-                if (!mChargeTracker.isCharging()) {
+                if (shouldTrackLocked()) {
                     mBgJobCount++;
                     if (mRunningBgJobs.size() == 1) {
                         // Started tracking the first job.
@@ -1142,6 +1292,10 @@
             }
         }
 
+        boolean isRunning(JobStatus jobStatus) {
+            return mRunningBgJobs.contains(jobStatus);
+        }
+
         long getCurrentDuration(long nowElapsed) {
             synchronized (mLock) {
                 return !isActive() ? 0 : nowElapsed - mStartTimeElapsed;
@@ -1154,17 +1308,21 @@
             }
         }
 
-        void onChargingChanged(long nowElapsed, boolean isCharging) {
+        private boolean shouldTrackLocked() {
+            return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid);
+        }
+
+        void onStateChanged(long nowElapsed, boolean isQuotaFree) {
             synchronized (mLock) {
-                if (isCharging) {
+                if (isQuotaFree) {
                     emitSessionLocked(nowElapsed);
-                } else {
+                } else if (shouldTrackLocked()) {
                     // Start timing from unplug.
                     if (mRunningBgJobs.size() > 0) {
                         mStartTimeElapsed = nowElapsed;
                         // NOTE: this does have the unfortunate consequence that if the device is
-                        // repeatedly plugged in and unplugged, the job count for a package may be
-                        // artificially high.
+                        // repeatedly plugged in and unplugged, or an app changes foreground state
+                        // very frequently, the job count for a package may be artificially high.
                         mBgJobCount = mRunningBgJobs.size();
                         // Starting the timer means that all cached execution stats are now
                         // incorrect.
@@ -1371,6 +1529,38 @@
                         }
                         break;
                     }
+                    case MSG_UID_PROCESS_STATE_CHANGED: {
+                        final int uid = msg.arg1;
+                        final int procState = msg.arg2;
+                        final int userId = UserHandle.getUserId(uid);
+                        final long nowElapsed = sElapsedRealtimeClock.millis();
+
+                        synchronized (mLock) {
+                            boolean isQuotaFree;
+                            if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                                mForegroundUids.put(uid, true);
+                                isQuotaFree = true;
+                            } else {
+                                mForegroundUids.delete(uid);
+                                isQuotaFree = false;
+                            }
+                            // Update Timers first.
+                            final int userIndex = mPkgTimers.indexOfKey(userId);
+                            if (userIndex != -1) {
+                                final int numPkgs = mPkgTimers.numPackagesForUser(userId);
+                                for (int p = 0; p < numPkgs; ++p) {
+                                    Timer t = mPkgTimers.valueAt(userIndex, p);
+                                    if (t != null) {
+                                        t.onStateChanged(nowElapsed, isQuotaFree);
+                                    }
+                                }
+                            }
+                            if (maybeUpdateConstraintForUidLocked(uid)) {
+                                mStateChangedListener.onControllerStateChanged();
+                            }
+                        }
+                        break;
+                    }
                 }
             }
         }
@@ -1420,6 +1610,12 @@
 
     @VisibleForTesting
     @NonNull
+    SparseBooleanArray getForegroundUids() {
+        return mForegroundUids;
+    }
+
+    @VisibleForTesting
+    @NonNull
     Handler getHandler() {
         return mHandler;
     }
@@ -1450,6 +1646,10 @@
         pw.println("In parole: " + mInParole);
         pw.println();
 
+        pw.print("Foreground UIDs: ");
+        pw.println(mForegroundUids.toString());
+        pw.println();
+
         mTrackedJobs.forEach((jobs) -> {
             for (int j = 0; j < jobs.size(); j++) {
                 final JobStatus js = jobs.valueAt(j);
@@ -1460,6 +1660,9 @@
                 js.printUniqueId(pw);
                 pw.print(" from ");
                 UserHandle.formatUid(pw, js.getSourceUid());
+                if (mTopStartedJobs.contains(js)) {
+                    pw.print(" (TOP)");
+                }
                 pw.println();
 
                 pw.increaseIndent();
@@ -1511,6 +1714,11 @@
         proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging());
         proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole);
 
+        for (int i = 0; i < mForegroundUids.size(); ++i) {
+            proto.write(StateControllerProto.QuotaController.FOREGROUND_UIDS,
+                    mForegroundUids.keyAt(i));
+        }
+
         mTrackedJobs.forEach((jobs) -> {
             for (int j = 0; j < jobs.size(); j++) {
                 final JobStatus js = jobs.valueAt(j);
@@ -1526,6 +1734,8 @@
                 proto.write(
                         StateControllerProto.QuotaController.TrackedJob.EFFECTIVE_STANDBY_BUCKET,
                         getEffectiveStandbyBucket(js));
+                proto.write(StateControllerProto.QuotaController.TrackedJob.IS_TOP_STARTED_JOB,
+                        mTopStartedJobs.contains(js));
                 proto.write(StateControllerProto.QuotaController.TrackedJob.HAS_QUOTA,
                         js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
                 proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS,
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 4c7c420..b3f1018 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -29,7 +29,7 @@
 import java.util.List;
 
 /**
- * Location Manager's interface for location providers.
+ * Location Manager's interface for location providers. Always starts as disabled.
  *
  * @hide
  */
@@ -41,12 +41,6 @@
     public interface LocationProviderManager {
 
         /**
-         * Called on location provider construction to make the location service aware of this
-         * provider and what it's initial enabled/disabled state should be.
-         */
-        void onAttachProvider(AbstractLocationProvider locationProvider, boolean initiallyEnabled);
-
-        /**
          * May be called to inform the location service of a change in this location provider's
          * enabled/disabled state.
          */
@@ -74,13 +68,7 @@
     private final LocationProviderManager mLocationProviderManager;
 
     protected AbstractLocationProvider(LocationProviderManager locationProviderManager) {
-        this(locationProviderManager, true);
-    }
-
-    protected AbstractLocationProvider(LocationProviderManager locationProviderManager,
-            boolean initiallyEnabled) {
         mLocationProviderManager = locationProviderManager;
-        mLocationProviderManager.onAttachProvider(this, initiallyEnabled);
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index ca4f7fe..38c7d49 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -113,8 +113,12 @@
             IGeofenceHardware geofenceHardware = IGeofenceHardware.Stub.asInterface(service);
 
             try {
-                geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
-                geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
+                if (mGpsGeofenceHardware != null) {
+                    geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
+                }
+                if (mFusedGeofenceHardware != null) {
+                    geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
+                }
 
                 mGeofenceHardware = geofenceHardware;
                 mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
new file mode 100644
index 0000000..7225926
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2019 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.location;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.StatsLog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+/**
+ * A utility class to hold GNSS configuration properties.
+ *
+ * The trigger to load/reload the configuration parameters should be managed by the class
+ * that owns an instance of this class.
+ *
+ * Instances of this class are not thread-safe and should either be used from a single thread
+ * or with external synchronization when used by multiple threads.
+ */
+class GnssConfiguration {
+    private static final String TAG = "GnssConfiguration";
+
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    //TODO(b/33112647): Create gps_debug.conf with commented career parameters.
+    private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf";
+
+    // config.xml properties
+    private static final String CONFIG_SUPL_HOST = "SUPL_HOST";
+    private static final String CONFIG_SUPL_PORT = "SUPL_PORT";
+    private static final String CONFIG_C2K_HOST = "C2K_HOST";
+    private static final String CONFIG_C2K_PORT = "C2K_PORT";
+    private static final String CONFIG_SUPL_VER = "SUPL_VER";
+    private static final String CONFIG_SUPL_MODE = "SUPL_MODE";
+    private static final String CONFIG_SUPL_ES = "SUPL_ES";
+    private static final String CONFIG_LPP_PROFILE = "LPP_PROFILE";
+    private static final String CONFIG_A_GLONASS_POS_PROTOCOL_SELECT =
+            "A_GLONASS_POS_PROTOCOL_SELECT";
+    private static final String CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL =
+            "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL";
+    private static final String CONFIG_GPS_LOCK = "GPS_LOCK";
+    private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC";
+    public static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS";
+
+    // Limit on NI emergency mode time extension after emergency sessions ends
+    private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300;  // 5 minute maximum
+
+    // Persist property for LPP_PROFILE
+    static final String LPP_PROFILE = "persist.sys.gps.lpp";
+
+    // Represents an HAL interface version. Instances of this class are created in the JNI layer
+    // and returned through native methods.
+    private static class HalInterfaceVersion {
+        final int mMajor;
+        final int mMinor;
+
+        HalInterfaceVersion(int major, int minor) {
+            mMajor = major;
+            mMinor = minor;
+        }
+    }
+
+    /**
+     * Properties loaded from PROPERTIES_FILE.
+     */
+    private Properties mProperties;
+
+    private int mEsExtensionSec = 0;
+
+    private final Context mContext;
+
+    GnssConfiguration(Context context) {
+        mContext = context;
+        mProperties = new Properties();
+    }
+
+    /**
+     * Returns the full set of properties loaded.
+     */
+    Properties getProperties() {
+        return mProperties;
+    }
+
+    /**
+     * Returns the value of config parameter ES_EXTENSION_SEC. The value is range checked
+     * and constrained to min/max limits.
+     */
+    int getEsExtensionSec() {
+        return mEsExtensionSec;
+    }
+
+    /**
+     * Returns the value of config parameter SUPL_HOST or {@code null} if no value is
+     * provided.
+     */
+    String getSuplHost() {
+        return mProperties.getProperty(CONFIG_SUPL_HOST);
+    }
+
+    /**
+     * Returns the value of config parameter SUPL_PORT or {@code defaultPort} if no value is
+     * provided or if there is an error parsing the configured value.
+     */
+    int getSuplPort(int defaultPort) {
+        return getIntConfig(CONFIG_SUPL_PORT, defaultPort);
+    }
+
+    /**
+     * Returns the value of config parameter C2K_HOST or {@code null} if no value is
+     * provided.
+     */
+    String getC2KHost() {
+        return mProperties.getProperty(CONFIG_C2K_HOST);
+    }
+
+    /**
+     * Returns the value of config parameter C2K_PORT or {@code defaultPort} if no value is
+     * provided or if there is an error parsing the configured value.
+     */
+    int getC2KPort(int defaultPort) {
+        return getIntConfig(CONFIG_C2K_PORT, defaultPort);
+    }
+
+    /**
+     * Returns the value of config parameter SUPL_MODE or {@code defaultMode} if no value is
+     * provided or if there is an error parsing the configured value.
+     */
+    int getSuplMode(int defaultMode) {
+        return getIntConfig(CONFIG_SUPL_MODE, defaultMode);
+    }
+
+    /**
+     * Returns the value of config parameter SUPL_ES or {@code defaultSuplEs} if no value is
+     * provided or if there is an error parsing the configured value.
+     */
+    int getSuplEs(int defaulSuplEs) {
+        return getIntConfig(CONFIG_SUPL_ES, defaulSuplEs);
+    }
+
+    /**
+     * Returns the value of config parameter LPP_PROFILE or {@code null} if no value is
+     * provided.
+     */
+    String getLppProfile() {
+        return mProperties.getProperty(CONFIG_LPP_PROFILE);
+    }
+
+    /**
+     * Returns the list of proxy apps from the value of config parameter NFW_PROXY_APPS or
+     * {@Collections.EMPTY_LIST} if no value is provided.
+     */
+    List<String> getProxyApps() {
+        // Space separated list of Android proxy app package names.
+        String proxyAppsStr = mProperties.getProperty(CONFIG_NFW_PROXY_APPS);
+        if (TextUtils.isEmpty(proxyAppsStr)) {
+            return Collections.EMPTY_LIST;
+        }
+
+        String[] proxyAppsArray = proxyAppsStr.trim().split("\\s+");
+        if (proxyAppsArray.length == 0) {
+            return Collections.EMPTY_LIST;
+        }
+
+        // TODO(b/122856486): Validate proxy app names so that a system app or some popular app
+        //                    with location permission is not specified as a proxy app.
+        ArrayList proxyApps = new ArrayList(proxyAppsArray.length);
+        for (String proxyApp : proxyAppsArray) {
+            proxyApps.add(proxyApp);
+        }
+
+        return proxyApps;
+    }
+
+    /**
+     * Updates the GNSS HAL satellite blacklist.
+     */
+    void setSatelliteBlacklist(int[] constellations, int[] svids) {
+        native_set_satellite_blacklist(constellations, svids);
+    }
+
+    interface SetCarrierProperty {
+        boolean set(int value);
+    }
+
+    /**
+     * Loads the GNSS properties from carrier config file followed by the properties from
+     * gps debug config file.
+     */
+    void reloadGpsProperties() {
+        if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + mProperties.size());
+        loadPropertiesFromCarrierConfig();
+
+        String lpp_prof = SystemProperties.get(LPP_PROFILE);
+        if (!TextUtils.isEmpty(lpp_prof)) {
+            // override default value of this if lpp_prof is not empty
+            mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof);
+        }
+        /*
+         * Overlay carrier properties from a debug configuration file.
+         */
+        loadPropertiesFromGpsDebugConfig(mProperties);
+
+        mEsExtensionSec = getRangeCheckedConfigEsExtensionSec();
+
+        logConfigurations();
+
+        final HalInterfaceVersion gnssConfigurationIfaceVersion =
+                native_get_gnss_configuration_version();
+        if (gnssConfigurationIfaceVersion != null) {
+            // Set to a range checked value.
+            if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion)
+                    && !native_set_es_extension_sec(mEsExtensionSec)) {
+                Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec);
+            }
+
+            Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
+                {
+                    put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version);
+                    put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode);
+
+                    if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) {
+                        put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es);
+                    }
+
+                    put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile);
+                    put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT,
+                            GnssConfiguration::native_set_gnss_pos_protocol_select);
+                    put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL,
+                            GnssConfiguration::native_set_emergency_supl_pdn);
+
+                    if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) {
+                        put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock);
+                    }
+                }
+            };
+
+            for (Entry<String, SetCarrierProperty> entry : map.entrySet()) {
+                String propertyName = entry.getKey();
+                String propertyValueString = mProperties.getProperty(propertyName);
+                if (propertyValueString != null) {
+                    try {
+                        int propertyValueInt = Integer.decode(propertyValueString);
+                        boolean result = entry.getValue().set(propertyValueInt);
+                        if (!result) {
+                            Log.e(TAG, "Unable to set " + propertyName);
+                        }
+                    } catch (NumberFormatException e) {
+                        Log.e(TAG, "Unable to parse propertyName: " + propertyValueString);
+                    }
+                }
+            }
+        } else if (DEBUG) {
+            Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
+                    + " supported");
+        }
+    }
+
+    private void logConfigurations() {
+        StatsLog.write(StatsLog.GNSS_CONFIGURATION_REPORTED,
+                getSuplHost(),
+                getSuplPort(0),
+                getC2KHost(),
+                getC2KPort(0),
+                getIntConfig(CONFIG_SUPL_VER, 0),
+                getSuplMode(0),
+                getSuplEs(0) == 1,
+                getIntConfig(CONFIG_LPP_PROFILE, 0),
+                getIntConfig(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 0),
+                getIntConfig(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 0) == 1,
+                getIntConfig(CONFIG_GPS_LOCK, 0),
+                getEsExtensionSec(),
+                mProperties.getProperty(CONFIG_NFW_PROXY_APPS));
+    }
+
+    /**
+     * Loads GNSS properties from carrier config file.
+     */
+    void loadPropertiesFromCarrierConfig() {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            return;
+        }
+        PersistableBundle configs = configManager.getConfigForSubId(
+                SubscriptionManager.getDefaultDataSubscriptionId());
+        if (configs == null) {
+            if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
+            configs = CarrierConfigManager.getDefaultConfig();
+        }
+        for (String configKey : configs.keySet()) {
+            if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
+                String key = configKey
+                        .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
+                        .toUpperCase();
+                Object value = configs.get(configKey);
+                if (value instanceof String) {
+                    // All GPS properties are of String type; convert so.
+                    if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
+                    mProperties.setProperty(key, (String) value);
+                }
+            }
+        }
+    }
+
+    private void loadPropertiesFromGpsDebugConfig(Properties properties) {
+        try {
+            File file = new File(DEBUG_PROPERTIES_FILE);
+            FileInputStream stream = null;
+            try {
+                stream = new FileInputStream(file);
+                properties.load(stream);
+            } finally {
+                IoUtils.closeQuietly(stream);
+            }
+        } catch (IOException e) {
+            if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
+        }
+    }
+
+    private int getRangeCheckedConfigEsExtensionSec() {
+        int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0);
+        if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
+            Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds
+                    + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
+            emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
+        } else if (emergencyExtensionSeconds < 0) {
+            Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds
+                    + " is negative, reset to zero.");
+            emergencyExtensionSeconds = 0;
+        }
+        return emergencyExtensionSeconds;
+    }
+
+    private int getIntConfig(String configParameter, int defaultValue) {
+        String valueString = mProperties.getProperty(configParameter);
+        if (TextUtils.isEmpty(valueString)) {
+            return defaultValue;
+        }
+        try {
+            return Integer.parseInt(valueString);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: "
+                    + valueString + ". Using default value: " + defaultValue);
+            return defaultValue;
+        }
+    }
+
+    private static boolean isConfigEsExtensionSecSupported(
+            HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+        // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal
+        return gnssConfiguartionIfaceVersion.mMajor >= 2;
+    }
+
+    private static boolean isConfigSuplEsSupported(
+            HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+        // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal
+        return gnssConfiguartionIfaceVersion.mMajor < 2;
+    }
+
+    private static boolean isConfigGpsLockSupported(
+            HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+        // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal
+        return gnssConfiguartionIfaceVersion.mMajor < 2;
+    }
+
+    private static native HalInterfaceVersion native_get_gnss_configuration_version();
+
+    private static native boolean native_set_supl_version(int version);
+
+    private static native boolean native_set_supl_mode(int mode);
+
+    private static native boolean native_set_supl_es(int es);
+
+    private static native boolean native_set_lpp_profile(int lppProfile);
+
+    private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
+
+    private static native boolean native_set_gps_lock(int gpsLock);
+
+    private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
+
+    private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds);
+
+    private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds);
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 05d77ab..d346ddc 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -64,6 +64,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.StatsLog;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.GpsNetInitiatedHandler;
@@ -74,12 +75,7 @@
 import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
 import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
 
-import libcore.io.IoUtils;
-
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -87,11 +83,7 @@
 import java.lang.annotation.Target;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
 
 /**
  * A GNSS implementation of LocationProvider used by LocationManager.
@@ -147,7 +139,6 @@
     private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
     private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
 
-
     // IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
     private static final int GPS_DELETE_EPHEMERIS = 0x0001;
     private static final int GPS_DELETE_ALMANAC = 0x0002;
@@ -200,9 +191,6 @@
     private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
     private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
 
-    //TODO(b/33112647): Create gps_debug.conf with commented career parameters.
-    private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf";
-
     // ref. location info
     private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
     private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
@@ -375,7 +363,7 @@
      * Properties loaded from PROPERTIES_FILE.
      * It must be accessed only inside {@link #mHandler}.
      */
-    private Properties mProperties;
+    private GnssConfiguration mGnssConfiguration;
 
     private String mSuplServerHost;
     private int mSuplServerPort = TCP_MIN_PORT;
@@ -384,6 +372,7 @@
     private boolean mSuplEsEnabled = false;
 
     private final Context mContext;
+    private final Looper mLooper;
     private final LocationExtras mLocationExtras = new LocationExtras();
     private final GnssStatusListenerHelper mGnssStatusListenerHelper;
     private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
@@ -394,6 +383,7 @@
     private final NtpTimeHelper mNtpTimeHelper;
     private final GnssBatchingProvider mGnssBatchingProvider;
     private final GnssGeofenceProvider mGnssGeofenceProvider;
+    private GnssVisibilityControl mGnssVisibilityControl;
 
     // Handler for processing events
     private Handler mHandler;
@@ -411,10 +401,6 @@
     private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
     private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
 
-    // Persist property for LPP_PROFILE
-    private final static String LPP_PROFILE = "persist.sys.gps.lpp";
-
-
     private final PowerManager mPowerManager;
     private final AlarmManager mAlarmManager;
     private final PendingIntent mWakeupIntent;
@@ -502,7 +488,7 @@
      */
     @Override
     public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
-        mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
+        mHandler.post(() -> mGnssConfiguration.setSatelliteBlacklist(constellations, svids));
     }
 
     private void subscriptionOrCarrierConfigChanged(Context context) {
@@ -525,17 +511,17 @@
                 }
                 if (isKeepLppProfile) {
                     // load current properties for the carrier
-                    loadPropertiesFromCarrierConfig(context, mProperties);
-                    String lpp_profile = mProperties.getProperty("LPP_PROFILE");
+                    mGnssConfiguration.loadPropertiesFromCarrierConfig();
+                    String lpp_profile = mGnssConfiguration.getLppProfile();
                     // set the persist property LPP_PROFILE for the value
                     if (lpp_profile != null) {
-                        SystemProperties.set(LPP_PROFILE, lpp_profile);
+                        SystemProperties.set(GnssConfiguration.LPP_PROFILE, lpp_profile);
                     }
                 } else {
                     // reset the persist property
-                    SystemProperties.set(LPP_PROFILE, "");
+                    SystemProperties.set(GnssConfiguration.LPP_PROFILE, "");
                 }
-                reloadGpsProperties(context, mProperties);
+                reloadGpsProperties();
                 mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
             }
         } else {
@@ -564,140 +550,25 @@
         return native_is_supported();
     }
 
-    interface SetCarrierProperty {
-        boolean set(int value);
-    }
-
-    private void reloadGpsProperties(Context context, Properties properties) {
-        if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
-        loadPropertiesFromCarrierConfig(context, properties);
-
-        String lpp_prof = SystemProperties.get(LPP_PROFILE);
-        if (!TextUtils.isEmpty(lpp_prof)) {
-            // override default value of this if lpp_prof is not empty
-            properties.setProperty("LPP_PROFILE", lpp_prof);
-        }
-        /*
-         * Overlay carrier properties from a debug configuration file.
-         */
-        loadPropertiesFromGpsDebugConfig(properties);
+    private void reloadGpsProperties() {
+        mGnssConfiguration.reloadGpsProperties();
+        setSuplHostPort();
         // TODO: we should get rid of C2K specific setting.
-        setSuplHostPort(properties.getProperty("SUPL_HOST"),
-                properties.getProperty("SUPL_PORT"));
-        mC2KServerHost = properties.getProperty("C2K_HOST");
-        String portString = properties.getProperty("C2K_PORT");
-        if (mC2KServerHost != null && portString != null) {
-            try {
-                mC2KServerPort = Integer.parseInt(portString);
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "unable to parse C2K_PORT: " + portString);
-            }
-        }
-        if (native_is_gnss_configuration_supported()) {
-            Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
-                {
-                    put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
-                    put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
-                    put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
-                    put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
-                    put("A_GLONASS_POS_PROTOCOL_SELECT",
-                            GnssLocationProvider::native_set_gnss_pos_protocol_select);
-                    put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
-                            GnssLocationProvider::native_set_emergency_supl_pdn);
-                    put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
-                }
-            };
-
-            for (Entry<String, SetCarrierProperty> entry : map.entrySet()) {
-                String propertyName = entry.getKey();
-                String propertyValueString = properties.getProperty(propertyName);
-                if (propertyValueString != null) {
-                    try {
-                        int propertyValueInt = Integer.decode(propertyValueString);
-                        boolean result = entry.getValue().set(propertyValueInt);
-                        if (!result) {
-                            Log.e(TAG, "Unable to set " + propertyName);
-                        }
-                    } catch (NumberFormatException e) {
-                        Log.e(TAG, "unable to parse propertyName: " + propertyValueString);
-                    }
-                }
-            }
-        } else if (DEBUG) {
-            Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
-                    + " supported");
-        }
-
-        // SUPL_ES configuration.
-        String suplESProperty = mProperties.getProperty("SUPL_ES");
-        if (suplESProperty != null) {
-            try {
-                mSuplEsEnabled = (Integer.parseInt(suplESProperty) == 1);
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
-            }
-        }
-
-        String emergencyExtensionSecondsString
-                = properties.getProperty("ES_EXTENSION_SEC", "0");
-        try {
-            int emergencyExtensionSeconds =
-                    Integer.parseInt(emergencyExtensionSecondsString);
-            mNIHandler.setEmergencyExtensionSeconds(emergencyExtensionSeconds);
-        } catch (NumberFormatException e) {
-            Log.e(TAG, "unable to parse ES_EXTENSION_SEC: "
-                    + emergencyExtensionSecondsString);
-        }
-    }
-
-    private void loadPropertiesFromCarrierConfig(Context context, Properties properties) {
-        CarrierConfigManager configManager = (CarrierConfigManager)
-                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager == null) {
-            return;
-        }
-        PersistableBundle configs = configManager.getConfigForSubId(
-                SubscriptionManager.getDefaultDataSubscriptionId());
-        if (configs == null) {
-            if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
-            configs = CarrierConfigManager.getDefaultConfig();
-        }
-        for (String configKey : configs.keySet()) {
-            if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
-                String key = configKey
-                        .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
-                        .toUpperCase();
-                Object value = configs.get(configKey);
-                if (value instanceof String) {
-                    // All GPS properties are of String type; convert so.
-                    if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
-                    properties.setProperty(key, (String) value);
-                }
-            }
-        }
-    }
-
-    private void loadPropertiesFromGpsDebugConfig(Properties properties) {
-        try {
-            File file = new File(DEBUG_PROPERTIES_FILE);
-            FileInputStream stream = null;
-            try {
-                stream = new FileInputStream(file);
-                properties.load(stream);
-            } finally {
-                IoUtils.closeQuietly(stream);
-            }
-
-        } catch (IOException e) {
-            if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
+        mC2KServerHost = mGnssConfiguration.getC2KHost();
+        mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
+        mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
+        mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
+        if (mGnssVisibilityControl != null) {
+            mGnssVisibilityControl.updateProxyApps(mGnssConfiguration.getProxyApps());
         }
     }
 
     public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
             Looper looper) {
-        super(locationProviderManager, true);
+        super(locationProviderManager);
 
         mContext = context;
+        mLooper = looper;
 
         // Create a wake lock
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -733,7 +604,7 @@
         // relative long time, so the ctor() is kept to create objects needed by this instance,
         // while IO initialization and registration is delegated to our internal handler
         // this approach is just fine because events are posted to our handler anyway
-        mProperties = new Properties();
+        mGnssConfiguration = new GnssConfiguration(mContext);
         sendMessage(INITIALIZE_HANDLER, 0, null);
 
         // Create a GPS net-initiated handler.
@@ -788,6 +659,7 @@
         }, UserHandle.ALL, intentFilter, null, mHandler);
 
         setProperties(PROPERTIES);
+        setEnabled(true);
     }
 
     /**
@@ -918,7 +790,8 @@
         mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
         Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
         AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
-            GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+            GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(
+                    mGnssConfiguration.getProperties());
             byte[] data = xtraDownloader.downloadXtraData();
             if (data != null) {
                 if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
@@ -963,17 +836,9 @@
         }
     }
 
-    private void setSuplHostPort(String hostString, String portString) {
-        if (hostString != null) {
-            mSuplServerHost = hostString;
-        }
-        if (portString != null) {
-            try {
-                mSuplServerPort = Integer.parseInt(portString);
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
-            }
-        }
+    private void setSuplHostPort() {
+        mSuplServerHost = mGnssConfiguration.getSuplHost();
+        mSuplServerPort = mGnssConfiguration.getSuplPort(TCP_MIN_PORT);
         if (mSuplServerHost != null
                 && mSuplServerPort > TCP_MIN_PORT
                 && mSuplServerPort <= TCP_MAX_PORT) {
@@ -986,23 +851,17 @@
      * Checks what SUPL mode to use, according to the AGPS mode as well as the
      * allowed mode from properties.
      *
-     * @param properties  GPS properties
      * @param agpsEnabled whether AGPS is enabled by settings value
      * @param singleShot  whether "singleshot" is needed
      * @return SUPL mode (MSA vs MSB vs STANDALONE)
      */
-    private int getSuplMode(Properties properties, boolean agpsEnabled, boolean singleShot) {
+    private int getSuplMode(boolean agpsEnabled, boolean singleShot) {
         if (agpsEnabled) {
-            String modeString = properties.getProperty("SUPL_MODE");
-            int suplMode = 0;
-            if (!TextUtils.isEmpty(modeString)) {
-                try {
-                    suplMode = Integer.parseInt(modeString);
-                } catch (NumberFormatException e) {
-                    Log.e(TAG, "unable to parse SUPL_MODE: " + modeString);
-                    return GPS_POSITION_MODE_STANDALONE;
-                }
+            int suplMode = mGnssConfiguration.getSuplMode(0);
+            if (suplMode == 0) {
+                return GPS_POSITION_MODE_STANDALONE;
             }
+
             // MS-Based is the preferred mode for Assisted-GPS position computation, so we favor
             // such mode when it is available
             if (hasCapability(GPS_CAPABILITY_MSB) && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
@@ -1307,7 +1166,7 @@
             boolean agpsEnabled =
                     (Settings.Global.getInt(mContext.getContentResolver(),
                             Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
-            mPositionMode = getSuplMode(mProperties, agpsEnabled, singleShot);
+            mPositionMode = getSuplMode(agpsEnabled, singleShot);
 
             if (DEBUG) {
                 String mode;
@@ -1670,7 +1529,7 @@
                 // re-calls native_init() and other setup.
                 handleEnable();
                 // resend configuration into the restarted HAL service.
-                reloadGpsProperties(mContext, mProperties);
+                reloadGpsProperties();
             }
         });
     }
@@ -1852,6 +1711,24 @@
                         ", response: " + userResponse);
             }
             native_send_ni_response(notificationId, userResponse);
+
+            StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
+                    StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE,
+                    notificationId,
+                    /* niType= */ 0,
+                    /* needNotify= */ false,
+                    /* needVerify= */ false,
+                    /* privacyOverride= */ false,
+                    /* timeout= */ 0,
+                    /* defaultResponse= */ 0,
+                    /* requestorId= */ null,
+                    /* text= */ null,
+                    /* requestorIdEncoding= */ 0,
+                    /* textEncoding= */ 0,
+                    mSuplEsEnabled,
+                    mEnabled,
+                    userResponse);
+
             return true;
         }
     };
@@ -1901,6 +1778,22 @@
         notification.textEncoding = textEncoding;
 
         mNIHandler.handleNiNotification(notification);
+        StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
+                StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_REQUEST,
+                notification.notificationId,
+                notification.niType,
+                notification.needNotify,
+                notification.needVerify,
+                notification.privacyOverride,
+                notification.timeout,
+                notification.defaultResponse,
+                notification.requestorId,
+                notification.text,
+                notification.requestorIdEncoding,
+                notification.textEncoding,
+                mSuplEsEnabled,
+                mEnabled,
+                /* userResponse= */ 0);
     }
 
     /**
@@ -1982,6 +1875,27 @@
         }
     }
 
+    // Implements method nfwNotifyCb() in IGnssVisibilityControlCallback.hal.
+    @NativeEntryPoint
+    private void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
+            String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
+            boolean inEmergencyMode, boolean isCachedLocation) {
+        if (mGnssVisibilityControl == null) {
+            Log.e(TAG, "reportNfwNotification: mGnssVisibilityControl is not initialized.");
+            return;
+        }
+
+        mGnssVisibilityControl.reportNfwNotification(proxyAppPackageName, protocolStack,
+                otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
+                isCachedLocation);
+    }
+
+    // Implements method isInEmergencySession() in IGnssVisibilityControlCallback.hal.
+    @NativeEntryPoint
+    boolean isInEmergencySession() {
+        return mNIHandler.getInEmergency();
+    }
+
     private void sendMessage(int message, int arg, Object obj) {
         // hold a wake lock until this message is delivered
         // note that this assumes the message will not be removed from the queue before
@@ -2070,9 +1984,13 @@
                 native_cleanup();
             }
 
+            if (native_is_gnss_visibility_control_supported()) {
+                mGnssVisibilityControl = new GnssVisibilityControl(mContext, mLooper);
+            }
+
             // load default GPS configuration
             // (this configuration might change in the future based on SIM changes)
-            reloadGpsProperties(mContext, mProperties);
+            reloadGpsProperties();
 
             // TODO: When this object "finishes" we should unregister by invoking
             // SubscriptionManager.getInstance(mContext).unregister
@@ -2227,7 +2145,7 @@
 
     private static native boolean native_is_supported();
 
-    private static native boolean native_is_gnss_configuration_supported();
+    private static native boolean native_is_gnss_visibility_control_supported();
 
     private static native void native_init_once();
 
@@ -2284,21 +2202,4 @@
             int lac, int cid);
 
     private native void native_agps_set_id(int type, String setid);
-
-    // GNSS Configuration
-    private static native boolean native_set_supl_version(int version);
-
-    private static native boolean native_set_supl_mode(int mode);
-
-    private static native boolean native_set_supl_es(int es);
-
-    private static native boolean native_set_lpp_profile(int lppProfile);
-
-    private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
-
-    private static native boolean native_set_gps_lock(int gpsLock);
-
-    private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
-
-    private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds);
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
new file mode 100644
index 0000000..591889d
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2019 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.location;
+
+import android.annotation.SuppressLint;
+import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.StatsLog;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Handles GNSS non-framework location access user visibility and control.
+ *
+ * The state of the GnssVisibilityControl object must be accessed/modified through the Handler
+ * thread only.
+ */
+class GnssVisibilityControl {
+    private static final String TAG = "GnssVisibilityControl";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // Constants related to non-framework (NFW) location access permission proxy apps.
+    private static final String NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX =
+            ".NonFrameworkLocationAccessActivity";
+    private static final String NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX =
+            ".intent.action.NON_FRAMEWORK_LOCATION_ACCESS";
+    private static final String NFW_INTENT_TYPE = "text/plain";
+
+    private static final String LOCATION_PERMISSION_NAME =
+            "android.permission.ACCESS_FINE_LOCATION";
+
+    // Wakelocks
+    private static final String WAKELOCK_KEY = TAG;
+    private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
+    private final PowerManager.WakeLock mWakeLock;
+
+    private final AppOpsManager mAppOps;
+    private final PackageManager mPackageManager;
+
+    private final Handler mHandler;
+    private final Context mContext;
+
+    // Number of non-framework location access proxy apps is expected to be small (< 5).
+    private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
+    private HashMap<String, Boolean> mProxyAppToLocationPermissions = new HashMap<>(
+            HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS);
+
+    private PackageManager.OnPermissionsChangedListener mOnPermissionsChangedListener =
+            uid -> postEvent(() -> handlePermissionsChanged(uid));
+
+    GnssVisibilityControl(Context context, Looper looper) {
+        mContext = context;
+        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+        mHandler = new Handler(looper);
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
+        mPackageManager = mContext.getPackageManager();
+        // TODO(b/122855984): Handle global location settings on/off.
+        // TODO(b/122856189): Handle roaming case.
+    }
+
+    void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
+        // NOTE: This class doesn't explicitly register and listen for SIM_STATE_CHANGED event
+        //       but rather piggy backs on the GnssLocationProvider SIM_STATE_CHANGED handling
+        //       so that the order of processing is preserved. GnssLocationProvider should
+        //       first load the new config parameters for the new SIM and then call this method.
+        postEvent(() -> handleSubscriptionOrSimChanged(nfwLocationAccessProxyApps));
+    }
+
+    void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
+            String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
+            boolean inEmergencyMode, boolean isCachedLocation) {
+        postEvent(() -> handleNfwNotification(
+                new NfwNotification(proxyAppPackageName, protocolStack, otherProtocolStackName,
+                        requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
+    }
+
+    private void handleSubscriptionOrSimChanged(List<String> nfwLocationAccessProxyApps) {
+        if (nfwLocationAccessProxyApps.isEmpty()) {
+            // Stop listening for app permission changes. Clear the app list in GNSS HAL.
+            if (!mProxyAppToLocationPermissions.isEmpty()) {
+                mPackageManager.removeOnPermissionsChangeListener(mOnPermissionsChangedListener);
+                mProxyAppToLocationPermissions.clear();
+                updateNfwLocationAccessProxyAppsInGnssHal();
+            }
+            return;
+        }
+
+        if (mProxyAppToLocationPermissions.isEmpty()) {
+            mPackageManager.addOnPermissionsChangeListener(mOnPermissionsChangedListener);
+        } else {
+            mProxyAppToLocationPermissions.clear();
+        }
+
+        for (String proxApp : nfwLocationAccessProxyApps) {
+            mProxyAppToLocationPermissions.put(proxApp, hasLocationPermission(proxApp));
+        }
+
+        updateNfwLocationAccessProxyAppsInGnssHal();
+    }
+
+    // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
+    private static class NfwNotification {
+        private static final String KEY_PROTOCOL_STACK = "ProtocolStack";
+        private static final String KEY_OTHER_PROTOCOL_STACK_NAME = "OtherProtocolStackName";
+        private static final String KEY_REQUESTOR = "Requestor";
+        private static final String KEY_REQUESTOR_ID = "RequestorId";
+        private static final String KEY_RESPONSE_TYPE = "ResponseType";
+        private static final String KEY_IN_EMERGENCY_MODE = "InEmergencyMode";
+        private static final String KEY_IS_CACHED_LOCATION = "IsCachedLocation";
+
+        // This must match with NfwResponseType enum in IGnssVisibilityControlCallback.hal.
+        private static final byte NFW_RESPONSE_TYPE_REJECTED = 0;
+        private static final byte NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED = 1;
+        private static final byte NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED = 2;
+
+        private final String mProxyAppPackageName;
+        private final byte mProtocolStack;
+        private final String mOtherProtocolStackName;
+        private final byte mRequestor;
+        private final String mRequestorId;
+        private final byte mResponseType;
+        private final boolean mInEmergencyMode;
+        private final boolean mIsCachedLocation;
+
+        NfwNotification(String proxyAppPackageName, byte protocolStack,
+                String otherProtocolStackName, byte requestor, String requestorId,
+                byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
+            mProxyAppPackageName = proxyAppPackageName;
+            mProtocolStack = protocolStack;
+            mOtherProtocolStackName = otherProtocolStackName;
+            mRequestor = requestor;
+            mRequestorId = requestorId;
+            mResponseType = responseType;
+            mInEmergencyMode = inEmergencyMode;
+            mIsCachedLocation = isCachedLocation;
+        }
+
+        void copyFieldsToIntent(Intent intent) {
+            intent.putExtra(KEY_PROTOCOL_STACK, mProtocolStack);
+            if (!TextUtils.isEmpty(mOtherProtocolStackName)) {
+                intent.putExtra(KEY_OTHER_PROTOCOL_STACK_NAME, mOtherProtocolStackName);
+            }
+            intent.putExtra(KEY_REQUESTOR, mRequestor);
+            if (!TextUtils.isEmpty(mRequestorId)) {
+                intent.putExtra(KEY_REQUESTOR_ID, mRequestorId);
+            }
+            intent.putExtra(KEY_RESPONSE_TYPE, mResponseType);
+            intent.putExtra(KEY_IN_EMERGENCY_MODE, mInEmergencyMode);
+            if (mResponseType == NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED) {
+                intent.putExtra(KEY_IS_CACHED_LOCATION, mIsCachedLocation);
+            }
+        }
+
+        @SuppressLint("DefaultLocale")
+        public String toString() {
+            return String.format(
+                    "[Notification] proxyAppPackageName: %s, protocolStack: %d"
+                            + ", otherProtocolStackName: %s, requestor: %d, requestorId: %s"
+                            + ", responseType: %d, inEmergencyMode: %b, isCachedLocation: %b",
+                    mProxyAppPackageName, mProtocolStack, mOtherProtocolStackName,
+                    mRequestor, mRequestorId, mResponseType, mInEmergencyMode, mIsCachedLocation);
+        }
+
+        String getResponseTypeAsString() {
+            switch (mResponseType) {
+                case NFW_RESPONSE_TYPE_REJECTED:
+                    return "REJECTED";
+                case NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED:
+                    return "ACCEPTED_NO_LOCATION_PROVIDED";
+                case NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED:
+                    return "ACCEPTED_LOCATION_PROVIDED";
+                default:
+                    return "<Unknown>";
+            }
+        }
+    }
+
+    private void handlePermissionsChanged(int uid) {
+        if (mProxyAppToLocationPermissions.isEmpty()) {
+            return;
+        }
+
+        for (Map.Entry<String, Boolean> entry : mProxyAppToLocationPermissions.entrySet()) {
+            // Cannot cache uid since the application could be uninstalled and reinstalled.
+            final String proxyApp = entry.getKey();
+            final Integer nfwProxyAppUid = getApplicationUid(proxyApp);
+            if (nfwProxyAppUid == null || nfwProxyAppUid != uid) {
+                continue;
+            }
+
+            final boolean isLocationPermissionEnabled = hasLocationPermission(proxyApp);
+            if (isLocationPermissionEnabled != entry.getValue()) {
+                Log.i(TAG, "Location permission setting is changed to "
+                        + (isLocationPermissionEnabled ? "enabled" : "disabled")
+                        + " for non-framework location access proxy app "
+                        + proxyApp);
+                entry.setValue(isLocationPermissionEnabled);
+                updateNfwLocationAccessProxyAppsInGnssHal();
+                return;
+            }
+        }
+    }
+
+    private Integer getApplicationUid(String pkgName) {
+        try {
+            return mPackageManager.getApplicationInfo(pkgName, 0).uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) {
+                Log.d(TAG, "Non-framework location access proxy app "
+                        + pkgName + " is not found.");
+            }
+            return null;
+        }
+    }
+
+    private boolean hasLocationPermission(String pkgName) {
+        return mPackageManager.checkPermission(LOCATION_PERMISSION_NAME, pkgName)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private void updateNfwLocationAccessProxyAppsInGnssHal() {
+        // Get a count of proxy apps with location permission enabled to array creation size.
+        int countLocationPermissionEnabledProxyApps = 0;
+        for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) {
+            if (hasLocationPermissionEnabled) {
+                ++countLocationPermissionEnabledProxyApps;
+            }
+        }
+
+        int i = 0;
+        String[] locationPermissionEnabledProxyApps =
+                new String[countLocationPermissionEnabledProxyApps];
+        for (Map.Entry<String, Boolean> entry : mProxyAppToLocationPermissions.entrySet()) {
+            final String proxyApp = entry.getKey();
+            final boolean hasLocationPermissionEnabled = entry.getValue();
+            if (hasLocationPermissionEnabled) {
+                locationPermissionEnabledProxyApps[i++] = proxyApp;
+            }
+        }
+
+        String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
+        Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
+                + proxyAppsStr);
+        boolean result = native_enable_nfw_location_access(locationPermissionEnabledProxyApps);
+        if (!result) {
+            Log.e(TAG, "Failed to update non-framework location access proxy apps in the"
+                    + " GNSS HAL to: " + proxyAppsStr);
+        }
+    }
+
+    private void handleNfwNotification(NfwNotification nfwNotification) {
+        if (DEBUG) Log.d(TAG, nfwNotification.toString());
+
+        final String proxyAppPackageName = nfwNotification.mProxyAppPackageName;
+        Boolean isLocationPermissionEnabled = mProxyAppToLocationPermissions.get(
+                proxyAppPackageName);
+        boolean isLocationRequestAccepted =
+                nfwNotification.mResponseType != NfwNotification.NFW_RESPONSE_TYPE_REJECTED;
+        boolean isPermissionMismatched;
+        if (isLocationPermissionEnabled == null) {
+            isPermissionMismatched = isLocationRequestAccepted;
+        } else {
+            isPermissionMismatched = (isLocationPermissionEnabled != isLocationRequestAccepted);
+        }
+        logEvent(nfwNotification, isPermissionMismatched);
+
+        if (TextUtils.isEmpty(proxyAppPackageName)) {
+            Log.e(TAG, "ProxyAppPackageName field is not set. Not sending intent to proxy app for "
+                    + nfwNotification);
+            return;
+        }
+
+        if (isLocationPermissionEnabled == null) {
+            // App is not in the configured list.
+            Log.e(TAG, "Could not find proxy app with name: " + proxyAppPackageName + " in the "
+                    + "value specified for config parameter: "
+                    + GnssConfiguration.CONFIG_NFW_PROXY_APPS + ". Not sending intent to proxy app"
+                    + " for " + nfwNotification);
+            return;
+        }
+
+        // Send intent to non-framework location proxy app with notification information.
+        final Intent intent = new Intent(
+                proxyAppPackageName + NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX);
+        final String proxAppActivityName =
+                proxyAppPackageName + NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX;
+        intent.setClassName(proxyAppPackageName, proxAppActivityName);
+        intent.setType(NFW_INTENT_TYPE);
+        nfwNotification.copyFieldsToIntent(intent);
+
+        // Check if the proxy app is still installed.
+        final Integer clsAppUid = getApplicationUid(proxyAppPackageName);
+        if (clsAppUid == null || intent.resolveActivity(mPackageManager) == null) {
+            Log.i(TAG, "Proxy application " + proxyAppPackageName + " and/or activity "
+                    + proxAppActivityName + " is not found. Not sending"
+                    + " intent to proxy app for " + nfwNotification);
+            return;
+        }
+
+        // Display location icon attributed to this proxy app.
+        mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, clsAppUid, proxyAppPackageName);
+
+        // Log proxy app permission mismatch between framework and GNSS HAL.
+        if (isPermissionMismatched) {
+            Log.w(TAG, "Permission mismatch. Framework proxy app " + proxyAppPackageName
+                    + " location permission is set to " + isLocationPermissionEnabled
+                    + " but GNSS non-framework location access response type is "
+                    + nfwNotification.getResponseTypeAsString() + " for " + nfwNotification);
+        }
+
+        // Notify proxy app.
+        try {
+            if (DEBUG) {
+                Log.d(TAG, "Sending non-framework location access notification intent: " + intent);
+            }
+            mContext.startActivityAsUser(intent, UserHandle.getUserHandleForUid(clsAppUid));
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "Activity not found. Failed to send non-framework location access"
+                    + " notification intent to proxy app activity: " + proxAppActivityName);
+        }
+    }
+
+    private void logEvent(NfwNotification notification, boolean isPermissionMismatched) {
+        StatsLog.write(StatsLog.GNSS_NFW_NOTIFICATION_REPORTED,
+                notification.mProxyAppPackageName,
+                notification.mProtocolStack,
+                notification.mOtherProtocolStackName,
+                notification.mRequestor,
+                notification.mRequestorId,
+                notification.mResponseType,
+                notification.mInEmergencyMode,
+                notification.mIsCachedLocation,
+                isPermissionMismatched);
+    }
+
+    private void postEvent(Runnable event) {
+        // Hold a wake lock until this message is delivered.
+        // Note that this assumes the message will not be removed from the queue before
+        // it is handled (otherwise the wake lock would be leaked).
+        mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
+        if (!mHandler.post(runEventAndReleaseWakeLock(event))) {
+            mWakeLock.release();
+        }
+    }
+
+    private Runnable runEventAndReleaseWakeLock(Runnable event) {
+        return () -> {
+            try {
+                event.run();
+            } finally {
+                mWakeLock.release();
+            }
+        };
+    }
+
+    private native boolean native_enable_nfw_location_access(String[] proxyApps);
+}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index dfcef70..a6da8c5 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -101,7 +101,7 @@
     private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
             String action, int overlaySwitchResId, int defaultServicePackageNameResId,
             int initialPackageNamesResId) {
-        super(locationProviderManager, false);
+        super(locationProviderManager);
 
         mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
                 defaultServicePackageNameResId, initialPackageNamesResId,
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
index b7934d9..b7ccb26 100644
--- a/services/core/java/com/android/server/location/LocationRequestStatistics.java
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -123,6 +123,9 @@
         // in foreground.
         private long mForegroundDurationMs;
 
+        // Time when package last went dormant (stopped requesting location)
+        private long mLastStopElapsedTimeMs;
+
         private PackageStatistics() {
             mInitialElapsedTimeMs = SystemClock.elapsedRealtime();
             mNumActiveRequests = 0;
@@ -131,6 +134,7 @@
             mSlowestIntervalMs = 0;
             mForegroundDurationMs = 0;
             mLastForegroundElapsedTimeMs = 0;
+            mLastStopElapsedTimeMs = 0;
         }
 
         private void startRequesting(long intervalMs) {
@@ -167,8 +171,8 @@
 
             mNumActiveRequests--;
             if (mNumActiveRequests == 0) {
-                long lastDurationMs
-                        = SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
+                mLastStopElapsedTimeMs = SystemClock.elapsedRealtime();
+                long lastDurationMs = mLastStopElapsedTimeMs - mLastActivitationElapsedTimeMs;
                 mTotalDurationMs += lastDurationMs;
                 updateForeground(false);
             }
@@ -206,6 +210,13 @@
         }
 
         /**
+         * Returns the time since the last request stopped in ms.
+         */
+        public long getTimeSinceLastRequestStoppedMs() {
+            return SystemClock.elapsedRealtime() - mLastStopElapsedTimeMs;
+        }
+
+        /**
          * Returns the fastest interval that has been tracked.
          */
         public long getFastestIntervalMs() {
@@ -244,6 +255,10 @@
                     .append(" minutes");
             if (isActive()) {
                 s.append(": Currently active");
+            } else {
+                s.append(": Last active ")
+                        .append((getTimeSinceLastRequestStoppedMs() / 1000) / 60)
+                        .append(" minutes ago");
             }
             return s.toString();
         }
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index bfbebf7..fe91c63 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -43,7 +43,7 @@
 
     public MockProvider(
             LocationProviderManager locationProviderManager, ProviderProperties properties) {
-        super(locationProviderManager, true);
+        super(locationProviderManager);
 
         mEnabled = true;
         mLocation = null;
@@ -63,8 +63,11 @@
     /** Sets the location to report for this mock provider. */
     public void setLocation(Location l) {
         mLocation = new Location(l);
+        if (!mLocation.isFromMockProvider()) {
+            mLocation.setIsFromMockProvider(true);
+        }
         if (mEnabled) {
-            reportLocation(l);
+            reportLocation(mLocation);
         }
     }
 
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 70d64b0..30260b2 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -43,11 +43,12 @@
     private boolean mReportLocation;
 
     public PassiveProvider(LocationProviderManager locationProviderManager) {
-        super(locationProviderManager, true);
+        super(locationProviderManager);
 
         mReportLocation = false;
 
         setProperties(PROPERTIES);
+        setEnabled(true);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ea8c792..8734ceb6 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -21,10 +21,10 @@
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.USER_FRP;
 import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
 import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
@@ -81,9 +81,9 @@
 import android.security.keystore.KeyProtection;
 import android.security.keystore.UserNotAuthenticatedException;
 import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
 import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.WrappedApplicationKey;
-import android.security.keystore.recovery.KeyChainSnapshot;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.text.TextUtils;
@@ -109,9 +109,9 @@
 import com.android.server.SystemService;
 import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
 import com.android.server.locksettings.LockSettingsStorage.PersistentData;
-import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
 import com.android.server.wm.WindowManagerInternal;
 
 import libcore.util.HexEncoding;
@@ -130,8 +130,8 @@
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -2103,11 +2103,24 @@
     }
 
     @Override
-    public @Nullable String importKey(@NonNull String alias, byte[] keyBytes) throws RemoteException {
+    public @Nullable String generateKeyWithMetadata(
+            @NonNull String alias, @Nullable byte[] metadata) throws RemoteException {
+        return mRecoverableKeyStoreManager.generateKeyWithMetadata(alias, metadata);
+    }
+
+    @Override
+    public @Nullable String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
+            throws RemoteException {
         return mRecoverableKeyStoreManager.importKey(alias, keyBytes);
     }
 
     @Override
+    public @Nullable String importKeyWithMetadata(@NonNull String alias, @NonNull byte[] keyBytes,
+            @Nullable byte[] metadata) throws RemoteException {
+        return mRecoverableKeyStoreManager.importKeyWithMetadata(alias, keyBytes, metadata);
+    }
+
+    @Override
     public @Nullable String getKey(@NonNull String alias) throws RemoteException {
         return mRecoverableKeyStoreManager.getKey(alias);
     }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index f1951b1..1f9b027 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -26,6 +26,7 @@
 import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -258,9 +259,9 @@
             localLskfHash = hashCredentialsBySaltedSha256(salt, mCredential);
         }
 
-        Map<String, SecretKey> rawKeys;
+        Map<String, Pair<SecretKey, byte[]>> rawKeysWithMetadata;
         try {
-            rawKeys = getKeysToSync(recoveryAgentUid);
+            rawKeysWithMetadata = getKeysToSync(recoveryAgentUid);
         } catch (GeneralSecurityException e) {
             Log.e(TAG, "Failed to load recoverable keys for sync", e);
             return;
@@ -278,7 +279,9 @@
         }
         // Only include insecure key material for test
         if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(rootCertAlias)) {
-            rawKeys = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+            rawKeysWithMetadata =
+                    mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(
+                            rawKeysWithMetadata);
         }
 
         SecretKey recoveryKey;
@@ -292,7 +295,7 @@
         Map<String, byte[]> encryptedApplicationKeys;
         try {
             encryptedApplicationKeys = KeySyncUtils.encryptKeysWithRecoveryKey(
-                    recoveryKey, rawKeys);
+                    recoveryKey, rawKeysWithMetadata);
         } catch (InvalidKeyException | NoSuchAlgorithmException e) {
             Log.wtf(TAG,
                     "Should be impossible: could not encrypt application keys with random key",
@@ -356,7 +359,8 @@
                 .setCounterId(counterId)
                 .setServerParams(vaultHandle)
                 .setKeyChainProtectionParams(metadataList)
-                .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys))
+                .setWrappedApplicationKeys(
+                        createApplicationKeyEntries(encryptedApplicationKeys, rawKeysWithMetadata))
                 .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey);
         try {
             keyChainSnapshotBuilder.setTrustedHardwareCertPath(certPath);
@@ -405,7 +409,7 @@
     /**
      * Returns all of the recoverable keys for the user.
      */
-    private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid)
+    private Map<String, Pair<SecretKey, byte[]>> getKeysToSync(int recoveryAgentUid)
             throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
             NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
             InvalidKeyException, InvalidAlgorithmParameterException, IOException {
@@ -521,12 +525,14 @@
     }
 
     private static List<WrappedApplicationKey> createApplicationKeyEntries(
-            Map<String, byte[]> encryptedApplicationKeys) {
+            Map<String, byte[]> encryptedApplicationKeys,
+            Map<String, Pair<SecretKey, byte[]>> originalKeysWithMetadata) {
         ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>();
         for (String alias : encryptedApplicationKeys.keySet()) {
             keyEntries.add(new WrappedApplicationKey.Builder()
                     .setAlias(alias)
                     .setEncryptedKeyMaterial(encryptedApplicationKeys.get(alias))
+                    .setMetadata(originalKeysWithMetadata.get(alias).second)
                     .build());
         }
         return keyEntries;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index 8e6f9cb..24d575e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -16,6 +16,9 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.annotation.Nullable;
+import android.util.Pair;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.nio.ByteBuffer;
@@ -152,15 +155,28 @@
      * @hide
      */
     public static Map<String, byte[]> encryptKeysWithRecoveryKey(
-            SecretKey recoveryKey, Map<String, SecretKey> keys)
+            SecretKey recoveryKey, Map<String, Pair<SecretKey, byte[]>> keys)
             throws NoSuchAlgorithmException, InvalidKeyException {
         HashMap<String, byte[]> encryptedKeys = new HashMap<>();
         for (String alias : keys.keySet()) {
-            SecretKey key = keys.get(alias);
+            SecretKey key = keys.get(alias).first;
+            byte[] metadata = keys.get(alias).second;
+            byte[] header;
+            if (metadata == null) {
+                header = ENCRYPTED_APPLICATION_KEY_HEADER;
+            } else {
+                // The provided metadata, if non-empty, will be bound to the authenticated
+                // encryption process of the key material. As a result, the ciphertext cannot be
+                // decrypted if a wrong metadata is provided during the recovery/decryption process.
+                // Note that Android P devices do not have the API to provide the optional metadata,
+                // so all the keys with non-empty metadata stored on Android Q+ devices cannot be
+                // recovered on Android P devices.
+                header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, metadata);
+            }
             byte[] encryptedKey = SecureBox.encrypt(
                     /*theirPublicKey=*/ null,
                     /*sharedSecret=*/ recoveryKey.getEncoded(),
-                    /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER,
+                    /*header=*/ header,
                     /*payload=*/ key.getEncoded());
             encryptedKeys.put(alias, encryptedKey);
         }
@@ -257,12 +273,19 @@
      * @throws AEADBadTagException if the message has been tampered with or was encrypted with a
      *     different key.
      */
-    public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey)
+    public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey,
+            @Nullable byte[] applicationKeyMetadata)
             throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+        byte[] header;
+        if (applicationKeyMetadata == null) {
+            header = ENCRYPTED_APPLICATION_KEY_HEADER;
+        } else {
+            header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, applicationKeyMetadata);
+        }
         return SecureBox.decrypt(
                 /*ourPrivateKey=*/ null,
                 /*sharedSecret=*/ recoveryKey,
-                /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER,
+                /*header=*/ header,
                 /*encryptedPayload=*/ encryptedApplicationKey);
     }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
index c249468..1692e5c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
@@ -17,6 +17,8 @@
 package com.android.server.locksettings.recoverablekeystore;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
 
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 
@@ -24,7 +26,6 @@
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.util.Locale;
-import android.util.Log;
 
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
@@ -84,6 +85,8 @@
      * @param userId The user ID of the profile to which the calling app belongs.
      * @param uid The uid of the application that will own the key.
      * @param alias The alias by which the key will be known in the recoverable key store.
+     * @param metadata The optional metadata that will be authenticated (but unencrypted) together
+     *     with the key material when the key is uploaded to cloud.
      * @throws RecoverableKeyStorageException if there is some error persisting the key either to
      *     the database.
      * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
@@ -92,12 +95,13 @@
      * @hide
      */
     public byte[] generateAndStoreKey(
-            PlatformEncryptionKey platformKey, int userId, int uid, String alias)
+            PlatformEncryptionKey platformKey, int userId, int uid, String alias,
+            @Nullable byte[] metadata)
             throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
         mKeyGenerator.init(KEY_SIZE_BITS);
         SecretKey key = mKeyGenerator.generateKey();
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata);
         long result = mDatabase.insertKey(userId, uid, alias, wrappedKey);
 
         if (result == RESULT_CANNOT_INSERT_ROW) {
@@ -126,6 +130,8 @@
      * @param uid The uid of the application that will own the key.
      * @param alias The alias by which the key will be known in the recoverable key store.
      * @param keyBytes The raw bytes of the AES key to be imported.
+     * @param metadata The optional metadata that will be authenticated (but unencrypted) together
+     *     with the key material when the key is uploaded to cloud.
      * @throws RecoverableKeyStorageException if there is some error persisting the key either to
      *     the database.
      * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
@@ -135,11 +141,11 @@
      */
     public void importKey(
             @NonNull PlatformEncryptionKey platformKey, int userId, int uid, @NonNull String alias,
-            @NonNull byte[] keyBytes)
+            @NonNull byte[] keyBytes, @Nullable byte[] metadata)
             throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
         SecretKey key = new SecretKeySpec(keyBytes, SECRET_KEY_ALGORITHM);
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata);
         long result = mDatabase.insertKey(userId, uid, alias, wrappedKey);
 
         if (result == RESULT_CANNOT_INSERT_ROW) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index fc5184d..ed864c0 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -20,8 +20,8 @@
 import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED;
 import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE;
 import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER;
-import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
 import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
+import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
 import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
 import static android.security.keystore.recovery.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
 import static android.security.keystore.recovery.RecoveryController.ERROR_SESSION_EXPIRED;
@@ -35,12 +35,12 @@
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
+import android.security.KeyStore;
 import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
 import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.RecoveryController;
 import android.security.keystore.recovery.WrappedApplicationKey;
-import android.security.KeyStore;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -59,7 +59,6 @@
 
 import java.io.IOException;
 import java.security.InvalidKeyException;
-import java.security.KeyFactory;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
@@ -70,7 +69,6 @@
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -606,7 +604,8 @@
 
         try {
             byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey);
-            Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys);
+            Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey,
+                    applicationKeys);
             return importKeyMaterials(userId, uid, keysByAlias);
         } catch (KeyStoreException e) {
             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
@@ -625,7 +624,8 @@
      * @throws KeyStoreException if an error occurs importing the key or getting the grant.
      */
     private @NonNull Map<String, String> importKeyMaterials(
-            int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException {
+            int userId, int uid, Map<String, byte[]> keysByAlias)
+            throws KeyStoreException {
         ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size());
         for (String alias : keysByAlias.keySet()) {
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias));
@@ -674,9 +674,29 @@
      * Generates a key named {@code alias} in caller's namespace.
      * The key is stored in system service keystore namespace.
      *
+     * @param alias the alias provided by caller as a reference to the key.
      * @return grant alias, which caller can use to access the key.
+     * @throws RemoteException if certain internal errors occur.
+     *
+     * @deprecated Use {@link #generateKeyWithMetadata(String, byte[])} instead.
      */
+    @Deprecated
     public String generateKey(@NonNull String alias) throws RemoteException {
+        return generateKeyWithMetadata(alias, /*metadata=*/ null);
+    }
+
+    /**
+     * Generates a key named {@code alias} with the {@code metadata} in caller's namespace.
+     * The key is stored in system service keystore namespace.
+     *
+     * @param alias the alias provided by caller as a reference to the key.
+     * @param metadata the optional metadata blob that will authenticated (but unencrypted) together
+     *         with the key material when the key is uploaded to cloud.
+     * @return grant alias, which caller can use to access the key.
+     * @throws RemoteException if certain internal errors occur.
+     */
+    public String generateKeyWithMetadata(@NonNull String alias, @Nullable byte[] metadata)
+            throws RemoteException {
         checkRecoverKeyStorePermission();
         Preconditions.checkNotNull(alias, "alias is null");
         int uid = Binder.getCallingUid();
@@ -695,8 +715,8 @@
         }
 
         try {
-            byte[] secretKey =
-                    mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
+            byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId,
+                    uid, alias, metadata);
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
             return getAlias(userId, uid, alias);
         } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
@@ -713,10 +733,30 @@
      * @return grant alias, which caller can use to access the key.
      * @throws RemoteException if the given key is invalid or some internal errors occur.
      *
+     * @deprecated Use {{@link #importKeyWithMetadata(String, byte[], byte[])}} instead.
+     *
      * @hide
      */
+    @Deprecated
     public @Nullable String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
             throws RemoteException {
+        return importKeyWithMetadata(alias, keyBytes, /*metadata=*/ null);
+    }
+
+    /**
+     * Imports a 256-bit AES-GCM key named {@code alias} with the given {@code metadata}. The key is
+     * stored in system service keystore namespace.
+     *
+     * @param alias the alias provided by caller as a reference to the key.
+     * @param keyBytes the raw bytes of the 256-bit AES key.
+     * @param metadata the metadata to be authenticated (but unencrypted) together with the key.
+     * @return grant alias, which caller can use to access the key.
+     * @throws RemoteException if the given key is invalid or some internal errors occur.
+     *
+     * @hide
+     */
+    public @Nullable String importKeyWithMetadata(@NonNull String alias, @NonNull byte[] keyBytes,
+            @Nullable byte[] metadata) throws RemoteException {
         checkRecoverKeyStorePermission();
         Preconditions.checkNotNull(alias, "alias is null");
         Preconditions.checkNotNull(keyBytes, "keyBytes is null");
@@ -745,7 +785,8 @@
 
         try {
             // Wrap the key by the platform key and store the wrapped key locally
-            mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes);
+            mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes,
+                    metadata);
 
             // Import the key to Android KeyStore and get grant
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes);
@@ -812,17 +853,17 @@
      * @return Map from alias to raw key material.
      * @throws RemoteException if an error occurred decrypting the keys.
      */
-    private @NonNull Map<String, byte[]> recoverApplicationKeys(
-            @NonNull byte[] recoveryKey,
+    private @NonNull Map<String, byte[]> recoverApplicationKeys(@NonNull byte[] recoveryKey,
             @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
         HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>();
         for (WrappedApplicationKey applicationKey : applicationKeys) {
             String alias = applicationKey.getAlias();
             byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
+            byte[] keyMetadata = applicationKey.getMetadata();
 
             try {
-                byte[] keyMaterial =
-                        KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial);
+                byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey,
+                        encryptedKeyMaterial, keyMetadata);
                 keyMaterialByAlias.put(alias, keyMaterial);
             } catch (NoSuchAlgorithmException e) {
                 Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
index 5ba3cce..ffea488 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
@@ -18,17 +18,20 @@
 
 import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
 
-import com.android.internal.widget.LockPatternUtils;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keystore.recovery.TrustedRootCertificates;
 import android.util.Log;
+import android.util.Pair;
 
-import java.util.HashMap;
+import com.android.internal.widget.LockPatternUtils;
+
 import java.security.cert.X509Certificate;
+import java.util.HashMap;
 import java.util.Map;
+
 import javax.crypto.SecretKey;
 
 /**
@@ -84,22 +87,24 @@
                 || isTestOnlyCertificateAlias(rootCertificateAlias);
     }
 
-    public boolean doesCredentialSupportInsecureMode(int credentialType, String credential) {
+    boolean doesCredentialSupportInsecureMode(int credentialType, String credential) {
         return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD)
             && (credential != null)
             && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX);
     }
 
-    public Map<String, SecretKey> keepOnlyWhitelistedInsecureKeys(Map<String, SecretKey> rawKeys) {
+    Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys(
+            Map<String, Pair<SecretKey, byte[]>> rawKeys) {
         if (rawKeys == null) {
             return null;
         }
-        Map<String, SecretKey> filteredKeys = new HashMap<>();
-        for (Map.Entry<String, SecretKey> entry : rawKeys.entrySet()) {
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys = new HashMap<>();
+        for (Map.Entry<String, Pair<SecretKey, byte[]>> entry : rawKeys.entrySet()) {
             String alias = entry.getKey();
             if (alias != null
                     && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) {
-                filteredKeys.put(entry.getKey(), entry.getValue());
+                filteredKeys.put(entry.getKey(),
+                        Pair.create(entry.getValue().first, entry.getValue().second));
                 Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot");
             }
         }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index 0077242..09d7541 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -16,8 +16,10 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.annotation.Nullable;
 import android.security.keystore.recovery.RecoveryController;
 import android.util.Log;
+import android.util.Pair;
 
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -49,6 +51,7 @@
     private final int mRecoveryStatus;
     private final byte[] mNonce;
     private final byte[] mKeyMaterial;
+    private final byte[] mKeyMetadata;
 
     /**
      * Returns a wrapped form of {@code key}, using {@code wrappingKey} to encrypt the key material.
@@ -58,7 +61,8 @@
      *     {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does
      *     not expose its key material.
      */
-    public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key)
+    public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key,
+            @Nullable byte[] metadata)
             throws InvalidKeyException, KeyStoreException {
         if (key.getEncoded() == null) {
             throw new InvalidKeyException(
@@ -96,6 +100,7 @@
         return new WrappedKey(
                 /*nonce=*/ cipher.getIV(),
                 /*keyMaterial=*/ encryptedKeyMaterial,
+                /*keyMetadata=*/ metadata,
                 /*platformKeyGenerationId=*/ wrappingKey.getGenerationId(),
                 RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
     }
@@ -110,11 +115,10 @@
      * @see RecoveryController#RECOVERY_STATUS_SYNC_IN_PROGRESS
      * @hide
      */
-    public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
-        mNonce = nonce;
-        mKeyMaterial = keyMaterial;
-        mPlatformKeyGenerationId = platformKeyGenerationId;
-        mRecoveryStatus = RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS;
+    public WrappedKey(byte[] nonce, byte[] keyMaterial, @Nullable byte[] keyMetadata,
+            int platformKeyGenerationId) {
+        this(nonce, keyMaterial, keyMetadata, platformKeyGenerationId,
+                RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
     }
 
     /**
@@ -122,15 +126,18 @@
      *
      * @param nonce The nonce with which the key material was encrypted.
      * @param keyMaterial The encrypted bytes of the key material.
+     * @param keyMetadata The metadata that will be authenticated (but unencrypted) together with
+     *     the key material when the key is uploaded to cloud.
      * @param platformKeyGenerationId The generation ID of the key used to wrap this key.
      * @param recoveryStatus recovery status of the key.
      *
      * @hide
      */
-    public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId,
-            int recoveryStatus) {
+    public WrappedKey(byte[] nonce, byte[] keyMaterial, @Nullable byte[] keyMetadata,
+            int platformKeyGenerationId, int recoveryStatus) {
         mNonce = nonce;
         mKeyMaterial = keyMaterial;
+        mKeyMetadata = keyMetadata;
         mPlatformKeyGenerationId = platformKeyGenerationId;
         mRecoveryStatus = recoveryStatus;
     }
@@ -154,6 +161,15 @@
     }
 
     /**
+     * Returns the key metadata.
+     *
+     * @hide
+     */
+    public @Nullable byte[] getKeyMetadata() {
+        return mKeyMetadata;
+    }
+
+    /**
      * Returns the generation ID of the platform key, with which this key was wrapped.
      *
      * @hide
@@ -181,12 +197,12 @@
      *
      * @hide
      */
-    public static Map<String, SecretKey> unwrapKeys(
+    public static Map<String, Pair<SecretKey, byte[]>> unwrapKeys(
             PlatformDecryptionKey platformKey,
             Map<String, WrappedKey> wrappedKeys)
             throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
             InvalidKeyException, InvalidAlgorithmParameterException {
-        HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
+        HashMap<String, Pair<SecretKey, byte[]>> unwrappedKeys = new HashMap<>();
         Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
         int platformKeyGenerationId = platformKey.getGenerationId();
 
@@ -219,7 +235,7 @@
                         e);
                 continue;
             }
-            unwrappedKeys.put(alias, key);
+            unwrappedKeys.put(alias, Pair.create(key, wrappedKey.getKeyMetadata()));
         }
 
         return unwrappedKeys;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
index b486834..8a19d62 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -23,19 +23,18 @@
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
-
-import static com.android.server.locksettings.recoverablekeystore.serialization
-        .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_METADATA;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
@@ -49,6 +48,9 @@
 import android.util.Base64;
 import android.util.Xml;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -59,9 +61,6 @@
 import java.util.List;
 import java.util.Locale;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 /**
  * Deserializes a {@link android.security.keystore.recovery.KeyChainSnapshot} instance from XML.
  */
@@ -191,6 +190,10 @@
                     builder.setEncryptedKeyMaterial(readBlobTag(parser, TAG_KEY_MATERIAL));
                     break;
 
+                case TAG_KEY_METADATA:
+                    builder.setMetadata(readBlobTag(parser, TAG_KEY_METADATA));
+                    break;
+
                 default:
                     throw new KeyChainSnapshotParserException(String.format(
                             Locale.US, "Unexpected tag %s in wrappedApplicationKey", name));
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
index 0f2c2fc..8f85a27 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -52,6 +52,7 @@
     static final String TAG_APPLICATION_KEY = "applicationKey";
     static final String TAG_ALIAS = "alias";
     static final String TAG_KEY_MATERIAL = "keyMaterial";
+    static final String TAG_KEY_METADATA = "keyMetadata";
 
     // Statics only
     private KeyChainSnapshotSchema() {}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
index 235df69..527e879 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -24,25 +24,24 @@
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
-
-import static com.android.server.locksettings.recoverablekeystore.serialization
-        .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_METADATA;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
 
+import android.annotation.Nullable;
 import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
 import android.security.keystore.recovery.KeyDerivationParams;
@@ -103,6 +102,7 @@
             XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException {
         writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias());
         writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial());
+        writePropertyTag(xmlSerializer, TAG_KEY_METADATA, applicationKey.getMetadata());
     }
 
     private static void writeKeyChainProtectionParams(
@@ -181,8 +181,11 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, byte[] propertyValue)
+            XmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue)
             throws IOException {
+        if (propertyValue == null) {
+            return;
+        }
         xmlSerializer.startTag(NAMESPACE, propertyName);
         xmlSerializer.text(Base64.encodeToString(propertyValue, /*flags=*/ Base64.DEFAULT));
         xmlSerializer.endTag(NAMESPACE, propertyName);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index e69f73d..dffaffe 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -104,6 +104,12 @@
         values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, LAST_SYNCED_AT_UNSYNCED);
         values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
         values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, wrappedKey.getRecoveryStatus());
+        byte[] keyMetadata = wrappedKey.getKeyMetadata();
+        if (keyMetadata == null) {
+            values.putNull(KeysEntry.COLUMN_NAME_KEY_METADATA);
+        } else {
+            values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, keyMetadata);
+        }
         return db.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
     }
 
@@ -119,7 +125,8 @@
                 KeysEntry.COLUMN_NAME_NONCE,
                 KeysEntry.COLUMN_NAME_WRAPPED_KEY,
                 KeysEntry.COLUMN_NAME_GENERATION_ID,
-                KeysEntry.COLUMN_NAME_RECOVERY_STATUS};
+                KeysEntry.COLUMN_NAME_RECOVERY_STATUS,
+                KeysEntry.COLUMN_NAME_KEY_METADATA};
         String selection =
                 KeysEntry.COLUMN_NAME_UID + " = ? AND "
                 + KeysEntry.COLUMN_NAME_ALIAS + " = ?";
@@ -155,7 +162,17 @@
                     cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_GENERATION_ID));
             int recoveryStatus = cursor.getInt(
                     cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS));
-            return new WrappedKey(nonce, keyMaterial, generationId, recoveryStatus);
+
+            // Retrieve the metadata associated with the key
+            byte[] keyMetadata;
+            int metadataIdx = cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_KEY_METADATA);
+            if (cursor.isNull(metadataIdx)) {
+                keyMetadata = null;
+            } else {
+                keyMetadata = cursor.getBlob(metadataIdx);
+            }
+
+            return new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, recoveryStatus);
         }
     }
 
@@ -252,7 +269,8 @@
                 KeysEntry.COLUMN_NAME_NONCE,
                 KeysEntry.COLUMN_NAME_WRAPPED_KEY,
                 KeysEntry.COLUMN_NAME_ALIAS,
-                KeysEntry.COLUMN_NAME_RECOVERY_STATUS};
+                KeysEntry.COLUMN_NAME_RECOVERY_STATUS,
+                KeysEntry.COLUMN_NAME_KEY_METADATA};
         String selection =
                 KeysEntry.COLUMN_NAME_USER_ID + " = ? AND "
                 + KeysEntry.COLUMN_NAME_UID + " = ? AND "
@@ -283,8 +301,18 @@
                         cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_ALIAS));
                 int recoveryStatus = cursor.getInt(
                         cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS));
-                keys.put(alias, new WrappedKey(nonce, keyMaterial, platformKeyGenerationId,
-                        recoveryStatus));
+
+                // Retrieve the metadata associated with the key
+                byte[] keyMetadata;
+                int metadataIdx = cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_KEY_METADATA);
+                if (cursor.isNull(metadataIdx)) {
+                    keyMetadata = null;
+                } else {
+                    keyMetadata = cursor.getBlob(metadataIdx);
+                }
+
+                keys.put(alias, new WrappedKey(nonce, keyMaterial, keyMetadata,
+                        platformKeyGenerationId, recoveryStatus));
             }
             return keys;
         }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 22e77cc..b58ee4b 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -67,6 +67,12 @@
          * Status of the key sync {@code RecoveryController#setRecoveryStatus}
          */
         static final String COLUMN_NAME_RECOVERY_STATUS = "recovery_status";
+
+        /**
+         * Data blob that will be authenticated (but encrypted) together with the key when the key
+         * is uploaded to cloud.
+         */
+        static final String COLUMN_NAME_KEY_METADATA = "key_metadata";
     }
 
     /**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index 43efe9c..b0613da 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -32,7 +32,7 @@
 class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
     private static final String TAG = "RecoverableKeyStoreDbHp";
 
-    static final int DATABASE_VERSION = 4;
+    static final int DATABASE_VERSION = 5;
     private static final String DATABASE_NAME = "recoverablekeystore.db";
 
     private static final String SQL_CREATE_KEYS_ENTRY =
@@ -46,6 +46,7 @@
                     + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
                     + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
                     + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
+                    + KeysEntry.COLUMN_NAME_KEY_METADATA + " BLOB,"
                     + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
                     + KeysEntry.COLUMN_NAME_ALIAS + "))";
 
@@ -135,6 +136,11 @@
             oldVersion = 4;
         }
 
+        if (oldVersion < 5 && newVersion >= 5) {
+            upgradeDbForVersion5(db);
+            oldVersion = 5;
+        }
+
         if (oldVersion != newVersion) {
             Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version");
         }
@@ -166,6 +172,13 @@
                 /*defaultStr=*/ null);
     }
 
+    private void upgradeDbForVersion5(SQLiteDatabase db) {
+        Log.d(TAG, "Updating recoverable keystore database to version 5");
+        // adds a column to store the metadata for application keys
+        addColumnToTable(db, KeysEntry.TABLE_NAME,
+                KeysEntry.COLUMN_NAME_KEY_METADATA, "BLOB", /*defaultStr=*/ null);
+    }
+
     private static void addColumnToTable(
             SQLiteDatabase db, String tableName, String column, String columnType,
             String defaultStr) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 139d8ac..7fffe8e 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -19,7 +19,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
@@ -27,15 +26,15 @@
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
+import android.media.session.ControllerCallbackLink;
 import android.media.session.ISession;
-import android.media.session.ISessionCallback;
 import android.media.session.ISessionController;
-import android.media.session.ISessionControllerCallback;
 import android.media.session.MediaController;
 import android.media.session.MediaController.PlaybackInfo;
 import android.media.session.MediaSession;
-import android.media.session.ParcelableVolumeInfo;
+import android.media.session.MediaSession.QueueItem;
 import android.media.session.PlaybackState;
+import android.media.session.SessionCallbackLink;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -45,7 +44,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.util.Log;
@@ -56,6 +54,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This is the system implementation of a Session. Apps will interact with the
@@ -81,11 +80,11 @@
     private final ControllerStub mController;
     private final SessionStub mSession;
     private final SessionCb mSessionCb;
-    private final MediaSessionService mService;
+    private final MediaSessionService.ServiceImpl mService;
     private final Context mContext;
 
     private final Object mLock = new Object();
-    private final ArrayList<ISessionControllerCallbackHolder> mControllerCallbackHolders =
+    private final ArrayList<ControllerCallbackLinkHolder> mControllerCallbackHolders =
             new ArrayList<>();
 
     private long mFlags;
@@ -98,7 +97,7 @@
     // may result in throwing an exception.
     private MediaMetadata mMetadata;
     private PlaybackState mPlaybackState;
-    private ParceledListSlice mQueue;
+    private List<QueueItem> mQueue;
     private CharSequence mQueueTitle;
     private int mRatingType;
     // End TransportPerformer fields
@@ -117,11 +116,12 @@
     private boolean mIsActive = false;
     private boolean mDestroyed = false;
 
-    private long mDuration;
+    private long mDuration = -1;
     private String mMetadataDescription;
 
     public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
-            ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) {
+            SessionCallbackLink cb, String tag, MediaSessionService.ServiceImpl service,
+            Looper handlerLooper) {
         mOwnerPid = ownerPid;
         mOwnerUid = ownerUid;
         mUserId = userId;
@@ -250,7 +250,7 @@
      * @param useSuggested True to use adjustSuggestedStreamVolume instead of
      */
     public void adjustVolume(String packageName, String opPackageName, int pid, int uid,
-            ISessionControllerCallback caller, boolean asSystemService, int direction, int flags,
+            ControllerCallbackLink caller, boolean asSystemService, int direction, int flags,
             boolean useSuggested) {
         int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
         if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
@@ -292,7 +292,7 @@
     }
 
     private void setVolumeTo(String packageName, String opPackageName, int pid, int uid,
-            ISessionControllerCallback caller, int value, int flags) {
+            ControllerCallbackLink caller, int value, int flags) {
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
             final int volumeValue = value;
@@ -441,7 +441,7 @@
         }
     }
 
-    public ISessionCallback getCallback() {
+    public SessionCallbackLink getCallback() {
         return mSessionCb.mCb;
     }
 
@@ -469,7 +469,7 @@
                 + ", max=" + mMaxVolume + ", current=" + mCurrentVolume);
         pw.println(indent + "metadata: " + mMetadataDescription);
         pw.println(indent + "queueTitle=" + mQueueTitle + ", size="
-                + (mQueue == null ? 0 : mQueue.getList().size()));
+                + (mQueue == null ? 0 : mQueue.size()));
     }
 
     @Override
@@ -519,7 +519,7 @@
     }
 
     private void logCallbackException(
-            String msg, ISessionControllerCallbackHolder holder, Exception e) {
+            String msg, ControllerCallbackLinkHolder holder, Exception e) {
         Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName
                 + ", exception=" + e);
     }
@@ -530,16 +530,18 @@
                 return;
             }
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onPlaybackStateChanged(mPlaybackState);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removed dead callback in pushPlaybackStateUpdate",
-                            holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushPlaybackStateUpdate",
-                            holder, e);
+                    holder.mCallback.notifyPlaybackStateChanged(mPlaybackState);
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushPlaybackStateUpdate",
+                                holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushPlaybackStateUpdate",
+                                holder, e);
+                    }
                 }
             }
         }
@@ -551,14 +553,18 @@
                 return;
             }
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onMetadataChanged(mMetadata);
-                } catch (DeadObjectException e) {
-                    logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
-                    mControllerCallbackHolders.remove(i);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushMetadataUpdate", holder, e);
+                    holder.mCallback.notifyMetadataChanged(mMetadata);
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushMetadataUpdate",
+                                holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushMetadataUpdate",
+                                holder, e);
+                    }
                 }
             }
         }
@@ -570,14 +576,17 @@
                 return;
             }
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onQueueChanged(mQueue);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removed dead callback in pushQueueUpdate", holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
+                    holder.mCallback.notifyQueueChanged(mQueue);
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushQueueUpdate",
+                                holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
+                    }
                 }
             }
         }
@@ -589,16 +598,18 @@
                 return;
             }
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onQueueTitleChanged(mQueueTitle);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removed dead callback in pushQueueTitleUpdate",
-                            holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushQueueTitleUpdate",
-                            holder, e);
+                    holder.mCallback.notifyQueueTitleChanged(mQueueTitle);
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushQueueTitleUpdate",
+                                holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushQueueTitleUpdate",
+                                holder, e);
+                    }
                 }
             }
         }
@@ -610,14 +621,17 @@
                 return;
             }
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onExtrasChanged(mExtras);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removed dead callback in pushExtrasUpdate", holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
+                    holder.mCallback.notifyExtrasChanged(mExtras);
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushExtrasUpdate",
+                                holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
+                    }
                 }
             }
         }
@@ -628,16 +642,19 @@
             if (mDestroyed) {
                 return;
             }
-            ParcelableVolumeInfo info = mController.getVolumeAttributes();
+            PlaybackInfo info = mController.getVolumeAttributes();
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onVolumeInfoChanged(info);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("Unexpected exception in pushVolumeUpdate", holder, e);
+                    holder.mCallback.notifyVolumeInfoChanged(info);
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushVolumeUpdate",
+                                holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushVolumeUpdate", holder, e);
+                    }
                 }
             }
         }
@@ -649,14 +666,16 @@
                 return;
             }
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onEvent(event, data);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removing dead callback in pushEvent", holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushEvent", holder, e);
+                    holder.mCallback.notifyEvent(event, data);
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushEvent", holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushEvent", holder, e);
+                    }
                 }
             }
         }
@@ -670,14 +689,18 @@
                 return;
             }
             for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+                ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
                 try {
-                    holder.mCallback.onSessionDestroyed();
-                } catch (DeadObjectException e) {
-                    logCallbackException("Removing dead callback in pushEvent", holder, e);
-                    mControllerCallbackHolders.remove(i);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushEvent", holder, e);
+                    holder.mCallback.notifySessionDestroyed();
+                } catch (RuntimeException e) {
+                    if (e.getCause() instanceof DeadObjectException) {
+                        mControllerCallbackHolders.remove(i);
+                        logCallbackException("Removing dead callback in pushSessionDestroyed",
+                                holder, e);
+                    } else {
+                        logCallbackException("unexpected exception in pushSessionDestroyed",
+                                holder, e);
+                    }
                 }
             }
             // After notifying clear all listeners
@@ -717,10 +740,10 @@
         return result == null ? state : result;
     }
 
-    private int getControllerHolderIndexForCb(ISessionControllerCallback cb) {
-        IBinder binder = cb.asBinder();
+    private int getControllerHolderIndexForCb(ControllerCallbackLink cb) {
+        IBinder binder = cb.getBinder();
         for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-            if (binder.equals(mControllerCallbackHolders.get(i).mCallback.asBinder())) {
+            if (binder.equals(mControllerCallbackHolders.get(i).mCallback.getBinder())) {
                 return i;
             }
         }
@@ -844,7 +867,7 @@
         }
 
         @Override
-        public void setQueue(ParceledListSlice queue) {
+        public void setQueue(List<QueueItem> queue) {
             synchronized (mLock) {
                 mQueue = queue;
             }
@@ -921,9 +944,9 @@
     }
 
     class SessionCb {
-        private final ISessionCallback mCb;
+        private final SessionCallbackLink mCb;
 
-        public SessionCb(ISessionCallback cb) {
+        SessionCb(SessionCallbackLink cb) {
             mCb = cb;
         }
 
@@ -931,224 +954,224 @@
                 boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
             try {
                 if (asSystemService) {
-                    mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
+                    mCb.notifyMediaButton(mContext.getPackageName(), Process.myPid(),
                             Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb);
                 } else {
-                    mCb.onMediaButton(packageName, pid, uid,
+                    mCb.notifyMediaButton(packageName, pid, uid,
                             createMediaButtonIntent(keyEvent), sequenceId, cb);
                 }
                 return true;
-            } catch (RemoteException e) {
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
             }
             return false;
         }
 
         public boolean sendMediaButton(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, boolean asSystemService,
+                ControllerCallbackLink caller, boolean asSystemService,
                 KeyEvent keyEvent) {
             try {
                 if (asSystemService) {
-                    mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
+                    mCb.notifyMediaButton(mContext.getPackageName(), Process.myPid(),
                             Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null);
                 } else {
-                    mCb.onMediaButtonFromController(packageName, pid, uid, caller,
+                    mCb.notifyMediaButtonFromController(packageName, pid, uid, caller,
                             createMediaButtonIntent(keyEvent));
                 }
                 return true;
-            } catch (RemoteException e) {
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
             }
             return false;
         }
 
         public void sendCommand(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
+                ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
             try {
-                mCb.onCommand(packageName, pid, uid, caller, command, args, cb);
-            } catch (RemoteException e) {
+                mCb.notifyCommand(packageName, pid, uid, caller, command, args, cb);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in sendCommand.", e);
             }
         }
 
         public void sendCustomAction(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String action,
+                ControllerCallbackLink caller, String action,
                 Bundle args) {
             try {
-                mCb.onCustomAction(packageName, pid, uid, caller, action, args);
-            } catch (RemoteException e) {
+                mCb.notifyCustomAction(packageName, pid, uid, caller, action, args);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in sendCustomAction.", e);
             }
         }
 
         public void prepare(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             try {
-                mCb.onPrepare(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyPrepare(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in prepare.", e);
             }
         }
 
         public void prepareFromMediaId(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String mediaId, Bundle extras) {
+                ControllerCallbackLink caller, String mediaId, Bundle extras) {
             try {
-                mCb.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
-            } catch (RemoteException e) {
+                mCb.notifyPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
             }
         }
 
         public void prepareFromSearch(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String query, Bundle extras) {
+                ControllerCallbackLink caller, String query, Bundle extras) {
             try {
-                mCb.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
-            } catch (RemoteException e) {
+                mCb.notifyPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
             }
         }
 
         public void prepareFromUri(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, Uri uri, Bundle extras) {
+                ControllerCallbackLink caller, Uri uri, Bundle extras) {
             try {
-                mCb.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
-            } catch (RemoteException e) {
+                mCb.notifyPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in prepareFromUri.", e);
             }
         }
 
-        public void play(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+        public void play(String packageName, int pid, int uid, ControllerCallbackLink caller) {
             try {
-                mCb.onPlay(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyPlay(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in play.", e);
             }
         }
 
         public void playFromMediaId(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String mediaId, Bundle extras) {
+                ControllerCallbackLink caller, String mediaId, Bundle extras) {
             try {
-                mCb.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
-            } catch (RemoteException e) {
+                mCb.notifyPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in playFromMediaId.", e);
             }
         }
 
         public void playFromSearch(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, String query, Bundle extras) {
+                ControllerCallbackLink caller, String query, Bundle extras) {
             try {
-                mCb.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
-            } catch (RemoteException e) {
+                mCb.notifyPlayFromSearch(packageName, pid, uid, caller, query, extras);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in playFromSearch.", e);
             }
         }
 
         public void playFromUri(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, Uri uri, Bundle extras) {
+                ControllerCallbackLink caller, Uri uri, Bundle extras) {
             try {
-                mCb.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
-            } catch (RemoteException e) {
+                mCb.notifyPlayFromUri(packageName, pid, uid, caller, uri, extras);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in playFromUri.", e);
             }
         }
 
         public void skipToTrack(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, long id) {
+                ControllerCallbackLink caller, long id) {
             try {
-                mCb.onSkipToTrack(packageName, pid, uid, caller, id);
-            } catch (RemoteException e) {
+                mCb.notifySkipToTrack(packageName, pid, uid, caller, id);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in skipToTrack", e);
             }
         }
 
-        public void pause(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+        public void pause(String packageName, int pid, int uid, ControllerCallbackLink caller) {
             try {
-                mCb.onPause(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyPause(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in pause.", e);
             }
         }
 
-        public void stop(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+        public void stop(String packageName, int pid, int uid, ControllerCallbackLink caller) {
             try {
-                mCb.onStop(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyStop(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in stop.", e);
             }
         }
 
-        public void next(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+        public void next(String packageName, int pid, int uid, ControllerCallbackLink caller) {
             try {
-                mCb.onNext(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyNext(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in next.", e);
             }
         }
 
         public void previous(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             try {
-                mCb.onPrevious(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyPrevious(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in previous.", e);
             }
         }
 
         public void fastForward(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             try {
-                mCb.onFastForward(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyFastForward(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in fastForward.", e);
             }
         }
 
         public void rewind(String packageName, int pid, int uid,
-                ISessionControllerCallback caller) {
+                ControllerCallbackLink caller) {
             try {
-                mCb.onRewind(packageName, pid, uid, caller);
-            } catch (RemoteException e) {
+                mCb.notifyRewind(packageName, pid, uid, caller);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in rewind.", e);
             }
         }
 
-        public void seekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
+        public void seekTo(String packageName, int pid, int uid, ControllerCallbackLink caller,
                 long pos) {
             try {
-                mCb.onSeekTo(packageName, pid, uid, caller, pos);
-            } catch (RemoteException e) {
+                mCb.notifySeekTo(packageName, pid, uid, caller, pos);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in seekTo.", e);
             }
         }
 
-        public void rate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+        public void rate(String packageName, int pid, int uid, ControllerCallbackLink caller,
                 Rating rating) {
             try {
-                mCb.onRate(packageName, pid, uid, caller, rating);
-            } catch (RemoteException e) {
+                mCb.notifyRate(packageName, pid, uid, caller, rating);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in rate.", e);
             }
         }
 
         public void adjustVolume(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, boolean asSystemService, int direction) {
+                ControllerCallbackLink caller, boolean asSystemService, int direction) {
             try {
                 if (asSystemService) {
-                    mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(),
+                    mCb.notifyAdjustVolume(mContext.getPackageName(), Process.myPid(),
                             Process.SYSTEM_UID, null, direction);
                 } else {
-                    mCb.onAdjustVolume(packageName, pid, uid, caller, direction);
+                    mCb.notifyAdjustVolume(packageName, pid, uid, caller, direction);
                 }
-            } catch (RemoteException e) {
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in adjustVolume.", e);
             }
         }
 
         public void setVolumeTo(String packageName, int pid, int uid,
-                ISessionControllerCallback caller, int value) {
+                ControllerCallbackLink caller, int value) {
             try {
-                mCb.onSetVolumeTo(packageName, pid, uid, caller, value);
-            } catch (RemoteException e) {
+                mCb.notifySetVolumeTo(packageName, pid, uid, caller, value);
+            } catch (RuntimeException e) {
                 Slog.e(TAG, "Remote failure in setVolumeTo.", e);
             }
         }
@@ -1162,34 +1185,34 @@
 
     class ControllerStub extends ISessionController.Stub {
         @Override
-        public void sendCommand(String packageName, ISessionControllerCallback caller,
+        public void sendCommand(String packageName, ControllerCallbackLink caller,
                 String command, Bundle args, ResultReceiver cb) {
             mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller, command, args, cb);
         }
 
         @Override
-        public boolean sendMediaButton(String packageName, ISessionControllerCallback cb,
+        public boolean sendMediaButton(String packageName, ControllerCallbackLink cb,
                 boolean asSystemService, KeyEvent keyEvent) {
             return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(),
                     Binder.getCallingUid(), cb, asSystemService, keyEvent);
         }
 
         @Override
-        public void registerCallbackListener(String packageName, ISessionControllerCallback cb) {
+        public void registerCallbackListener(String packageName, ControllerCallbackLink cb) {
             synchronized (mLock) {
                 // If this session is already destroyed tell the caller and
                 // don't add them.
                 if (mDestroyed) {
                     try {
-                        cb.onSessionDestroyed();
+                        cb.notifySessionDestroyed();
                     } catch (Exception e) {
                         // ignored
                     }
                     return;
                 }
                 if (getControllerHolderIndexForCb(cb) < 0) {
-                    mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb,
+                    mControllerCallbackHolders.add(new ControllerCallbackLinkHolder(cb,
                             packageName, Binder.getCallingUid()));
                     if (DEBUG) {
                         Log.d(TAG, "registering controller callback " + cb + " from controller"
@@ -1200,14 +1223,14 @@
         }
 
         @Override
-        public void unregisterCallbackListener(ISessionControllerCallback cb) {
+        public void unregisterCallbackListener(ControllerCallbackLink cb) {
             synchronized (mLock) {
                 int index = getControllerHolderIndexForCb(cb);
                 if (index != -1) {
                     mControllerCallbackHolders.remove(index);
                 }
                 if (DEBUG) {
-                    Log.d(TAG, "unregistering callback " + cb.asBinder());
+                    Log.d(TAG, "unregistering callback " + cb.getBinder());
                 }
             }
         }
@@ -1233,14 +1256,14 @@
         }
 
         @Override
-        public ParcelableVolumeInfo getVolumeAttributes() {
+        public PlaybackInfo getVolumeAttributes() {
             int volumeType;
             AudioAttributes attributes;
             synchronized (mLock) {
                 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
                     int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume;
-                    return new ParcelableVolumeInfo(
-                            mVolumeType, mAudioAttrs, mVolumeControlType, mMaxVolume, current);
+                    return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current,
+                            mAudioAttrs);
                 }
                 volumeType = mVolumeType;
                 attributes = mAudioAttrs;
@@ -1248,13 +1271,13 @@
             int stream = AudioAttributes.toLegacyStreamType(attributes);
             int max = mAudioManager.getStreamMaxVolume(stream);
             int current = mAudioManager.getStreamVolume(stream);
-            return new ParcelableVolumeInfo(
-                    volumeType, attributes, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, current);
+            return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max,
+                    current, attributes);
         }
 
         @Override
         public void adjustVolume(String packageName, String opPackageName,
-                ISessionControllerCallback caller, boolean asSystemService, int direction,
+                ControllerCallbackLink caller, boolean asSystemService, int direction,
                 int flags) {
             int pid = Binder.getCallingPid();
             int uid = Binder.getCallingUid();
@@ -1269,7 +1292,7 @@
 
         @Override
         public void setVolumeTo(String packageName, String opPackageName,
-                ISessionControllerCallback caller, int value, int flags) {
+                ControllerCallbackLink caller, int value, int flags) {
             int pid = Binder.getCallingPid();
             int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
@@ -1282,110 +1305,110 @@
         }
 
         @Override
-        public void prepare(String packageName, ISessionControllerCallback caller) {
+        public void prepare(String packageName, ControllerCallbackLink caller) {
             mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
         }
 
         @Override
-        public void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
+        public void prepareFromMediaId(String packageName, ControllerCallbackLink caller,
                 String mediaId, Bundle extras) {
             mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(),
                     Binder.getCallingUid(), caller, mediaId, extras);
         }
 
         @Override
-        public void prepareFromSearch(String packageName, ISessionControllerCallback caller,
+        public void prepareFromSearch(String packageName, ControllerCallbackLink caller,
                 String query, Bundle extras) {
             mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(),
                     Binder.getCallingUid(), caller, query, extras);
         }
 
         @Override
-        public void prepareFromUri(String packageName, ISessionControllerCallback caller,
+        public void prepareFromUri(String packageName, ControllerCallbackLink caller,
                 Uri uri, Bundle extras) {
             mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller, uri, extras);
         }
 
         @Override
-        public void play(String packageName, ISessionControllerCallback caller) {
+        public void play(String packageName, ControllerCallbackLink caller) {
             mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
         }
 
         @Override
-        public void playFromMediaId(String packageName, ISessionControllerCallback caller,
+        public void playFromMediaId(String packageName, ControllerCallbackLink caller,
                 String mediaId, Bundle extras) {
             mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller, mediaId, extras);
         }
 
         @Override
-        public void playFromSearch(String packageName, ISessionControllerCallback caller,
+        public void playFromSearch(String packageName, ControllerCallbackLink caller,
                 String query, Bundle extras) {
             mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller, query, extras);
         }
 
         @Override
-        public void playFromUri(String packageName, ISessionControllerCallback caller,
+        public void playFromUri(String packageName, ControllerCallbackLink caller,
                 Uri uri, Bundle extras) {
             mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller, uri, extras);
         }
 
         @Override
-        public void skipToQueueItem(String packageName, ISessionControllerCallback caller,
+        public void skipToQueueItem(String packageName, ControllerCallbackLink caller,
                 long id) {
             mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller, id);
         }
 
         @Override
-        public void pause(String packageName, ISessionControllerCallback caller) {
+        public void pause(String packageName, ControllerCallbackLink caller) {
             mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
         }
 
         @Override
-        public void stop(String packageName, ISessionControllerCallback caller) {
+        public void stop(String packageName, ControllerCallbackLink caller) {
             mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
         }
 
         @Override
-        public void next(String packageName, ISessionControllerCallback caller) {
+        public void next(String packageName, ControllerCallbackLink caller) {
             mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
         }
 
         @Override
-        public void previous(String packageName, ISessionControllerCallback caller) {
+        public void previous(String packageName, ControllerCallbackLink caller) {
             mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller);
         }
 
         @Override
-        public void fastForward(String packageName, ISessionControllerCallback caller) {
+        public void fastForward(String packageName, ControllerCallbackLink caller) {
             mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller);
         }
 
         @Override
-        public void rewind(String packageName, ISessionControllerCallback caller) {
+        public void rewind(String packageName, ControllerCallbackLink caller) {
             mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
         }
 
         @Override
-        public void seekTo(String packageName, ISessionControllerCallback caller, long pos) {
+        public void seekTo(String packageName, ControllerCallbackLink caller, long pos) {
             mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller,
                     pos);
         }
 
         @Override
-        public void rate(String packageName, ISessionControllerCallback caller, Rating rating) {
+        public void rate(String packageName, ControllerCallbackLink caller, Rating rating) {
             mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller,
                     rating);
         }
 
         @Override
-        public void sendCustomAction(String packageName, ISessionControllerCallback caller,
+        public void sendCustomAction(String packageName, ControllerCallbackLink caller,
                 String action, Bundle args) {
             mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
                     caller, action, args);
@@ -1404,7 +1427,7 @@
         }
 
         @Override
-        public ParceledListSlice getQueue() {
+        public List<QueueItem> getQueue() {
             synchronized (mLock) {
                 return mQueue;
             }
@@ -1433,12 +1456,12 @@
         }
     }
 
-    private class ISessionControllerCallbackHolder {
-        private final ISessionControllerCallback mCallback;
+    private class ControllerCallbackLinkHolder {
+        private final ControllerCallbackLink mCallback;
         private final String mPackageName;
         private final int mUid;
 
-        ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName,
+        ControllerCallbackLinkHolder(ControllerCallbackLink callback, String packageName,
                 int uid) {
             mCallback = callback;
             mPackageName = packageName;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d51da99..d20ed8c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,71 +16,15 @@
 
 package com.android.server.media;
 
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.INotificationManager;
-import android.app.KeyguardManager;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.media.AudioManager;
-import android.media.AudioManagerInternal;
-import android.media.AudioPlaybackConfiguration;
-import android.media.AudioSystem;
-import android.media.IAudioService;
-import android.media.IRemoteVolumeController;
-import android.media.session.IActiveSessionsListener;
-import android.media.session.ICallback;
-import android.media.session.IOnMediaKeyListener;
-import android.media.session.IOnVolumeKeyLongPressListener;
-import android.media.session.ISession;
-import android.media.session.ISessionCallback;
-import android.media.session.ISessionManager;
-import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
+import android.media.Session2Token;
 import android.os.IBinder;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.speech.RecognizerIntent;
-import android.text.TextUtils;
 import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
 
-import com.android.internal.util.DumpUtils;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
 import com.android.server.Watchdog.Monitor;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -89,1829 +33,124 @@
 public class MediaSessionService extends SystemService implements Monitor {
     private static final String TAG = "MediaSessionService";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    // Leave log for key event always.
-    private static final boolean DEBUG_KEY_EVENT = true;
 
-    private static final int WAKELOCK_TIMEOUT = 5000;
-    private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
-
-    private final SessionManagerImpl mSessionManagerImpl;
-
-    // Keeps the full user id for each user.
-    private final SparseIntArray mFullUserIds = new SparseIntArray();
-    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
-    private final ArrayList<SessionsListenerRecord> mSessionsListeners
-            = new ArrayList<SessionsListenerRecord>();
-    private final Object mLock = new Object();
-    private final MessageHandler mHandler = new MessageHandler();
-    private final PowerManager.WakeLock mMediaEventWakeLock;
-    private final int mLongPressTimeout;
-    private final INotificationManager mNotificationManager;
-
-    private KeyguardManager mKeyguardManager;
-    private IAudioService mAudioService;
-    private AudioManagerInternal mAudioManagerInternal;
-    private ActivityManager mActivityManager;
-    private ContentResolver mContentResolver;
-    private SettingsObserver mSettingsObserver;
-    private boolean mHasFeatureLeanback;
-
-    // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
-    // It's always not null after the MediaSessionService is started.
-    private FullUserRecord mCurrentFullUserRecord;
-    private MediaSessionRecord mGlobalPrioritySession;
-    private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
-
-    // Used to notify system UI when remote volume was changed. TODO find a
-    // better way to handle this.
-    private IRemoteVolumeController mRvc;
+    private final ServiceImpl mImpl;
 
     public MediaSessionService(Context context) {
         super(context);
-        mSessionManagerImpl = new SessionManagerImpl();
-        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
-        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mImpl = new MediaSessionServiceImpl(context);
     }
 
     @Override
     public void onStart() {
-        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
+        publishBinderService(Context.MEDIA_SESSION_SERVICE, mImpl.getServiceBinder());
         Watchdog.getInstance().addMonitor(this);
-        mKeyguardManager =
-                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
-        mAudioService = getAudioService();
-        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
-        mActivityManager =
-                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
-        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
-        mAudioPlayerStateMonitor.registerListener(
-                (config, isRemoved) -> {
-                    if (isRemoved || !config.isActive() || config.getPlayerType()
-                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
-                        return;
-                    }
-                    synchronized (mLock) {
-                        FullUserRecord user = getFullUserRecordLocked(
-                                UserHandle.getUserId(config.getClientUid()));
-                        if (user != null) {
-                            user.mPriorityStack.updateMediaButtonSessionIfNeeded();
-                        }
-                    }
-                }, null /* handler */);
-        mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
-        mContentResolver = getContext().getContentResolver();
-        mSettingsObserver = new SettingsObserver();
-        mSettingsObserver.observe();
-        mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_LEANBACK);
 
-        updateUser();
+        mImpl.onStart();
     }
 
-    private IAudioService getAudioService() {
-        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
-        return IAudioService.Stub.asInterface(b);
+    @Override
+    public void onStartUser(int userId) {
+        mImpl.onStartUser(userId);
     }
 
-    private boolean isGlobalPriorityActiveLocked() {
-        return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
+    @Override
+    public void onSwitchUser(int userId) {
+        mImpl.onSwitchUser(userId);
     }
 
+    // Called when the user with the userId is removed.
+    @Override
+    public void onStopUser(int userId) {
+        mImpl.onStopUser(userId);
+    }
+
+    @Override
+    public void monitor() {
+        mImpl.monitor();
+    }
+
+    /**
+     * Updates session.
+     */
     public void updateSession(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (user == null) {
-                Log.w(TAG, "Unknown session updated. Ignoring.");
-                return;
-            }
-            if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
-                if (DEBUG_KEY_EVENT) {
-                    Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
-                }
-                user.pushAddressedPlayerChangedLocked();
-            } else {
-                if (!user.mPriorityStack.contains(record)) {
-                    Log.w(TAG, "Unknown session updated. Ignoring.");
-                    return;
-                }
-                user.mPriorityStack.onSessionStateChange(record);
-            }
-            mHandler.postSessionsChanged(record.getUserId());
-        }
+        mImpl.updateSession(record);
     }
 
+    /**
+     * Sets global priority session.
+     */
     public void setGlobalPrioritySession(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (mGlobalPrioritySession != record) {
-                Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
-                        + " to " + record);
-                mGlobalPrioritySession = record;
-                if (user != null && user.mPriorityStack.contains(record)) {
-                    // Handle the global priority session separately.
-                    // Otherwise, it can be the media button session regardless of the active state
-                    // because it or other system components might have been the lastly played media
-                    // app.
-                    user.mPriorityStack.removeSession(record);
-                }
-            }
-        }
+        mImpl.setGlobalPrioritySession(record);
     }
 
-    private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
-        List<MediaSessionRecord> records = new ArrayList<>();
-        if (userId == UserHandle.USER_ALL) {
-            int size = mUserRecords.size();
-            for (int i = 0; i < size; i++) {
-                records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
-            }
-        } else {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user == null) {
-                Log.w(TAG, "getSessions failed. Unknown user " + userId);
-                return records;
-            }
-            records.addAll(user.mPriorityStack.getActiveSessions(userId));
-        }
-
-        // Return global priority session at the first whenever it's asked.
-        if (isGlobalPriorityActiveLocked()
-                && (userId == UserHandle.USER_ALL
-                    || userId == mGlobalPrioritySession.getUserId())) {
-            records.add(0, mGlobalPrioritySession);
-        }
-        return records;
+    List<Session2Token> getSession2TokensLocked(int userId) {
+        return mImpl.getSession2TokensLocked(userId);
     }
 
     /**
      * Tells the system UI that volume has changed on an active remote session.
      */
     public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
-        if (mRvc == null || !session.isActive()) {
-            return;
-        }
-        try {
-            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
-        } catch (Exception e) {
-            Log.wtf(TAG, "Error sending volume change to system UI.", e);
-        }
+        mImpl.notifyRemoteVolumeChanged(flags, session);
     }
 
+    /**
+     * Called when session playstate is changed.
+     */
     public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (user == null || !user.mPriorityStack.contains(record)) {
-                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
-                return;
-            }
-            user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
-        }
+        mImpl.onSessionPlaystateChanged(record, oldState, newState);
     }
 
+    /**
+     * Called when session playback type is changed.
+     */
     public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (user == null || !user.mPriorityStack.contains(record)) {
-                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
-                return;
-            }
-            pushRemoteVolumeUpdateLocked(record.getUserId());
-        }
-    }
-
-    @Override
-    public void onStartUser(int userId) {
-        if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
-        updateUser();
-    }
-
-    @Override
-    public void onSwitchUser(int userId) {
-        if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
-        updateUser();
-    }
-
-    @Override
-    public void onStopUser(int userId) {
-        if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user != null) {
-                if (user.mFullUserId == userId) {
-                    user.destroySessionsForUserLocked(UserHandle.USER_ALL);
-                    mUserRecords.remove(userId);
-                } else {
-                    user.destroySessionsForUserLocked(userId);
-                }
-            }
-            updateUser();
-        }
-    }
-
-    @Override
-    public void monitor() {
-        synchronized (mLock) {
-            // Check for deadlock
-        }
+        mImpl.onSessionPlaybackTypeChanged(record);
     }
 
     protected void enforcePhoneStatePermission(int pid, int uid) {
-        if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
-        }
+        mImpl.enforcePhoneStatePermission(pid, uid);
     }
 
     void sessionDied(MediaSessionRecord session) {
-        synchronized (mLock) {
-            destroySessionLocked(session);
-        }
+        mImpl.sessionDied(session);
     }
 
     void destroySession(MediaSessionRecord session) {
-        synchronized (mLock) {
-            destroySessionLocked(session);
-        }
+        mImpl.destroySession(session);
     }
 
-    private void updateUser() {
-        synchronized (mLock) {
-            UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
-            mFullUserIds.clear();
-            List<UserInfo> allUsers = manager.getUsers();
-            if (allUsers != null) {
-                for (UserInfo userInfo : allUsers) {
-                    if (userInfo.isManagedProfile()) {
-                        mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
-                    } else {
-                        mFullUserIds.put(userInfo.id, userInfo.id);
-                        if (mUserRecords.get(userInfo.id) == null) {
-                            mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
-                        }
-                    }
-                }
-            }
-            // Ensure that the current full user exists.
-            int currentFullUserId = ActivityManager.getCurrentUser();
-            mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
-            if (mCurrentFullUserRecord == null) {
-                Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
-                mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
-                mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
-            }
-            mFullUserIds.put(currentFullUserId, currentFullUserId);
-        }
-    }
-
-    private void updateActiveSessionListeners() {
-        synchronized (mLock) {
-            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
-                SessionsListenerRecord listener = mSessionsListeners.get(i);
-                try {
-                    enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
-                            listener.mUserId);
-                } catch (SecurityException e) {
-                    Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
-                            + " is no longer authorized. Disconnecting.");
-                    mSessionsListeners.remove(i);
-                    try {
-                        listener.mListener
-                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
-                    } catch (Exception e1) {
-                        // ignore
-                    }
-                }
-            }
-        }
-    }
-
-    /*
-     * When a session is removed several things need to happen.
-     * 1. We need to remove it from the relevant user.
-     * 2. We need to remove it from the priority stack.
-     * 3. We need to remove it from all sessions.
-     * 4. If this is the system priority session we need to clear it.
-     * 5. We need to unlink to death from the cb binder
-     * 6. We need to tell the session to do any final cleanup (onDestroy)
-     */
-    private void destroySessionLocked(MediaSessionRecord session) {
-        if (DEBUG) {
-            Log.d(TAG, "Destroying " + session);
-        }
-        FullUserRecord user = getFullUserRecordLocked(session.getUserId());
-        if (mGlobalPrioritySession == session) {
-            mGlobalPrioritySession = null;
-            if (session.isActive() && user != null) {
-                user.pushAddressedPlayerChangedLocked();
-            }
-        } else {
-            if (user != null) {
-                user.mPriorityStack.removeSession(session);
-            }
-        }
-
-        try {
-            session.getCallback().asBinder().unlinkToDeath(session, 0);
-        } catch (Exception e) {
-            // ignore exceptions while destroying a session.
-        }
-        session.onDestroy();
-        mHandler.postSessionsChanged(session.getUserId());
-    }
-
-    private void enforcePackageName(String packageName, int uid) {
-        if (TextUtils.isEmpty(packageName)) {
-            throw new IllegalArgumentException("packageName may not be empty");
-        }
-        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
-        final int packageCount = packages.length;
-        for (int i = 0; i < packageCount; i++) {
-            if (packageName.equals(packages[i])) {
-                return;
-            }
-        }
-        throw new IllegalArgumentException("packageName is not owned by the calling process");
+    void pushSession2TokensChangedLocked(int userId) {
+        mImpl.pushSession2TokensChangedLocked(userId);
     }
 
     /**
-     * Checks a caller's authorization to register an IRemoteControlDisplay.
-     * Authorization is granted if one of the following is true:
-     * <ul>
-     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
-     * permission</li>
-     * <li>the caller's listener is one of the enabled notification listeners
-     * for the caller's user</li>
-     * </ul>
-     */
-    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
-            int resolvedUserId) {
-        if (isCurrentVolumeController(pid, uid)) return;
-        if (getContext()
-                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
-                    != PackageManager.PERMISSION_GRANTED
-                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
-                        resolvedUserId)) {
-            throw new SecurityException("Missing permission to control media.");
-        }
-    }
-
-    private boolean isCurrentVolumeController(int pid, int uid) {
-        return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
-                pid, uid) == PackageManager.PERMISSION_GRANTED;
-    }
-
-    private void enforceSystemUiPermission(String action, int pid, int uid) {
-        if (!isCurrentVolumeController(pid, uid)) {
-            throw new SecurityException("Only system ui may " + action);
-        }
-    }
-
-    /**
-     * This checks if the component is an enabled notification listener for the
-     * specified user. Enabled components may only operate on behalf of the user
-     * they're running as.
-     *
-     * @param compName The component that is enabled.
-     * @param userId The user id of the caller.
-     * @param forUserId The user id they're making the request on behalf of.
-     * @return True if the component is enabled, false otherwise
-     */
-    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
-            int forUserId) {
-        if (userId != forUserId) {
-            // You may not access another user's content as an enabled listener.
-            return false;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Checking if enabled notification listener " + compName);
-        }
-        if (compName != null) {
-            try {
-                return mNotificationManager.isNotificationListenerAccessGrantedForUser(
-                        compName, userId);
-            } catch(RemoteException e) {
-                Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
-            }
-        }
-        return false;
-    }
-
-    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
-            String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
-        synchronized (mLock) {
-            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
-        }
-    }
-
-    /*
-     * When a session is created the following things need to happen.
-     * 1. Its callback binder needs a link to death
-     * 2. It needs to be added to all sessions.
-     * 3. It needs to be added to the priority stack.
-     * 4. It needs to be added to the relevant user record.
-     */
-    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
-            String callerPackageName, ISessionCallback cb, String tag) {
-        FullUserRecord user = getFullUserRecordLocked(userId);
-        if (user == null) {
-            Log.wtf(TAG, "Request from invalid user: " +  userId);
-            throw new RuntimeException("Session request from invalid user.");
-        }
-
-        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
-                callerPackageName, cb, tag, this, mHandler.getLooper());
-        try {
-            cb.asBinder().linkToDeath(session, 0);
-        } catch (RemoteException e) {
-            throw new RuntimeException("Media Session owner died prematurely.", e);
-        }
-
-        user.mPriorityStack.addSession(session);
-        mHandler.postSessionsChanged(userId);
-
-        if (DEBUG) {
-            Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
-        }
-        return session;
-    }
-
-    private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
-        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
-            if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private void pushSessionsChanged(int userId) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user == null) {
-                Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
-                return;
-            }
-            List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
-            int size = records.size();
-            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
-            for (int i = 0; i < size; i++) {
-                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
-            }
-            pushRemoteVolumeUpdateLocked(userId);
-            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
-                SessionsListenerRecord record = mSessionsListeners.get(i);
-                if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
-                    try {
-                        record.mListener.onActiveSessionsChanged(tokens);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
-                                e);
-                        mSessionsListeners.remove(i);
-                    }
-                }
-            }
-        }
-    }
-
-    private void pushRemoteVolumeUpdateLocked(int userId) {
-        if (mRvc != null) {
-            try {
-                FullUserRecord user = getFullUserRecordLocked(userId);
-                if (user == null) {
-                    Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
-                    return;
-                }
-                MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
-                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
-            }
-        }
-    }
-
-    /**
-     * Called when the media button receiver for the {@param record} is changed.
-     *
-     * @param record the media session whose media button receiver is updated.
+     * Called when media button receiver changed.
      */
     public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            MediaSessionRecord mediaButtonSession =
-                    user.mPriorityStack.getMediaButtonSession();
-            if (record == mediaButtonSession) {
-                user.rememberMediaButtonReceiverLocked(mediaButtonSession);
-            }
-        }
+        mImpl.onMediaButtonReceiverChanged(record);
     }
 
-    private String getCallingPackageName(int uid) {
-        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
-        if (packages != null && packages.length > 0) {
-            return packages[0];
-        }
-        return "";
-    }
-
-    private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
-        if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
-            return;
-        }
-        try {
-            mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
-        }
-    }
-
-    private FullUserRecord getFullUserRecordLocked(int userId) {
-        int fullUserId = mFullUserIds.get(userId, -1);
-        if (fullUserId < 0) {
-            return null;
-        }
-        return mUserRecords.get(fullUserId);
-    }
-
-    /**
-     * Information about a full user and its corresponding managed profiles.
-     *
-     * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
-     * them when he/she presses a media/volume button. So keeping media sessions for them in one
-     * place makes more sense and increases the readability.</p>
-     * <p>The contents of this object is guarded by {@link #mLock}.
-     */
-    final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
-        public static final int COMPONENT_TYPE_INVALID = 0;
-        public static final int COMPONENT_TYPE_BROADCAST = 1;
-        public static final int COMPONENT_TYPE_ACTIVITY = 2;
-        public static final int COMPONENT_TYPE_SERVICE = 3;
-        private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
-
-        private final int mFullUserId;
-        private final MediaSessionStack mPriorityStack;
-        private PendingIntent mLastMediaButtonReceiver;
-        private ComponentName mRestoredMediaButtonReceiver;
-        private int mRestoredMediaButtonReceiverComponentType;
-        private int mRestoredMediaButtonReceiverUserId;
-
-        private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
-        private int mOnVolumeKeyLongPressListenerUid;
-        private KeyEvent mInitialDownVolumeKeyEvent;
-        private int mInitialDownVolumeStream;
-        private boolean mInitialDownMusicOnly;
-
-        private IOnMediaKeyListener mOnMediaKeyListener;
-        private int mOnMediaKeyListenerUid;
-        private ICallback mCallback;
-
-        public FullUserRecord(int fullUserId) {
-            mFullUserId = fullUserId;
-            mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
-            // Restore the remembered media button receiver before the boot.
-            String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
-            if (mediaButtonReceiverInfo == null) {
-                return;
-            }
-            String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
-            if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
-                return;
-            }
-            mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
-            mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
-            if (tokens.length == 3) {
-                mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]);
-            } else {
-                mRestoredMediaButtonReceiverComponentType =
-                        getComponentType(mRestoredMediaButtonReceiver);
-            }
-        }
-
-        public void destroySessionsForUserLocked(int userId) {
-            List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
-            for (MediaSessionRecord session : sessions) {
-                MediaSessionService.this.destroySessionLocked(session);
-            }
-        }
-
-        public void dumpLocked(PrintWriter pw, String prefix) {
-            pw.print(prefix + "Record for full_user=" + mFullUserId);
-            // Dump managed profile user ids associated with this user.
-            int size = mFullUserIds.size();
-            for (int i = 0; i < size; i++) {
-                if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
-                        && mFullUserIds.valueAt(i) == mFullUserId) {
-                    pw.print(", profile_user=" + mFullUserIds.keyAt(i));
-                }
-            }
-            pw.println();
-            String indent = prefix + "  ";
-            pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
-            pw.println(indent + "Volume key long-press listener package: " +
-                    getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
-            pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
-            pw.println(indent + "Media key listener package: " +
-                    getCallingPackageName(mOnMediaKeyListenerUid));
-            pw.println(indent + "Callback: " + mCallback);
-            pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
-            pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
-            pw.println(indent + "Restored MediaButtonReceiverComponentType: "
-                    + mRestoredMediaButtonReceiverComponentType);
-            mPriorityStack.dump(pw, indent);
-        }
-
-        @Override
-        public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
-                MediaSessionRecord newMediaButtonSession) {
-            if (DEBUG_KEY_EVENT) {
-                Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
-            }
-            synchronized (mLock) {
-                if (oldMediaButtonSession != null) {
-                    mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
-                }
-                if (newMediaButtonSession != null) {
-                    rememberMediaButtonReceiverLocked(newMediaButtonSession);
-                    mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
-                }
-                pushAddressedPlayerChangedLocked();
-            }
-        }
-
-        // Remember media button receiver and keep it in the persistent storage.
-        public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
-            PendingIntent receiver = record.getMediaButtonReceiver();
-            mLastMediaButtonReceiver = receiver;
-            mRestoredMediaButtonReceiver = null;
-            mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
-
-            String mediaButtonReceiverInfo = "";
-            if (receiver != null) {
-                ComponentName component = receiver.getIntent().getComponent();
-                if (component != null
-                        && record.getPackageName().equals(component.getPackageName())) {
-                    String componentName = component.flattenToString();
-                    int componentType = getComponentType(component);
-                    mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM,
-                            componentName, String.valueOf(record.getUserId()),
-                            String.valueOf(componentType));
-                }
-            }
-            Settings.Secure.putStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo,
-                    mFullUserId);
-        }
-
-        private void pushAddressedPlayerChangedLocked() {
-            if (mCallback == null) {
-                return;
-            }
-            try {
-                MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
-                if (mediaButtonSession != null) {
-                    mCallback.onAddressedPlayerChangedToMediaSession(
-                            new MediaSession.Token(mediaButtonSession.getControllerBinder()));
-                } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
-                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
-                            mCurrentFullUserRecord.mLastMediaButtonReceiver
-                                    .getIntent().getComponent());
-                } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
-                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
-                            mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
-            }
-        }
-
-        private MediaSessionRecord getMediaButtonSessionLocked() {
-            return isGlobalPriorityActiveLocked()
-                    ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
-        }
-
-        private int getComponentType(@Nullable ComponentName componentName) {
-            if (componentName == null) {
-                return COMPONENT_TYPE_INVALID;
-            }
-            PackageManager pm = getContext().getPackageManager();
-            try {
-                ActivityInfo activityInfo = pm.getActivityInfo(componentName,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                | PackageManager.GET_ACTIVITIES);
-                if (activityInfo != null) {
-                    return COMPONENT_TYPE_ACTIVITY;
-                }
-            } catch (NameNotFoundException e) {
-            }
-            try {
-                ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                | PackageManager.GET_SERVICES);
-                if (serviceInfo != null) {
-                    return COMPONENT_TYPE_SERVICE;
-                }
-            } catch (NameNotFoundException e) {
-            }
-            // Pick legacy behavior for BroadcastReceiver or unknown.
-            return COMPONENT_TYPE_BROADCAST;
-        }
-    }
-
-    final class SessionsListenerRecord implements IBinder.DeathRecipient {
-        private final IActiveSessionsListener mListener;
-        private final ComponentName mComponentName;
-        private final int mUserId;
-        private final int mPid;
-        private final int mUid;
-
-        public SessionsListenerRecord(IActiveSessionsListener listener,
-                ComponentName componentName,
-                int userId, int pid, int uid) {
-            mListener = listener;
-            mComponentName = componentName;
-            mUserId = userId;
-            mPid = pid;
-            mUid = uid;
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                mSessionsListeners.remove(this);
-            }
-        }
-    }
-
-    final class SettingsObserver extends ContentObserver {
-        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
-                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
-
-        private SettingsObserver() {
-            super(null);
-        }
-
-        private void observe() {
-            mContentResolver.registerContentObserver(mSecureSettingsUri,
-                    false, this, UserHandle.USER_ALL);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            updateActiveSessionListeners();
-        }
-    }
-
-    class SessionManagerImpl extends ISessionManager.Stub {
-        private static final String EXTRA_WAKELOCK_ACQUIRED =
-                "android.media.AudioService.WAKELOCK_ACQUIRED";
-        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
-
-        private boolean mVoiceButtonDown = false;
-        private boolean mVoiceButtonHandled = false;
-
-        @Override
-        public ISession createSession(String packageName, ISessionCallback cb, String tag,
-                int userId) throws RemoteException {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                enforcePackageName(packageName, uid);
-                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
-                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
-                if (cb == null) {
-                    throw new IllegalArgumentException("Controller callback cannot be null");
-                }
-                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
-                        .getSessionBinder();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public List<IBinder> getSessions(ComponentName componentName, int userId) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
-                ArrayList<IBinder> binders = new ArrayList<IBinder>();
-                synchronized (mLock) {
-                    List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
-                    for (MediaSessionRecord record : records) {
-                        binders.add(record.getControllerBinder().asBinder());
-                    }
-                }
-                return binders;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void addSessionsListener(IActiveSessionsListener listener,
-                ComponentName componentName, int userId) throws RemoteException {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
-                synchronized (mLock) {
-                    int index = findIndexOfSessionsListenerLocked(listener);
-                    if (index != -1) {
-                        Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
-                        return;
-                    }
-                    SessionsListenerRecord record = new SessionsListenerRecord(listener,
-                            componentName, resolvedUserId, pid, uid);
-                    try {
-                        listener.asBinder().linkToDeath(record, 0);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
-                        return;
-                    }
-                    mSessionsListeners.add(record);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void removeSessionsListener(IActiveSessionsListener listener)
-                throws RemoteException {
-            synchronized (mLock) {
-                int index = findIndexOfSessionsListenerLocked(listener);
-                if (index != -1) {
-                    SessionsListenerRecord record = mSessionsListeners.remove(index);
-                    try {
-                        record.mListener.asBinder().unlinkToDeath(record, 0);
-                    } catch (Exception e) {
-                        // ignore exceptions, the record is being removed
-                    }
-                }
-            }
-        }
-
-        /**
-         * Handles the dispatching of the media button events to one of the
-         * registered listeners, or if there was none, broadcast an
-         * ACTION_MEDIA_BUTTON intent to the rest of the system.
-         *
-         * @param packageName The caller package
-         * @param asSystemService {@code true} if the event sent to the session as if it was come
-         *          from the system service instead of the app process. This helps sessions to
-         *          distinguish between the key injection by the app and key events from the
-         *          hardware devices. Should be used only when the volume key events aren't handled
-         *          by foreground activity. {@code false} otherwise to tell session about the real
-         *          caller.
-         * @param keyEvent a non-null KeyEvent whose key code is one of the
-         *            supported media buttons
-         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
-         *            while this key event is dispatched.
-         */
-        @Override
-        public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
-                KeyEvent keyEvent, boolean needWakeLock) {
-            if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
-                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
-                return;
-            }
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid
-                            + ", uid=" + uid + ", asSystem=" + asSystemService + ", event="
-                            + keyEvent);
-                }
-                if (!isUserSetupComplete()) {
-                    // Global media key handling can have the side-effect of starting new
-                    // activities which is undesirable while setup is in progress.
-                    Slog.i(TAG, "Not dispatching media key event because user "
-                            + "setup is in progress.");
-                    return;
-                }
-
-                synchronized (mLock) {
-                    boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
-                    if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
-                        // Prevent dispatching key event through reflection while the global
-                        // priority session is active.
-                        Slog.i(TAG, "Only the system can dispatch media key event "
-                                + "to the global priority session.");
-                        return;
-                    }
-                    if (!isGlobalPriorityActive) {
-                        if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
-                            if (DEBUG_KEY_EVENT) {
-                                Log.d(TAG, "Send " + keyEvent + " to the media key listener");
-                            }
-                            try {
-                                mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
-                                        new MediaKeyListenerResultReceiver(packageName, pid, uid,
-                                                asSystemService, keyEvent, needWakeLock));
-                                return;
-                            } catch (RemoteException e) {
-                                Log.w(TAG, "Failed to send " + keyEvent
-                                        + " to the media key listener");
-                            }
-                        }
-                    }
-                    if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
-                        handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
-                                needWakeLock);
-                    } else {
-                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
-                                keyEvent, needWakeLock);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setCallback(ICallback callback) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
-                    throw new SecurityException("Only Bluetooth service processes can set"
-                            + " Callback");
-                }
-                synchronized (mLock) {
-                    int userId = UserHandle.getUserId(uid);
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can set the callback"
-                                + ", userId=" + userId);
-                        return;
-                    }
-                    user.mCallback = callback;
-                    Log.d(TAG, "The callback " + user.mCallback
-                            + " is set by " + getCallingPackageName(uid));
-                    if (user.mCallback == null) {
-                        return;
-                    }
-                    try {
-                        user.mCallback.asBinder().linkToDeath(
-                                new IBinder.DeathRecipient() {
-                                    @Override
-                                    public void binderDied() {
-                                        synchronized (mLock) {
-                                            user.mCallback = null;
-                                        }
-                                    }
-                                }, 0);
-                        user.pushAddressedPlayerChangedLocked();
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to set callback", e);
-                        user.mCallback = null;
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
-                if (getContext().checkPermission(
-                        android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
-                            != PackageManager.PERMISSION_GRANTED) {
-                    throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
-                            " permission.");
-                }
-
-                synchronized (mLock) {
-                    int userId = UserHandle.getUserId(uid);
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can set the volume key long-press listener"
-                                + ", userId=" + userId);
-                        return;
-                    }
-                    if (user.mOnVolumeKeyLongPressListener != null &&
-                            user.mOnVolumeKeyLongPressListenerUid != uid) {
-                        Log.w(TAG, "The volume key long-press listener cannot be reset"
-                                + " by another app , mOnVolumeKeyLongPressListener="
-                                + user.mOnVolumeKeyLongPressListenerUid
-                                + ", uid=" + uid);
-                        return;
-                    }
-
-                    user.mOnVolumeKeyLongPressListener = listener;
-                    user.mOnVolumeKeyLongPressListenerUid = uid;
-
-                    Log.d(TAG, "The volume key long-press listener "
-                            + listener + " is set by " + getCallingPackageName(uid));
-
-                    if (user.mOnVolumeKeyLongPressListener != null) {
-                        try {
-                            user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
-                                    new IBinder.DeathRecipient() {
-                                        @Override
-                                        public void binderDied() {
-                                            synchronized (mLock) {
-                                                user.mOnVolumeKeyLongPressListener = null;
-                                            }
-                                        }
-                                    }, 0);
-                        } catch (RemoteException e) {
-                            Log.w(TAG, "Failed to set death recipient "
-                                    + user.mOnVolumeKeyLongPressListener);
-                            user.mOnVolumeKeyLongPressListener = null;
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Enforce SET_MEDIA_KEY_LISTENER permission.
-                if (getContext().checkPermission(
-                        android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
-                            != PackageManager.PERMISSION_GRANTED) {
-                    throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
-                            " permission.");
-                }
-
-                synchronized (mLock) {
-                    int userId = UserHandle.getUserId(uid);
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can set the media key listener"
-                                + ", userId=" + userId);
-                        return;
-                    }
-                    if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
-                        Log.w(TAG, "The media key listener cannot be reset by another app. "
-                                + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
-                                + ", uid=" + uid);
-                        return;
-                    }
-
-                    user.mOnMediaKeyListener = listener;
-                    user.mOnMediaKeyListenerUid = uid;
-
-                    Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
-                            + " is set by " + getCallingPackageName(uid));
-
-                    if (user.mOnMediaKeyListener != null) {
-                        try {
-                            user.mOnMediaKeyListener.asBinder().linkToDeath(
-                                    new IBinder.DeathRecipient() {
-                                        @Override
-                                        public void binderDied() {
-                                            synchronized (mLock) {
-                                                user.mOnMediaKeyListener = null;
-                                            }
-                                        }
-                                    }, 0);
-                        } catch (RemoteException e) {
-                            Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
-                            user.mOnMediaKeyListener = null;
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * Handles the dispatching of the volume button events to one of the
-         * registered listeners. If there's a volume key long-press listener and
-         * there's no active global priority session, long-pressess will be sent to the
-         * long-press listener instead of adjusting volume.
-         *
-         * @param packageName The caller's package name, obtained by Context#getPackageName()
-         * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
-         * @param asSystemService {@code true} if the event sent to the session as if it was come
-         *          from the system service instead of the app process. This helps sessions to
-         *          distinguish between the key injection by the app and key events from the
-         *          hardware devices. Should be used only when the volume key events aren't handled
-         *          by foreground activity. {@code false} otherwise to tell session about the real
-         *          caller.
-         * @param keyEvent a non-null KeyEvent whose key code is one of the
-         *            {@link KeyEvent#KEYCODE_VOLUME_UP},
-         *            {@link KeyEvent#KEYCODE_VOLUME_DOWN},
-         *            or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
-         * @param stream stream type to adjust volume.
-         * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
-         */
-        @Override
-        public void dispatchVolumeKeyEvent(String packageName, String opPackageName,
-                boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
-            if (keyEvent == null ||
-                    (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
-                             && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
-                             && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
-                Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
-                return;
-            }
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            if (DEBUG_KEY_EVENT) {
-                Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid="
-                        + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent);
-            }
-
-            try {
-                synchronized (mLock) {
-                    if (isGlobalPriorityActiveLocked()
-                            || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
-                        dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
-                                asSystemService, keyEvent, stream, musicOnly);
-                    } else {
-                        // TODO: Consider the case when both volume up and down keys are pressed
-                        //       at the same time.
-                        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
-                            if (keyEvent.getRepeatCount() == 0) {
-                                // Keeps the copy of the KeyEvent because it can be reused.
-                                mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
-                                        KeyEvent.obtain(keyEvent);
-                                mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
-                                mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
-                                mHandler.sendMessageDelayed(
-                                        mHandler.obtainMessage(
-                                                MessageHandler.MSG_VOLUME_INITIAL_DOWN,
-                                                mCurrentFullUserRecord.mFullUserId, 0),
-                                        mLongPressTimeout);
-                            }
-                            if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
-                                mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
-                                if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
-                                    dispatchVolumeKeyLongPressLocked(
-                                            mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
-                                    // Mark that the key is already handled.
-                                    mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
-                                }
-                                dispatchVolumeKeyLongPressLocked(keyEvent);
-                            }
-                        } else { // if up
-                            mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
-                            if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
-                                    && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
-                                            .getDownTime() == keyEvent.getDownTime()) {
-                                // Short-press. Should change volume.
-                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
-                                        asSystemService,
-                                        mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
-                                        mCurrentFullUserRecord.mInitialDownVolumeStream,
-                                        mCurrentFullUserRecord.mInitialDownMusicOnly);
-                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
-                                        asSystemService, keyEvent, stream, musicOnly);
-                            } else {
-                                dispatchVolumeKeyLongPressLocked(keyEvent);
-                            }
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid,
-                int uid, boolean asSystemService, KeyEvent keyEvent, int stream,
-                boolean musicOnly) {
-            boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
-            boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
-            int direction = 0;
-            boolean isMute = false;
-            switch (keyEvent.getKeyCode()) {
-                case KeyEvent.KEYCODE_VOLUME_UP:
-                    direction = AudioManager.ADJUST_RAISE;
-                    break;
-                case KeyEvent.KEYCODE_VOLUME_DOWN:
-                    direction = AudioManager.ADJUST_LOWER;
-                    break;
-                case KeyEvent.KEYCODE_VOLUME_MUTE:
-                    isMute = true;
-                    break;
-            }
-            if (down || up) {
-                int flags = AudioManager.FLAG_FROM_KEY;
-                if (musicOnly) {
-                    // This flag is used when the screen is off to only affect active media.
-                    flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
-                } else {
-                    // These flags are consistent with the home screen
-                    if (up) {
-                        flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
-                    } else {
-                        flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
-                    }
-                }
-                if (direction != 0) {
-                    // If this is action up we want to send a beep for non-music events
-                    if (up) {
-                        direction = 0;
-                    }
-                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
-                            asSystemService, stream, direction, flags);
-                } else if (isMute) {
-                    if (down && keyEvent.getRepeatCount() == 0) {
-                        dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
-                                asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void dispatchAdjustVolume(String packageName, String opPackageName,
-                int suggestedStream, int delta, int flags) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
-                            suggestedStream, delta, flags);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setRemoteVolumeController(IRemoteVolumeController rvc) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                enforceSystemUiPermission("listen for volume changes", pid, uid);
-                mRvc = rvc;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public boolean isGlobalPriorityActive() {
-            synchronized (mLock) {
-                return isGlobalPriorityActiveLocked();
-            }
-        }
-
-        @Override
-        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
-
-            pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
-            pw.println();
-
-            synchronized (mLock) {
-                pw.println(mSessionsListeners.size() + " sessions listeners.");
-                pw.println("Global priority session is " + mGlobalPrioritySession);
-                if (mGlobalPrioritySession != null) {
-                    mGlobalPrioritySession.dump(pw, "  ");
-                }
-                pw.println("User Records:");
-                int count = mUserRecords.size();
-                for (int i = 0; i < count; i++) {
-                    mUserRecords.valueAt(i).dumpLocked(pw, "");
-                }
-                mAudioPlayerStateMonitor.dump(getContext(), pw, "");
-            }
-        }
-
-        /**
-         * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
-         * permission or an enabled notification listener)
-         *
-         * @param controllerPackageName package name of the controller app
-         * @param controllerPid pid of the controller app
-         * @param controllerUid uid of the controller app
-         */
-        @Override
-        public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
-                throws RemoteException {
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Don't perform sanity check between controllerPackageName and controllerUid.
-                // When an (activity|service) runs on the another apps process by specifying
-                // android:process in the AndroidManifest.xml, then PID and UID would have the
-                // running process' information instead of the (activity|service) that has created
-                // MediaController.
-                // Note that we can use Context#getOpPackageName() instead of
-                // Context#getPackageName() for getting package name that matches with the PID/UID,
-                // but it doesn't tell which package has created the MediaController, so useless.
-                return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
-                        controllerPid, controllerUid);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        // For MediaSession
-        private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
-                final int uid) {
-            String packageName = null;
-            if (componentName != null) {
-                // If they gave us a component name verify they own the
-                // package
-                packageName = componentName.getPackageName();
-                enforcePackageName(packageName, uid);
-            }
-            // Check that they can make calls on behalf of the user and
-            // get the final user id
-            int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
-                    true /* allowAll */, true /* requireFull */, "getSessions", packageName);
-            // Check if they have the permissions or their component is
-            // enabled for the user they're calling from.
-            enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
-            return resolvedUserId;
-        }
-
-        private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
-                int pid, int uid) throws RemoteException {
-            // Allow API calls from the System UI
-            if (isCurrentVolumeController(pid, uid)) {
-                return true;
-            }
-
-            // Check if it's system server or has MEDIA_CONTENT_CONTROL.
-            // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
-            // check here.
-            if (uid == Process.SYSTEM_UID || getContext().checkPermission(
-                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
-                return true;
-            } else if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
-            }
-
-            // You may not access another user's content as an enabled listener.
-            final int userId = UserHandle.getUserId(uid);
-            if (resolvedUserId != userId) {
-                return false;
-            }
-
-            // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
-            //               String pkgName) to notification team for optimization
-            final List<ComponentName> enabledNotificationListeners =
-                    mNotificationManager.getEnabledNotificationListeners(userId);
-            if (enabledNotificationListeners != null) {
-                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
-                    if (TextUtils.equals(packageName,
-                            enabledNotificationListeners.get(i).getPackageName())) {
-                        return true;
-                    }
-                }
-            }
-            if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
-                        + "notification listener");
-            }
-            return false;
-        }
-
-        private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
-                int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
-            MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
-                    : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
-
-            boolean preferSuggestedStream = false;
-            if (isValidLocalStreamType(suggestedStream)
-                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
-                preferSuggestedStream = true;
-            }
-            if (DEBUG_KEY_EVENT) {
-                Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
-                        + flags + ", suggestedStream=" + suggestedStream
-                        + ", preferSuggestedStream=" + preferSuggestedStream);
-            }
-            if (session == null || preferSuggestedStream) {
-                if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
-                        && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
-                    if (DEBUG) {
-                        Log.d(TAG, "No active session to adjust, skipping media only volume event");
-                    }
-                    return;
-                }
-
-                // Execute mAudioService.adjustSuggestedStreamVolume() on
-                // handler thread of MediaSessionService.
-                // This will release the MediaSessionService.mLock sooner and avoid
-                // a potential deadlock between MediaSessionService.mLock and
-                // ActivityManagerService lock.
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        final String callingOpPackageName;
-                        final int callingUid;
-                        if (asSystemService) {
-                            callingOpPackageName = getContext().getOpPackageName();
-                            callingUid = Process.myUid();
-                        } else {
-                            callingOpPackageName = opPackageName;
-                            callingUid = uid;
-                        }
-                        try {
-                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
-                                    direction, flags, callingOpPackageName, callingUid);
-                        } catch (SecurityException | IllegalArgumentException e) {
-                            Log.e(TAG, "Cannot adjust volume: direction=" + direction
-                                    + ", suggestedStream=" + suggestedStream + ", flags=" + flags
-                                    + ", packageName=" + packageName + ", uid=" + uid
-                                    + ", asSystemService=" + asSystemService, e);
-                        }
-                    }
-                });
-            } else {
-                session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService,
-                        direction, flags, true);
-            }
-        }
-
-        private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
-                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
-            int action = keyEvent.getAction();
-            boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
-            if (action == KeyEvent.ACTION_DOWN) {
-                if (keyEvent.getRepeatCount() == 0) {
-                    mVoiceButtonDown = true;
-                    mVoiceButtonHandled = false;
-                } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
-                    mVoiceButtonHandled = true;
-                    startVoiceInput(needWakeLock);
-                }
-            } else if (action == KeyEvent.ACTION_UP) {
-                if (mVoiceButtonDown) {
-                    mVoiceButtonDown = false;
-                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
-                        // Resend the down then send this event through
-                        KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
-                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
-                                downEvent, needWakeLock);
-                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
-                                keyEvent, needWakeLock);
-                    }
-                }
-            }
-        }
-
-        private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
-                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
-            MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
-            if (session != null) {
-                if (DEBUG_KEY_EVENT) {
-                    Log.d(TAG, "Sending " + keyEvent + " to " + session);
-                }
-                if (needWakeLock) {
-                    mKeyEventReceiver.aquireWakeLockLocked();
-                }
-                // If we don't need a wakelock use -1 as the id so we won't release it later.
-                session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
-                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                        mKeyEventReceiver);
-                if (mCurrentFullUserRecord.mCallback != null) {
-                    try {
-                        mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
-                                keyEvent, new MediaSession.Token(session.getControllerBinder()));
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to send callback", e);
-                    }
-                }
-            } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
-                    || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
-                if (needWakeLock) {
-                    mKeyEventReceiver.aquireWakeLockLocked();
-                }
-                Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-                mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-                // TODO: Find a way to also send PID/UID in secure way.
-                String callerPackageName =
-                        (asSystemService) ? getContext().getPackageName() : packageName;
-                mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
-                try {
-                    if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
-                        PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
-                        if (DEBUG_KEY_EVENT) {
-                            Log.d(TAG, "Sending " + keyEvent
-                                    + " to the last known PendingIntent " + receiver);
-                        }
-                        receiver.send(getContext(),
-                                needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                                mediaButtonIntent, mKeyEventReceiver, mHandler);
-                        if (mCurrentFullUserRecord.mCallback != null) {
-                            ComponentName componentName = mCurrentFullUserRecord
-                                    .mLastMediaButtonReceiver.getIntent().getComponent();
-                            if (componentName != null) {
-                                mCurrentFullUserRecord.mCallback
-                                        .onMediaKeyEventDispatchedToMediaButtonReceiver(
-                                                keyEvent, componentName);
-                            }
-                        }
-                    } else {
-                        ComponentName receiver =
-                                mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
-                        int componentType = mCurrentFullUserRecord
-                                .mRestoredMediaButtonReceiverComponentType;
-                        UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
-                                .mRestoredMediaButtonReceiverUserId);
-                        if (DEBUG_KEY_EVENT) {
-                            Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
-                                    + receiver + ", type=" + componentType);
-                        }
-                        mediaButtonIntent.setComponent(receiver);
-                        try {
-                            switch (componentType) {
-                                case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
-                                    getContext().startActivityAsUser(mediaButtonIntent, userHandle);
-                                    break;
-                                case FullUserRecord.COMPONENT_TYPE_SERVICE:
-                                    getContext().startForegroundServiceAsUser(mediaButtonIntent,
-                                            userHandle);
-                                    break;
-                                default:
-                                    // Legacy behavior for other cases.
-                                    getContext().sendBroadcastAsUser(mediaButtonIntent, userHandle);
-                            }
-                        } catch (Exception e) {
-                            Log.w(TAG, "Error sending media button to the restored intent "
-                                    + receiver + ", type=" + componentType, e);
-                        }
-                        if (mCurrentFullUserRecord.mCallback != null) {
-                            mCurrentFullUserRecord.mCallback
-                                    .onMediaKeyEventDispatchedToMediaButtonReceiver(
-                                            keyEvent, receiver);
-                        }
-                    }
-                } catch (CanceledException e) {
-                    Log.i(TAG, "Error sending key event to media button receiver "
-                            + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to send callback", e);
-                }
-            }
-        }
-
-        private void startVoiceInput(boolean needWakeLock) {
-            Intent voiceIntent = null;
-            // select which type of search to launch:
-            // - screen on and device unlocked: action is ACTION_WEB_SEARCH
-            // - device locked or screen off: action is
-            // ACTION_VOICE_SEARCH_HANDS_FREE
-            // with EXTRA_SECURE set to true if the device is securely locked
-            PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
-            boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
-            if (!isLocked && pm.isScreenOn()) {
-                voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
-                Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
-            } else {
-                voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
-                voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
-                        isLocked && mKeyguardManager.isKeyguardSecure());
-                Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
-            }
-            // start the search activity
-            if (needWakeLock) {
-                mMediaEventWakeLock.acquire();
-            }
-            try {
-                if (voiceIntent != null) {
-                    voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                    if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
-                    getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
-                }
-            } catch (ActivityNotFoundException e) {
-                Log.w(TAG, "No activity for search: " + e);
-            } finally {
-                if (needWakeLock) {
-                    mMediaEventWakeLock.release();
-                }
-            }
-        }
-
-        private boolean isVoiceKey(int keyCode) {
-            return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
-                    || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
-        }
-
-        private boolean isUserSetupComplete() {
-            return Settings.Secure.getIntForUser(getContext().getContentResolver(),
-                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
-        }
-
-        // we only handle public stream types, which are 0-5
-        private boolean isValidLocalStreamType(int streamType) {
-            return streamType >= AudioManager.STREAM_VOICE_CALL
-                    && streamType <= AudioManager.STREAM_NOTIFICATION;
-        }
-
-        private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
-            private final String mPackageName;
-            private final int mPid;
-            private final int mUid;
-            private final boolean mAsSystemService;
-            private final KeyEvent mKeyEvent;
-            private final boolean mNeedWakeLock;
-            private boolean mHandled;
-
-            private MediaKeyListenerResultReceiver(String packageName, int pid, int uid,
-                    boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
-                super(mHandler);
-                mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
-                mPackageName = packageName;
-                mPid = pid;
-                mUid = uid;
-                mAsSystemService = asSystemService;
-                mKeyEvent = keyEvent;
-                mNeedWakeLock = needWakeLock;
-            }
-
-            @Override
-            public void run() {
-                Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
-                dispatchMediaKeyEvent();
-            }
-
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
-                    mHandled = true;
-                    mHandler.removeCallbacks(this);
-                    return;
-                }
-                dispatchMediaKeyEvent();
-            }
-
-            private void dispatchMediaKeyEvent() {
-                if (mHandled) {
-                    return;
-                }
-                mHandled = true;
-                mHandler.removeCallbacks(this);
-                synchronized (mLock) {
-                    if (!isGlobalPriorityActiveLocked()
-                            && isVoiceKey(mKeyEvent.getKeyCode())) {
-                        handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
-                                mKeyEvent, mNeedWakeLock);
-                    } else {
-                        dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
-                                mKeyEvent, mNeedWakeLock);
-                    }
-                }
-            }
-        }
-
-        private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
-
-        class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
-                PendingIntent.OnFinished {
-            private final Handler mHandler;
-            private int mRefCount = 0;
-            private int mLastTimeoutId = 0;
-
-            public KeyEventWakeLockReceiver(Handler handler) {
-                super(handler);
-                mHandler = handler;
-            }
-
-            public void onTimeout() {
-                synchronized (mLock) {
-                    if (mRefCount == 0) {
-                        // We've already released it, so just return
-                        return;
-                    }
-                    mLastTimeoutId++;
-                    mRefCount = 0;
-                    releaseWakeLockLocked();
-                }
-            }
-
-            public void aquireWakeLockLocked() {
-                if (mRefCount == 0) {
-                    mMediaEventWakeLock.acquire();
-                }
-                mRefCount++;
-                mHandler.removeCallbacks(this);
-                mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
-
-            }
-
-            @Override
-            public void run() {
-                onTimeout();
-            }
-
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                if (resultCode < mLastTimeoutId) {
-                    // Ignore results from calls that were before the last
-                    // timeout, just in case.
-                    return;
-                } else {
-                    synchronized (mLock) {
-                        if (mRefCount > 0) {
-                            mRefCount--;
-                            if (mRefCount == 0) {
-                                releaseWakeLockLocked();
-                            }
-                        }
-                    }
-                }
-            }
-
-            private void releaseWakeLockLocked() {
-                mMediaEventWakeLock.release();
-                mHandler.removeCallbacks(this);
-            }
-
-            @Override
-            public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
-                    String resultData, Bundle resultExtras) {
-                onReceiveResult(resultCode, null);
-            }
-        };
-
-        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent == null) {
-                    return;
-                }
-                Bundle extras = intent.getExtras();
-                if (extras == null) {
-                    return;
-                }
-                synchronized (mLock) {
-                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
-                            && mMediaEventWakeLock.isHeld()) {
-                        mMediaEventWakeLock.release();
-                    }
-                }
-            }
-        };
-    }
-
-    final class MessageHandler extends Handler {
-        private static final int MSG_SESSIONS_CHANGED = 1;
-        private static final int MSG_VOLUME_INITIAL_DOWN = 2;
-        private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SESSIONS_CHANGED:
-                    pushSessionsChanged((int) msg.obj);
-                    break;
-                case MSG_VOLUME_INITIAL_DOWN:
-                    synchronized (mLock) {
-                        FullUserRecord user = mUserRecords.get((int) msg.arg1);
-                        if (user != null && user.mInitialDownVolumeKeyEvent != null) {
-                            dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
-                            // Mark that the key is already handled.
-                            user.mInitialDownVolumeKeyEvent = null;
-                        }
-                    }
-                    break;
-            }
-        }
-
-        public void postSessionsChanged(int userId) {
-            // Use object instead of the arguments when posting message to remove pending requests.
-            Integer userIdInteger = mIntegerCache.get(userId);
-            if (userIdInteger == null) {
-                userIdInteger = Integer.valueOf(userId);
-                mIntegerCache.put(userId, userIdInteger);
-            }
-            removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
-            obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
-        }
+    abstract static class ServiceImpl {
+        public abstract void onStart();
+        public abstract void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session);
+        public abstract void onSessionPlaystateChanged(
+                MediaSessionRecord record, int oldState, int newState);
+        public abstract void onSessionPlaybackTypeChanged(MediaSessionRecord record);
+        public abstract void onStartUser(int userId);
+        public abstract void onSwitchUser(int userId);
+        public abstract void monitor();
+        public abstract void onMediaButtonReceiverChanged(MediaSessionRecord record);
+        protected abstract void enforcePhoneStatePermission(int pid, int uid);
+        abstract void updateSession(MediaSessionRecord record);
+        abstract void setGlobalPrioritySession(MediaSessionRecord record);
+        abstract List<Session2Token> getSession2TokensLocked(int userId);
+        abstract void onStopUser(int userId);
+        abstract void sessionDied(MediaSessionRecord session);
+        abstract void destroySession(MediaSessionRecord session);
+        abstract void pushSession2TokensChangedLocked(int userId);
+        abstract Context getContext();
+        abstract IBinder getServiceBinder();
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
new file mode 100644
index 0000000..f374c6d
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
@@ -0,0 +1,2142 @@
+/*
+ * Copyright 2019 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.media;
+
+import static android.os.UserHandle.USER_ALL;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.INotificationManager;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.media.AudioManager;
+import android.media.AudioManagerInternal;
+import android.media.AudioPlaybackConfiguration;
+import android.media.AudioSystem;
+import android.media.IAudioService;
+import android.media.IRemoteVolumeController;
+import android.media.MediaController2;
+import android.media.Session2CommandGroup;
+import android.media.Session2Token;
+import android.media.session.IActiveSessionsListener;
+import android.media.session.ICallback;
+import android.media.session.IOnMediaKeyListener;
+import android.media.session.IOnVolumeKeyLongPressListener;
+import android.media.session.ISession;
+import android.media.session.ISession2TokensListener;
+import android.media.session.ISessionManager;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.media.session.SessionCallbackLink;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.speech.RecognizerIntent;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * System implementation of MediaSessionManager
+ */
+public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
+    private static final String TAG = "MediaSessionService";
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    // Leave log for key event always.
+    private static final boolean DEBUG_KEY_EVENT = true;
+
+    private static final int WAKELOCK_TIMEOUT = 5000;
+    private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
+
+    private final Context mContext;
+    private final SessionManagerImpl mSessionManagerImpl;
+    private final MessageHandler mHandler = new MessageHandler();
+    private final PowerManager.WakeLock mMediaEventWakeLock;
+    private final int mLongPressTimeout;
+    private final INotificationManager mNotificationManager;
+    private final Object mLock = new Object();
+    // Keeps the full user id for each user.
+    @GuardedBy("mLock")
+    private final SparseIntArray mFullUserIds = new SparseIntArray();
+    @GuardedBy("mLock")
+    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
+    @GuardedBy("mLock")
+    private final ArrayList<SessionsListenerRecord> mSessionsListeners =
+            new ArrayList<SessionsListenerRecord>();
+    // Map user id as index to list of Session2Tokens
+    // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
+    //       one place.
+    @GuardedBy("mLock")
+    private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords =
+            new ArrayList<>();
+
+    private KeyguardManager mKeyguardManager;
+    private IAudioService mAudioService;
+    private AudioManagerInternal mAudioManagerInternal;
+    private ActivityManager mActivityManager;
+    private ContentResolver mContentResolver;
+    private SettingsObserver mSettingsObserver;
+    private boolean mHasFeatureLeanback;
+
+    // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
+    // It's always not null after the MediaSessionService is started.
+    private FullUserRecord mCurrentFullUserRecord;
+    private MediaSessionRecord mGlobalPrioritySession;
+    private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
+
+    // Used to notify system UI when remote volume was changed. TODO find a
+    // better way to handle this.
+    private IRemoteVolumeController mRvc;
+
+    public MediaSessionServiceImpl(Context context) {
+        mContext = context;
+        mSessionManagerImpl = new SessionManagerImpl();
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
+        mNotificationManager = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    IBinder getServiceBinder() {
+        return mSessionManagerImpl;
+    }
+
+    @Override
+    public void onStart() {
+        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        mAudioService = getAudioService();
+        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
+        mActivityManager =
+                (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
+        mAudioPlayerStateMonitor.registerListener(
+                (config, isRemoved) -> {
+                    if (isRemoved || !config.isActive() || config.getPlayerType()
+                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+                        return;
+                    }
+                    synchronized (mLock) {
+                        FullUserRecord user = getFullUserRecordLocked(
+                                UserHandle.getUserId(config.getClientUid()));
+                        if (user != null) {
+                            user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+                        }
+                    }
+                }, null /* handler */);
+        mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
+        mContentResolver = mContext.getContentResolver();
+        mSettingsObserver = new SettingsObserver();
+        mSettingsObserver.observe();
+        mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_LEANBACK);
+
+        updateUser();
+    }
+
+    private IAudioService getAudioService() {
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        return IAudioService.Stub.asInterface(b);
+    }
+
+    private boolean isGlobalPriorityActiveLocked() {
+        return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
+    }
+
+    @Override
+    public void updateSession(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (user == null) {
+                Log.w(TAG, "Unknown session updated. Ignoring.");
+                return;
+            }
+            if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
+                if (DEBUG_KEY_EVENT) {
+                    Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
+                }
+                user.pushAddressedPlayerChangedLocked();
+            } else {
+                if (!user.mPriorityStack.contains(record)) {
+                    Log.w(TAG, "Unknown session updated. Ignoring.");
+                    return;
+                }
+                user.mPriorityStack.onSessionStateChange(record);
+            }
+            mHandler.postSessionsChanged(record.getUserId());
+        }
+    }
+
+    @Override
+    public void setGlobalPrioritySession(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (mGlobalPrioritySession != record) {
+                Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
+                        + " to " + record);
+                mGlobalPrioritySession = record;
+                if (user != null && user.mPriorityStack.contains(record)) {
+                    // Handle the global priority session separately.
+                    // Otherwise, it can be the media button session regardless of the active state
+                    // because it or other system components might have been the lastly played media
+                    // app.
+                    user.mPriorityStack.removeSession(record);
+                }
+            }
+        }
+    }
+
+    private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
+        List<MediaSessionRecord> records = new ArrayList<>();
+        if (userId == USER_ALL) {
+            int size = mUserRecords.size();
+            for (int i = 0; i < size; i++) {
+                records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
+            }
+        } else {
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user == null) {
+                Log.w(TAG, "getSessions failed. Unknown user " + userId);
+                return records;
+            }
+            records.addAll(user.mPriorityStack.getActiveSessions(userId));
+        }
+
+        // Return global priority session at the first whenever it's asked.
+        if (isGlobalPriorityActiveLocked()
+                && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) {
+            records.add(0, mGlobalPrioritySession);
+        }
+        return records;
+    }
+
+    List<Session2Token> getSession2TokensLocked(int userId) {
+        List<Session2Token> list = new ArrayList<>();
+        if (userId == USER_ALL) {
+            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
+                list.addAll(mSession2TokensPerUser.valueAt(i));
+            }
+        } else {
+            list.addAll(mSession2TokensPerUser.get(userId));
+        }
+        return list;
+    }
+
+    /**
+     * Tells the system UI that volume has changed on an active remote session.
+     */
+    public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
+        if (mRvc == null || !session.isActive()) {
+            return;
+        }
+        try {
+            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
+        } catch (Exception e) {
+            Log.wtf(TAG, "Error sending volume change to system UI.", e);
+        }
+    }
+
+    @Override
+    public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (user == null || !user.mPriorityStack.contains(record)) {
+                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
+                return;
+            }
+            user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
+        }
+    }
+
+    @Override
+    public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (user == null || !user.mPriorityStack.contains(record)) {
+                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
+                return;
+            }
+            pushRemoteVolumeUpdateLocked(record.getUserId());
+        }
+    }
+
+    @Override
+    public void onStartUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
+        updateUser();
+    }
+
+    @Override
+    public void onSwitchUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
+        updateUser();
+    }
+
+    // Called when the user with the userId is removed.
+    @Override
+    public void onStopUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
+        synchronized (mLock) {
+            // TODO: Also handle removing user in updateUser() because adding/switching user is
+            //       handled in updateUser().
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user != null) {
+                if (user.mFullUserId == userId) {
+                    user.destroySessionsForUserLocked(USER_ALL);
+                    mUserRecords.remove(userId);
+                } else {
+                    user.destroySessionsForUserLocked(userId);
+                }
+            }
+            mSession2TokensPerUser.remove(userId);
+            updateUser();
+        }
+    }
+
+    @Override
+    public void monitor() {
+        synchronized (mLock) {
+            // Check for deadlock
+        }
+    }
+
+    protected void enforcePhoneStatePermission(int pid, int uid) {
+        if (mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
+        }
+    }
+
+    void sessionDied(MediaSessionRecord session) {
+        synchronized (mLock) {
+            destroySessionLocked(session);
+        }
+    }
+
+    void destroySession(MediaSessionRecord session) {
+        synchronized (mLock) {
+            destroySessionLocked(session);
+        }
+    }
+
+    private void updateUser() {
+        synchronized (mLock) {
+            UserManager manager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            mFullUserIds.clear();
+            List<UserInfo> allUsers = manager.getUsers();
+            if (allUsers != null) {
+                for (UserInfo userInfo : allUsers) {
+                    if (userInfo.isManagedProfile()) {
+                        mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
+                    } else {
+                        mFullUserIds.put(userInfo.id, userInfo.id);
+                        if (mUserRecords.get(userInfo.id) == null) {
+                            mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
+                        }
+                    }
+                    if (mSession2TokensPerUser.get(userInfo.id) == null) {
+                        mSession2TokensPerUser.put(userInfo.id, new ArrayList<>());
+                    }
+                }
+            }
+            // Ensure that the current full user exists.
+            int currentFullUserId = ActivityManager.getCurrentUser();
+            mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
+            if (mCurrentFullUserRecord == null) {
+                Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
+                mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
+                mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
+                if (mSession2TokensPerUser.get(currentFullUserId) == null) {
+                    mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>());
+                }
+            }
+            mFullUserIds.put(currentFullUserId, currentFullUserId);
+        }
+    }
+
+    private void updateActiveSessionListeners() {
+        synchronized (mLock) {
+            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+                SessionsListenerRecord listener = mSessionsListeners.get(i);
+                try {
+                    enforceMediaPermissions(listener.componentName, listener.pid, listener.uid,
+                            listener.userId);
+                } catch (SecurityException e) {
+                    Log.i(TAG, "ActiveSessionsListener " + listener.componentName
+                            + " is no longer authorized. Disconnecting.");
+                    mSessionsListeners.remove(i);
+                    try {
+                        listener.listener
+                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
+                    } catch (Exception e1) {
+                        // ignore
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * When a session is removed several things need to happen.
+     * 1. We need to remove it from the relevant user.
+     * 2. We need to remove it from the priority stack.
+     * 3. We need to remove it from all sessions.
+     * 4. If this is the system priority session we need to clear it.
+     * 5. We need to unlink to death from the cb binder
+     * 6. We need to tell the session to do any final cleanup (onDestroy)
+     */
+    private void destroySessionLocked(MediaSessionRecord session) {
+        if (DEBUG) {
+            Log.d(TAG, "Destroying " + session);
+        }
+        FullUserRecord user = getFullUserRecordLocked(session.getUserId());
+        if (mGlobalPrioritySession == session) {
+            mGlobalPrioritySession = null;
+            if (session.isActive() && user != null) {
+                user.pushAddressedPlayerChangedLocked();
+            }
+        } else {
+            if (user != null) {
+                user.mPriorityStack.removeSession(session);
+            }
+        }
+
+        try {
+            session.getCallback().getBinder().unlinkToDeath(session, 0);
+        } catch (Exception e) {
+            // ignore exceptions while destroying a session.
+        }
+        session.onDestroy();
+        mHandler.postSessionsChanged(session.getUserId());
+    }
+
+    private void enforcePackageName(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName)) {
+            throw new IllegalArgumentException("packageName may not be empty");
+        }
+        String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+        final int packageCount = packages.length;
+        for (int i = 0; i < packageCount; i++) {
+            if (packageName.equals(packages[i])) {
+                return;
+            }
+        }
+        throw new IllegalArgumentException("packageName is not owned by the calling process");
+    }
+
+    /**
+     * Checks a caller's authorization to register an IRemoteControlDisplay.
+     * Authorization is granted if one of the following is true:
+     * <ul>
+     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
+     * permission</li>
+     * <li>the caller's listener is one of the enabled notification listeners
+     * for the caller's user</li>
+     * </ul>
+     */
+    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
+            int resolvedUserId) {
+        if (isCurrentVolumeController(pid, uid)) return;
+        if (mContext
+                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+                != PackageManager.PERMISSION_GRANTED
+                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
+                resolvedUserId)) {
+            throw new SecurityException("Missing permission to control media.");
+        }
+    }
+
+    private boolean isCurrentVolumeController(int pid, int uid) {
+        return mContext.checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+                pid, uid) == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private void enforceSystemUiPermission(String action, int pid, int uid) {
+        if (!isCurrentVolumeController(pid, uid)) {
+            throw new SecurityException("Only system ui may " + action);
+        }
+    }
+
+    /**
+     * This checks if the component is an enabled notification listener for the
+     * specified user. Enabled components may only operate on behalf of the user
+     * they're running as.
+     *
+     * @param compName The component that is enabled.
+     * @param userId The user id of the caller.
+     * @param forUserId The user id they're making the request on behalf of.
+     * @return True if the component is enabled, false otherwise
+     */
+    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+            int forUserId) {
+        if (userId != forUserId) {
+            // You may not access another user's content as an enabled listener.
+            return false;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Checking if enabled notification listener " + compName);
+        }
+        if (compName != null) {
+            try {
+                return mNotificationManager.isNotificationListenerAccessGrantedForUser(
+                        compName, userId);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
+            }
+        }
+        return false;
+    }
+
+    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
+            String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException {
+        synchronized (mLock) {
+            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
+        }
+    }
+
+    /*
+     * When a session is created the following things need to happen.
+     * 1. Its callback binder needs a link to death
+     * 2. It needs to be added to all sessions.
+     * 3. It needs to be added to the priority stack.
+     * 4. It needs to be added to the relevant user record.
+     */
+    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
+            String callerPackageName, SessionCallbackLink cb, String tag) {
+        FullUserRecord user = getFullUserRecordLocked(userId);
+        if (user == null) {
+            Log.wtf(TAG, "Request from invalid user: " +  userId);
+            throw new RuntimeException("Session request from invalid user.");
+        }
+
+        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
+                callerPackageName, cb, tag, this, mHandler.getLooper());
+        try {
+            cb.getBinder().linkToDeath(session, 0);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Media Session owner died prematurely.", e);
+        }
+
+        user.mPriorityStack.addSession(session);
+        mHandler.postSessionsChanged(userId);
+
+        if (DEBUG) {
+            Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
+        }
+        return session;
+    }
+
+    private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
+        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+            if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) {
+        for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
+            if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+
+    private void pushSessionsChanged(int userId) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user == null) {
+                Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
+                return;
+            }
+            List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
+            int size = records.size();
+            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
+            for (int i = 0; i < size; i++) {
+                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
+            }
+            pushRemoteVolumeUpdateLocked(userId);
+            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+                SessionsListenerRecord record = mSessionsListeners.get(i);
+                if (record.userId == USER_ALL || record.userId == userId) {
+                    try {
+                        record.listener.onActiveSessionsChanged(tokens);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
+                                e);
+                        mSessionsListeners.remove(i);
+                    }
+                }
+            }
+        }
+    }
+
+    private void pushRemoteVolumeUpdateLocked(int userId) {
+        if (mRvc != null) {
+            try {
+                FullUserRecord user = getFullUserRecordLocked(userId);
+                if (user == null) {
+                    Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
+                    return;
+                }
+                MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
+                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
+            }
+        }
+    }
+
+    void pushSession2TokensChangedLocked(int userId) {
+        List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
+        List<Session2Token> session2Tokens = getSession2TokensLocked(userId);
+
+        for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
+            Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
+            try {
+                if (listenerRecord.userId == USER_ALL) {
+                    listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
+                } else if (listenerRecord.userId == userId) {
+                    listenerRecord.listener.onSession2TokensChanged(session2Tokens);
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e);
+                mSession2TokensListenerRecords.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Called when the media button receiver for the {@code record} is changed.
+     *
+     * @param record the media session whose media button receiver is updated.
+     */
+    public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            MediaSessionRecord mediaButtonSession =
+                    user.mPriorityStack.getMediaButtonSession();
+            if (record == mediaButtonSession) {
+                user.rememberMediaButtonReceiverLocked(mediaButtonSession);
+            }
+        }
+    }
+
+    private String getCallingPackageName(int uid) {
+        String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+        if (packages != null && packages.length > 0) {
+            return packages[0];
+        }
+        return "";
+    }
+
+    private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
+        if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
+            return;
+        }
+        try {
+            mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
+        }
+    }
+
+    private FullUserRecord getFullUserRecordLocked(int userId) {
+        int fullUserId = mFullUserIds.get(userId, -1);
+        if (fullUserId < 0) {
+            return null;
+        }
+        return mUserRecords.get(fullUserId);
+    }
+
+    /**
+     * Information about a full user and its corresponding managed profiles.
+     *
+     * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
+     * them when he/she presses a media/volume button. So keeping media sessions for them in one
+     * place makes more sense and increases the readability.</p>
+     * <p>The contents of this object is guarded by {@link #mLock}.
+     */
+    final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
+        public static final int COMPONENT_TYPE_INVALID = 0;
+        public static final int COMPONENT_TYPE_BROADCAST = 1;
+        public static final int COMPONENT_TYPE_ACTIVITY = 2;
+        public static final int COMPONENT_TYPE_SERVICE = 3;
+        private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+
+        private final int mFullUserId;
+        private final MediaSessionStack mPriorityStack;
+        private PendingIntent mLastMediaButtonReceiver;
+        private ComponentName mRestoredMediaButtonReceiver;
+        private int mRestoredMediaButtonReceiverComponentType;
+        private int mRestoredMediaButtonReceiverUserId;
+
+        private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
+        private int mOnVolumeKeyLongPressListenerUid;
+        private KeyEvent mInitialDownVolumeKeyEvent;
+        private int mInitialDownVolumeStream;
+        private boolean mInitialDownMusicOnly;
+
+        private IOnMediaKeyListener mOnMediaKeyListener;
+        private int mOnMediaKeyListenerUid;
+        private ICallback mCallback;
+
+        FullUserRecord(int fullUserId) {
+            mFullUserId = fullUserId;
+            mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
+            // Restore the remembered media button receiver before the boot.
+            String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
+                    Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
+            if (mediaButtonReceiverInfo == null) {
+                return;
+            }
+            String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
+            if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
+                return;
+            }
+            mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
+            mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
+            if (tokens.length == 3) {
+                mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]);
+            } else {
+                mRestoredMediaButtonReceiverComponentType =
+                        getComponentType(mRestoredMediaButtonReceiver);
+            }
+        }
+
+        public void destroySessionsForUserLocked(int userId) {
+            List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
+            for (MediaSessionRecord session : sessions) {
+                MediaSessionServiceImpl.this.destroySessionLocked(session);
+            }
+        }
+
+        public void dumpLocked(PrintWriter pw, String prefix) {
+            pw.print(prefix + "Record for full_user=" + mFullUserId);
+            // Dump managed profile user ids associated with this user.
+            int size = mFullUserIds.size();
+            for (int i = 0; i < size; i++) {
+                if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
+                        && mFullUserIds.valueAt(i) == mFullUserId) {
+                    pw.print(", profile_user=" + mFullUserIds.keyAt(i));
+                }
+            }
+            pw.println();
+            String indent = prefix + "  ";
+            pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
+            pw.println(indent + "Volume key long-press listener package: "
+                    + getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
+            pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
+            pw.println(indent + "Media key listener package: "
+                    + getCallingPackageName(mOnMediaKeyListenerUid));
+            pw.println(indent + "Callback: " + mCallback);
+            pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
+            pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
+            pw.println(indent + "Restored MediaButtonReceiverComponentType: "
+                    + mRestoredMediaButtonReceiverComponentType);
+            mPriorityStack.dump(pw, indent);
+            pw.println(indent + "Session2Tokens:");
+            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
+                List<Session2Token> list = mSession2TokensPerUser.valueAt(i);
+                if (list == null || list.size() == 0) {
+                    continue;
+                }
+                for (Session2Token token : list) {
+                    pw.println(indent + "  " + token);
+                }
+            }
+        }
+
+        @Override
+        public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
+                MediaSessionRecord newMediaButtonSession) {
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
+            }
+            synchronized (mLock) {
+                if (oldMediaButtonSession != null) {
+                    mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
+                }
+                if (newMediaButtonSession != null) {
+                    rememberMediaButtonReceiverLocked(newMediaButtonSession);
+                    mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
+                }
+                pushAddressedPlayerChangedLocked();
+            }
+        }
+
+        // Remember media button receiver and keep it in the persistent storage.
+        public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
+            PendingIntent receiver = record.getMediaButtonReceiver();
+            mLastMediaButtonReceiver = receiver;
+            mRestoredMediaButtonReceiver = null;
+            mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
+
+            String mediaButtonReceiverInfo = "";
+            if (receiver != null) {
+                ComponentName component = receiver.getIntent().getComponent();
+                if (component != null
+                        && record.getPackageName().equals(component.getPackageName())) {
+                    String componentName = component.flattenToString();
+                    int componentType = getComponentType(component);
+                    mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM,
+                            componentName, String.valueOf(record.getUserId()),
+                            String.valueOf(componentType));
+                }
+            }
+            Settings.Secure.putStringForUser(mContentResolver,
+                    Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo,
+                    mFullUserId);
+        }
+
+        private void pushAddressedPlayerChangedLocked() {
+            if (mCallback == null) {
+                return;
+            }
+            try {
+                MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
+                if (mediaButtonSession != null) {
+                    mCallback.onAddressedPlayerChangedToMediaSession(
+                            new MediaSession.Token(mediaButtonSession.getControllerBinder()));
+                } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
+                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
+                            mCurrentFullUserRecord.mLastMediaButtonReceiver
+                                    .getIntent().getComponent());
+                } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
+                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
+                            mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
+            }
+        }
+
+        private MediaSessionRecord getMediaButtonSessionLocked() {
+            return isGlobalPriorityActiveLocked()
+                    ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
+        }
+
+        private int getComponentType(@Nullable ComponentName componentName) {
+            if (componentName == null) {
+                return COMPONENT_TYPE_INVALID;
+            }
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                ActivityInfo activityInfo = pm.getActivityInfo(componentName,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.GET_ACTIVITIES);
+                if (activityInfo != null) {
+                    return COMPONENT_TYPE_ACTIVITY;
+                }
+            } catch (NameNotFoundException e) {
+            }
+            try {
+                ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.GET_SERVICES);
+                if (serviceInfo != null) {
+                    return COMPONENT_TYPE_SERVICE;
+                }
+            } catch (NameNotFoundException e) {
+            }
+            // Pick legacy behavior for BroadcastReceiver or unknown.
+            return COMPONENT_TYPE_BROADCAST;
+        }
+    }
+
+    final class SessionsListenerRecord implements IBinder.DeathRecipient {
+        public final IActiveSessionsListener listener;
+        public final ComponentName componentName;
+        public final int userId;
+        public final int pid;
+        public final int uid;
+
+        SessionsListenerRecord(IActiveSessionsListener listener,
+                ComponentName componentName,
+                int userId, int pid, int uid) {
+            this.listener = listener;
+            this.componentName = componentName;
+            this.userId = userId;
+            this.pid = pid;
+            this.uid = uid;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mSessionsListeners.remove(this);
+            }
+        }
+    }
+
+    final class Session2TokensListenerRecord implements IBinder.DeathRecipient {
+        public final ISession2TokensListener listener;
+        public final int userId;
+
+        Session2TokensListenerRecord(ISession2TokensListener listener,
+                int userId) {
+            this.listener = listener;
+            this.userId = userId;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mSession2TokensListenerRecords.remove(this);
+            }
+        }
+    }
+
+    final class SettingsObserver extends ContentObserver {
+        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
+                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
+        private SettingsObserver() {
+            super(null);
+        }
+
+        private void observe() {
+            mContentResolver.registerContentObserver(mSecureSettingsUri,
+                    false, this, USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updateActiveSessionListeners();
+        }
+    }
+
+    class SessionManagerImpl extends ISessionManager.Stub {
+        private static final String EXTRA_WAKELOCK_ACQUIRED =
+                "android.media.AudioService.WAKELOCK_ACQUIRED";
+        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
+
+        private boolean mVoiceButtonDown = false;
+        private boolean mVoiceButtonHandled = false;
+
+        @Override
+        public ISession createSession(String packageName, SessionCallbackLink cb, String tag,
+                int userId) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                enforcePackageName(packageName, uid);
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
+                if (cb == null) {
+                    throw new IllegalArgumentException("Controller callback cannot be null");
+                }
+                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
+                        .getSessionBinder();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void notifySession2Created(Session2Token sessionToken) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "Session2 is created " + sessionToken);
+                }
+                if (uid != sessionToken.getUid()) {
+                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+                            + " but actually=" + sessionToken.getUid());
+                }
+                Controller2Callback callback = new Controller2Callback(sessionToken);
+                // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
+                //       it's closed.
+                // TODO: Keep controller as well for better readability
+                //       because the GC behavior isn't straightforward.
+                MediaController2 controller = new MediaController2(mContext, sessionToken,
+                        new HandlerExecutor(mHandler), callback);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<IBinder> getSessions(ComponentName componentName, int userId) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
+                ArrayList<IBinder> binders = new ArrayList<IBinder>();
+                synchronized (mLock) {
+                    List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
+                    for (MediaSessionRecord record : records) {
+                        binders.add(record.getControllerBinder().asBinder());
+                    }
+                }
+                return binders;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<Session2Token> getSession2Tokens(int userId) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                // Check that they can make calls on behalf of the user and
+                // get the final user id
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        true /* allowAll */, true /* requireFull */, "getSession2Tokens",
+                        null /* optional packageName */);
+                List<Session2Token> result;
+                synchronized (mLock) {
+                    result = getSession2TokensLocked(resolvedUserId);
+                }
+                return result;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void addSessionsListener(IActiveSessionsListener listener,
+                ComponentName componentName, int userId) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
+                synchronized (mLock) {
+                    int index = findIndexOfSessionsListenerLocked(listener);
+                    if (index != -1) {
+                        Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
+                        return;
+                    }
+                    SessionsListenerRecord record = new SessionsListenerRecord(listener,
+                            componentName, resolvedUserId, pid, uid);
+                    try {
+                        listener.asBinder().linkToDeath(record, 0);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
+                        return;
+                    }
+                    mSessionsListeners.add(record);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void removeSessionsListener(IActiveSessionsListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                int index = findIndexOfSessionsListenerLocked(listener);
+                if (index != -1) {
+                    SessionsListenerRecord record = mSessionsListeners.remove(index);
+                    try {
+                        record.listener.asBinder().unlinkToDeath(record, 0);
+                    } catch (Exception e) {
+                        // ignore exceptions, the record is being removed
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void addSession2TokensListener(ISession2TokensListener listener,
+                int userId) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                // Check that they can make calls on behalf of the user and get the final user id.
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        true /* allowAll */, true /* requireFull */, "addSession2TokensListener",
+                        null /* optional packageName */);
+                synchronized (mLock) {
+                    int index = findIndexOfSession2TokensListenerLocked(listener);
+                    if (index >= 0) {
+                        Log.w(TAG, "addSession2TokensListener is already added, ignoring");
+                        return;
+                    }
+                    mSession2TokensListenerRecords.add(
+                            new Session2TokensListenerRecord(listener, resolvedUserId));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void removeSession2TokensListener(ISession2TokensListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                synchronized (mLock) {
+                    int index = findIndexOfSession2TokensListenerLocked(listener);
+                    if (index >= 0) {
+                        Session2TokensListenerRecord listenerRecord =
+                                mSession2TokensListenerRecords.remove(index);
+                        try {
+                            listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0);
+                        } catch (Exception e) {
+                            // Ignore exception.
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Handles the dispatching of the media button events to one of the
+         * registered listeners, or if there was none, broadcast an
+         * ACTION_MEDIA_BUTTON intent to the rest of the system.
+         *
+         * @param packageName The caller package
+         * @param asSystemService {@code true} if the event sent to the session as if it was come
+         *          from the system service instead of the app process. This helps sessions to
+         *          distinguish between the key injection by the app and key events from the
+         *          hardware devices. Should be used only when the volume key events aren't handled
+         *          by foreground activity. {@code false} otherwise to tell session about the real
+         *          caller.
+         * @param keyEvent a non-null KeyEvent whose key code is one of the
+         *            supported media buttons
+         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
+         *            while this key event is dispatched.
+         */
+        @Override
+        public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
+                KeyEvent keyEvent, boolean needWakeLock) {
+            if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
+                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
+                return;
+            }
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid
+                            + ", uid=" + uid + ", asSystem=" + asSystemService + ", event="
+                            + keyEvent);
+                }
+                if (!isUserSetupComplete()) {
+                    // Global media key handling can have the side-effect of starting new
+                    // activities which is undesirable while setup is in progress.
+                    Slog.i(TAG, "Not dispatching media key event because user "
+                            + "setup is in progress.");
+                    return;
+                }
+
+                synchronized (mLock) {
+                    boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
+                    if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
+                        // Prevent dispatching key event through reflection while the global
+                        // priority session is active.
+                        Slog.i(TAG, "Only the system can dispatch media key event "
+                                + "to the global priority session.");
+                        return;
+                    }
+                    if (!isGlobalPriorityActive) {
+                        if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
+                            if (DEBUG_KEY_EVENT) {
+                                Log.d(TAG, "Send " + keyEvent + " to the media key listener");
+                            }
+                            try {
+                                mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
+                                        new MediaKeyListenerResultReceiver(packageName, pid, uid,
+                                                asSystemService, keyEvent, needWakeLock));
+                                return;
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "Failed to send " + keyEvent
+                                        + " to the media key listener");
+                            }
+                        }
+                    }
+                    if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
+                        handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
+                                needWakeLock);
+                    } else {
+                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+                                keyEvent, needWakeLock);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setCallback(ICallback callback) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
+                    throw new SecurityException("Only Bluetooth service processes can set"
+                            + " Callback");
+                }
+                synchronized (mLock) {
+                    int userId = UserHandle.getUserId(uid);
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can set the callback"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    user.mCallback = callback;
+                    Log.d(TAG, "The callback " + user.mCallback
+                            + " is set by " + getCallingPackageName(uid));
+                    if (user.mCallback == null) {
+                        return;
+                    }
+                    try {
+                        user.mCallback.asBinder().linkToDeath(
+                                new IBinder.DeathRecipient() {
+                                    @Override
+                                    public void binderDied() {
+                                        synchronized (mLock) {
+                                            user.mCallback = null;
+                                        }
+                                    }
+                                }, 0);
+                        user.pushAddressedPlayerChangedLocked();
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to set callback", e);
+                        user.mCallback = null;
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
+                if (mContext.checkPermission(
+                        android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER"
+                            + " permission.");
+                }
+
+                synchronized (mLock) {
+                    int userId = UserHandle.getUserId(uid);
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can set the volume key long-press listener"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    if (user.mOnVolumeKeyLongPressListener != null
+                            && user.mOnVolumeKeyLongPressListenerUid != uid) {
+                        Log.w(TAG, "The volume key long-press listener cannot be reset"
+                                + " by another app , mOnVolumeKeyLongPressListener="
+                                + user.mOnVolumeKeyLongPressListenerUid
+                                + ", uid=" + uid);
+                        return;
+                    }
+
+                    user.mOnVolumeKeyLongPressListener = listener;
+                    user.mOnVolumeKeyLongPressListenerUid = uid;
+
+                    Log.d(TAG, "The volume key long-press listener "
+                            + listener + " is set by " + getCallingPackageName(uid));
+
+                    if (user.mOnVolumeKeyLongPressListener != null) {
+                        try {
+                            user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
+                                    new IBinder.DeathRecipient() {
+                                        @Override
+                                        public void binderDied() {
+                                            synchronized (mLock) {
+                                                user.mOnVolumeKeyLongPressListener = null;
+                                            }
+                                        }
+                                    }, 0);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to set death recipient "
+                                    + user.mOnVolumeKeyLongPressListener);
+                            user.mOnVolumeKeyLongPressListener = null;
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Enforce SET_MEDIA_KEY_LISTENER permission.
+                if (mContext.checkPermission(
+                        android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER permission.");
+                }
+
+                synchronized (mLock) {
+                    int userId = UserHandle.getUserId(uid);
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can set the media key listener"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
+                        Log.w(TAG, "The media key listener cannot be reset by another app. "
+                                + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
+                                + ", uid=" + uid);
+                        return;
+                    }
+
+                    user.mOnMediaKeyListener = listener;
+                    user.mOnMediaKeyListenerUid = uid;
+
+                    Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
+                            + " is set by " + getCallingPackageName(uid));
+
+                    if (user.mOnMediaKeyListener != null) {
+                        try {
+                            user.mOnMediaKeyListener.asBinder().linkToDeath(
+                                    new IBinder.DeathRecipient() {
+                                        @Override
+                                        public void binderDied() {
+                                            synchronized (mLock) {
+                                                user.mOnMediaKeyListener = null;
+                                            }
+                                        }
+                                    }, 0);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
+                            user.mOnMediaKeyListener = null;
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Handles the dispatching of the volume button events to one of the
+         * registered listeners. If there's a volume key long-press listener and
+         * there's no active global priority session, long-pressess will be sent to the
+         * long-press listener instead of adjusting volume.
+         *
+         * @param packageName The caller's package name, obtained by Context#getPackageName()
+         * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
+         * @param asSystemService {@code true} if the event sent to the session as if it was come
+         *          from the system service instead of the app process. This helps sessions to
+         *          distinguish between the key injection by the app and key events from the
+         *          hardware devices. Should be used only when the volume key events aren't handled
+         *          by foreground activity. {@code false} otherwise to tell session about the real
+         *          caller.
+         * @param keyEvent a non-null KeyEvent whose key code is one of the
+         *            {@link KeyEvent#KEYCODE_VOLUME_UP},
+         *            {@link KeyEvent#KEYCODE_VOLUME_DOWN},
+         *            or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
+         * @param stream stream type to adjust volume.
+         * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
+         */
+        @Override
+        public void dispatchVolumeKeyEvent(String packageName, String opPackageName,
+                boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
+            if (keyEvent == null
+                    || (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
+                            && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
+                            && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
+                Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
+                return;
+            }
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid="
+                        + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent);
+            }
+
+            try {
+                synchronized (mLock) {
+                    if (isGlobalPriorityActiveLocked()
+                            || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
+                        dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                asSystemService, keyEvent, stream, musicOnly);
+                    } else {
+                        // TODO: Consider the case when both volume up and down keys are pressed
+                        //       at the same time.
+                        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                            if (keyEvent.getRepeatCount() == 0) {
+                                // Keeps the copy of the KeyEvent because it can be reused.
+                                mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
+                                        KeyEvent.obtain(keyEvent);
+                                mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
+                                mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
+                                mHandler.sendMessageDelayed(
+                                        mHandler.obtainMessage(
+                                                MessageHandler.MSG_VOLUME_INITIAL_DOWN,
+                                                mCurrentFullUserRecord.mFullUserId, 0),
+                                        mLongPressTimeout);
+                            }
+                            if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
+                                mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
+                                if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
+                                    dispatchVolumeKeyLongPressLocked(
+                                            mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
+                                    // Mark that the key is already handled.
+                                    mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
+                                }
+                                dispatchVolumeKeyLongPressLocked(keyEvent);
+                            }
+                        } else { // if up
+                            mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
+                            if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
+                                    && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
+                                    .getDownTime() == keyEvent.getDownTime()) {
+                                // Short-press. Should change volume.
+                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                        asSystemService,
+                                        mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
+                                        mCurrentFullUserRecord.mInitialDownVolumeStream,
+                                        mCurrentFullUserRecord.mInitialDownMusicOnly);
+                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                        asSystemService, keyEvent, stream, musicOnly);
+                            } else {
+                                dispatchVolumeKeyLongPressLocked(keyEvent);
+                            }
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid,
+                int uid, boolean asSystemService, KeyEvent keyEvent, int stream,
+                boolean musicOnly) {
+            boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
+            boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
+            int direction = 0;
+            boolean isMute = false;
+            switch (keyEvent.getKeyCode()) {
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                    direction = AudioManager.ADJUST_RAISE;
+                    break;
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                    direction = AudioManager.ADJUST_LOWER;
+                    break;
+                case KeyEvent.KEYCODE_VOLUME_MUTE:
+                    isMute = true;
+                    break;
+            }
+            if (down || up) {
+                int flags = AudioManager.FLAG_FROM_KEY;
+                if (musicOnly) {
+                    // This flag is used when the screen is off to only affect active media.
+                    flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
+                } else {
+                    // These flags are consistent with the home screen
+                    if (up) {
+                        flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
+                    } else {
+                        flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
+                    }
+                }
+                if (direction != 0) {
+                    // If this is action up we want to send a beep for non-music events
+                    if (up) {
+                        direction = 0;
+                    }
+                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+                            asSystemService, stream, direction, flags);
+                } else if (isMute) {
+                    if (down && keyEvent.getRepeatCount() == 0) {
+                        dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+                                asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void dispatchAdjustVolume(String packageName, String opPackageName,
+                int suggestedStream, int delta, int flags) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
+                            suggestedStream, delta, flags);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setRemoteVolumeController(IRemoteVolumeController rvc) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                enforceSystemUiPermission("listen for volume changes", pid, uid);
+                mRvc = rvc;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public boolean isGlobalPriorityActive() {
+            synchronized (mLock) {
+                return isGlobalPriorityActiveLocked();
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+            pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
+            pw.println();
+
+            synchronized (mLock) {
+                pw.println(mSessionsListeners.size() + " sessions listeners.");
+                pw.println("Global priority session is " + mGlobalPrioritySession);
+                if (mGlobalPrioritySession != null) {
+                    mGlobalPrioritySession.dump(pw, "  ");
+                }
+                pw.println("User Records:");
+                int count = mUserRecords.size();
+                for (int i = 0; i < count; i++) {
+                    mUserRecords.valueAt(i).dumpLocked(pw, "");
+                }
+                mAudioPlayerStateMonitor.dump(mContext, pw, "");
+            }
+        }
+
+        /**
+         * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
+         * permission or an enabled notification listener)
+         *
+         * @param controllerPackageName package name of the controller app
+         * @param controllerPid pid of the controller app
+         * @param controllerUid uid of the controller app
+         */
+        @Override
+        public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
+                throws RemoteException {
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Don't perform sanity check between controllerPackageName and controllerUid.
+                // When an (activity|service) runs on the another apps process by specifying
+                // android:process in the AndroidManifest.xml, then PID and UID would have the
+                // running process' information instead of the (activity|service) that has created
+                // MediaController.
+                // Note that we can use Context#getOpPackageName() instead of
+                // Context#getPackageName() for getting package name that matches with the PID/UID,
+                // but it doesn't tell which package has created the MediaController, so useless.
+                return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
+                        controllerPid, controllerUid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        // For MediaSession
+        private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
+                final int uid) {
+            String packageName = null;
+            if (componentName != null) {
+                // If they gave us a component name verify they own the
+                // package
+                packageName = componentName.getPackageName();
+                enforcePackageName(packageName, uid);
+            }
+            // Check that they can make calls on behalf of the user and
+            // get the final user id
+            int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                    true /* allowAll */, true /* requireFull */, "getSessions", packageName);
+            // Check if they have the permissions or their component is
+            // enabled for the user they're calling from.
+            enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
+            return resolvedUserId;
+        }
+
+        private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
+                int pid, int uid) throws RemoteException {
+            // Allow API calls from the System UI
+            if (isCurrentVolumeController(pid, uid)) {
+                return true;
+            }
+
+            // Check if it's system server or has MEDIA_CONTENT_CONTROL.
+            // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
+            // check here.
+            if (uid == Process.SYSTEM_UID || mContext.checkPermission(
+                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            } else if (DEBUG) {
+                Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
+            }
+
+            // You may not access another user's content as an enabled listener.
+            final int userId = UserHandle.getUserId(uid);
+            if (resolvedUserId != userId) {
+                return false;
+            }
+
+            // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
+            //               String pkgName) to notification team for optimization
+            final List<ComponentName> enabledNotificationListeners =
+                    mNotificationManager.getEnabledNotificationListeners(userId);
+            if (enabledNotificationListeners != null) {
+                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
+                    if (TextUtils.equals(packageName,
+                            enabledNotificationListeners.get(i).getPackageName())) {
+                        return true;
+                    }
+                }
+            }
+            if (DEBUG) {
+                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
+                        + "notification listener");
+            }
+            return false;
+        }
+
+        private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
+                int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
+            MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
+                    : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
+
+            boolean preferSuggestedStream = false;
+            if (isValidLocalStreamType(suggestedStream)
+                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
+                preferSuggestedStream = true;
+            }
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
+                        + flags + ", suggestedStream=" + suggestedStream
+                        + ", preferSuggestedStream=" + preferSuggestedStream);
+            }
+            if (session == null || preferSuggestedStream) {
+                if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
+                        && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "No active session to adjust, skipping media only volume event");
+                    }
+                    return;
+                }
+
+                // Execute mAudioService.adjustSuggestedStreamVolume() on
+                // handler thread of MediaSessionService.
+                // This will release the MediaSessionService.mLock sooner and avoid
+                // a potential deadlock between MediaSessionService.mLock and
+                // ActivityManagerService lock.
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final String callingOpPackageName;
+                        final int callingUid;
+                        if (asSystemService) {
+                            callingOpPackageName = mContext.getOpPackageName();
+                            callingUid = Process.myUid();
+                        } else {
+                            callingOpPackageName = opPackageName;
+                            callingUid = uid;
+                        }
+                        try {
+                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
+                                    direction, flags, callingOpPackageName, callingUid);
+                        } catch (SecurityException | IllegalArgumentException e) {
+                            Log.e(TAG, "Cannot adjust volume: direction=" + direction
+                                    + ", suggestedStream=" + suggestedStream + ", flags=" + flags
+                                    + ", packageName=" + packageName + ", uid=" + uid
+                                    + ", asSystemService=" + asSystemService, e);
+                        }
+                    }
+                });
+            } else {
+                session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService,
+                        direction, flags, true);
+            }
+        }
+
+        private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
+                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+            int action = keyEvent.getAction();
+            boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
+            if (action == KeyEvent.ACTION_DOWN) {
+                if (keyEvent.getRepeatCount() == 0) {
+                    mVoiceButtonDown = true;
+                    mVoiceButtonHandled = false;
+                } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
+                    mVoiceButtonHandled = true;
+                    startVoiceInput(needWakeLock);
+                }
+            } else if (action == KeyEvent.ACTION_UP) {
+                if (mVoiceButtonDown) {
+                    mVoiceButtonDown = false;
+                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
+                        // Resend the down then send this event through
+                        KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
+                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+                                downEvent, needWakeLock);
+                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+                                keyEvent, needWakeLock);
+                    }
+                }
+            }
+        }
+
+        private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
+                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+            MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
+            if (session != null) {
+                if (DEBUG_KEY_EVENT) {
+                    Log.d(TAG, "Sending " + keyEvent + " to " + session);
+                }
+                if (needWakeLock) {
+                    mKeyEventReceiver.aquireWakeLockLocked();
+                }
+                // If we don't need a wakelock use -1 as the id so we won't release it later.
+                session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
+                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
+                        mKeyEventReceiver);
+                if (mCurrentFullUserRecord.mCallback != null) {
+                    try {
+                        mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
+                                keyEvent, new MediaSession.Token(session.getControllerBinder()));
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to send callback", e);
+                    }
+                }
+            } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
+                    || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
+                if (needWakeLock) {
+                    mKeyEventReceiver.aquireWakeLockLocked();
+                }
+                Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+                // TODO: Find a way to also send PID/UID in secure way.
+                String callerPackageName =
+                        (asSystemService) ? mContext.getPackageName() : packageName;
+                mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
+                try {
+                    if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
+                        PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
+                        if (DEBUG_KEY_EVENT) {
+                            Log.d(TAG, "Sending " + keyEvent
+                                    + " to the last known PendingIntent " + receiver);
+                        }
+                        receiver.send(mContext,
+                                needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
+                                mediaButtonIntent, mKeyEventReceiver, mHandler);
+                        if (mCurrentFullUserRecord.mCallback != null) {
+                            ComponentName componentName = mCurrentFullUserRecord
+                                    .mLastMediaButtonReceiver.getIntent().getComponent();
+                            if (componentName != null) {
+                                mCurrentFullUserRecord.mCallback
+                                        .onMediaKeyEventDispatchedToMediaButtonReceiver(
+                                                keyEvent, componentName);
+                            }
+                        }
+                    } else {
+                        ComponentName receiver =
+                                mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
+                        int componentType = mCurrentFullUserRecord
+                                .mRestoredMediaButtonReceiverComponentType;
+                        UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
+                                .mRestoredMediaButtonReceiverUserId);
+                        if (DEBUG_KEY_EVENT) {
+                            Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
+                                    + receiver + ", type=" + componentType);
+                        }
+                        mediaButtonIntent.setComponent(receiver);
+                        try {
+                            switch (componentType) {
+                                case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
+                                    mContext.startActivityAsUser(mediaButtonIntent, userHandle);
+                                    break;
+                                case FullUserRecord.COMPONENT_TYPE_SERVICE:
+                                    mContext.startForegroundServiceAsUser(mediaButtonIntent,
+                                            userHandle);
+                                    break;
+                                default:
+                                    // Legacy behavior for other cases.
+                                    mContext.sendBroadcastAsUser(mediaButtonIntent, userHandle);
+                            }
+                        } catch (Exception e) {
+                            Log.w(TAG, "Error sending media button to the restored intent "
+                                    + receiver + ", type=" + componentType, e);
+                        }
+                        if (mCurrentFullUserRecord.mCallback != null) {
+                            mCurrentFullUserRecord.mCallback
+                                    .onMediaKeyEventDispatchedToMediaButtonReceiver(
+                                            keyEvent, receiver);
+                        }
+                    }
+                } catch (CanceledException e) {
+                    Log.i(TAG, "Error sending key event to media button receiver "
+                            + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to send callback", e);
+                }
+            }
+        }
+
+        private void startVoiceInput(boolean needWakeLock) {
+            Intent voiceIntent = null;
+            // select which type of search to launch:
+            // - screen on and device unlocked: action is ACTION_WEB_SEARCH
+            // - device locked or screen off: action is
+            // ACTION_VOICE_SEARCH_HANDS_FREE
+            // with EXTRA_SECURE set to true if the device is securely locked
+            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+            if (!isLocked && pm.isScreenOn()) {
+                voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+                Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
+            } else {
+                voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+                voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
+                        isLocked && mKeyguardManager.isKeyguardSecure());
+                Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
+            }
+            // start the search activity
+            if (needWakeLock) {
+                mMediaEventWakeLock.acquire();
+            }
+            try {
+                if (voiceIntent != null) {
+                    voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
+                    mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT);
+                }
+            } catch (ActivityNotFoundException e) {
+                Log.w(TAG, "No activity for search: " + e);
+            } finally {
+                if (needWakeLock) {
+                    mMediaEventWakeLock.release();
+                }
+            }
+        }
+
+        private boolean isVoiceKey(int keyCode) {
+            return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
+                    || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        }
+
+        private boolean isUserSetupComplete() {
+            return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+        }
+
+        // we only handle public stream types, which are 0-5
+        private boolean isValidLocalStreamType(int streamType) {
+            return streamType >= AudioManager.STREAM_VOICE_CALL
+                    && streamType <= AudioManager.STREAM_NOTIFICATION;
+        }
+
+        private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
+            private final String mPackageName;
+            private final int mPid;
+            private final int mUid;
+            private final boolean mAsSystemService;
+            private final KeyEvent mKeyEvent;
+            private final boolean mNeedWakeLock;
+            private boolean mHandled;
+
+            private MediaKeyListenerResultReceiver(String packageName, int pid, int uid,
+                    boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+                super(mHandler);
+                mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
+                mPackageName = packageName;
+                mPid = pid;
+                mUid = uid;
+                mAsSystemService = asSystemService;
+                mKeyEvent = keyEvent;
+                mNeedWakeLock = needWakeLock;
+            }
+
+            @Override
+            public void run() {
+                Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
+                dispatchMediaKeyEvent();
+            }
+
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
+                    mHandled = true;
+                    mHandler.removeCallbacks(this);
+                    return;
+                }
+                dispatchMediaKeyEvent();
+            }
+
+            private void dispatchMediaKeyEvent() {
+                if (mHandled) {
+                    return;
+                }
+                mHandled = true;
+                mHandler.removeCallbacks(this);
+                synchronized (mLock) {
+                    if (!isGlobalPriorityActiveLocked()
+                            && isVoiceKey(mKeyEvent.getKeyCode())) {
+                        handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
+                                mKeyEvent, mNeedWakeLock);
+                    } else {
+                        dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
+                                mKeyEvent, mNeedWakeLock);
+                    }
+                }
+            }
+        }
+
+        private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
+
+        class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
+                PendingIntent.OnFinished {
+            private final Handler mHandler;
+            private int mRefCount = 0;
+            private int mLastTimeoutId = 0;
+
+            KeyEventWakeLockReceiver(Handler handler) {
+                super(handler);
+                mHandler = handler;
+            }
+
+            public void onTimeout() {
+                synchronized (mLock) {
+                    if (mRefCount == 0) {
+                        // We've already released it, so just return
+                        return;
+                    }
+                    mLastTimeoutId++;
+                    mRefCount = 0;
+                    releaseWakeLockLocked();
+                }
+            }
+
+            public void aquireWakeLockLocked() {
+                if (mRefCount == 0) {
+                    mMediaEventWakeLock.acquire();
+                }
+                mRefCount++;
+                mHandler.removeCallbacks(this);
+                mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
+
+            }
+
+            @Override
+            public void run() {
+                onTimeout();
+            }
+
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode < mLastTimeoutId) {
+                    // Ignore results from calls that were before the last
+                    // timeout, just in case.
+                    return;
+                } else {
+                    synchronized (mLock) {
+                        if (mRefCount > 0) {
+                            mRefCount--;
+                            if (mRefCount == 0) {
+                                releaseWakeLockLocked();
+                            }
+                        }
+                    }
+                }
+            }
+
+            private void releaseWakeLockLocked() {
+                mMediaEventWakeLock.release();
+                mHandler.removeCallbacks(this);
+            }
+
+            @Override
+            public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
+                    String resultData, Bundle resultExtras) {
+                onReceiveResult(resultCode, null);
+            }
+        };
+
+        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent == null) {
+                    return;
+                }
+                Bundle extras = intent.getExtras();
+                if (extras == null) {
+                    return;
+                }
+                synchronized (mLock) {
+                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
+                            && mMediaEventWakeLock.isHeld()) {
+                        mMediaEventWakeLock.release();
+                    }
+                }
+            }
+        };
+    }
+
+    final class MessageHandler extends Handler {
+        private static final int MSG_SESSIONS_CHANGED = 1;
+        private static final int MSG_VOLUME_INITIAL_DOWN = 2;
+        private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SESSIONS_CHANGED:
+                    pushSessionsChanged((int) msg.obj);
+                    break;
+                case MSG_VOLUME_INITIAL_DOWN:
+                    synchronized (mLock) {
+                        FullUserRecord user = mUserRecords.get((int) msg.arg1);
+                        if (user != null && user.mInitialDownVolumeKeyEvent != null) {
+                            dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
+                            // Mark that the key is already handled.
+                            user.mInitialDownVolumeKeyEvent = null;
+                        }
+                    }
+                    break;
+            }
+        }
+
+        public void postSessionsChanged(int userId) {
+            // Use object instead of the arguments when posting message to remove pending requests.
+            Integer userIdInteger = mIntegerCache.get(userId);
+            if (userIdInteger == null) {
+                userIdInteger = Integer.valueOf(userId);
+                mIntegerCache.put(userId, userIdInteger);
+            }
+            removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
+            obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
+        }
+    }
+
+    private class Controller2Callback extends MediaController2.ControllerCallback {
+        private final Session2Token mToken;
+
+        Controller2Callback(Session2Token token) {
+            mToken = token;
+        }
+
+        @Override
+        public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
+            synchronized (mLock) {
+                int userId = UserHandle.getUserId(mToken.getUid());
+                mSession2TokensPerUser.get(userId).add(mToken);
+                pushSession2TokensChangedLocked(userId);
+            }
+        }
+
+        @Override
+        public void onDisconnected(MediaController2 controller) {
+            synchronized (mLock) {
+                int userId = UserHandle.getUserId(mToken.getUid());
+                mSession2TokensPerUser.get(userId).remove(mToken);
+                pushSession2TokensChangedLocked(userId);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java b/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java
index 6a5f563..64c451d 100644
--- a/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java
+++ b/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java
@@ -159,12 +159,13 @@
                     + serviceInfo.packageName + "/" + serviceInfo.name);
             return false;
         }
-        if (!hasCaptureVideoPermission(serviceInfo.packageName)) {
-            // If the service does not have permission to capture video then it
-            // isn't going to be terribly useful as a remote display, is it?
-            // Kind of makes you wonder what it's doing there in the first place.
+        if (mPackageManager.checkPermission(Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+                        serviceInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
+            // If the service does not have this permission then the system will not bind to it.
+            // This is to prevent non privileged apps declaring themselves as remote display
+            // providers just to be bound to by the system and keep their process alive.
             Slog.w(TAG, "Ignoring remote display provider service because it does not "
-                    + "have the CAPTURE_VIDEO_OUTPUT or CAPTURE_SECURE_VIDEO_OUTPUT "
+                    + "have the REMOTE_DISPLAY_PROVIDER "
                     + "permission: " + serviceInfo.packageName + "/" + serviceInfo.name);
             return false;
         }
@@ -172,18 +173,6 @@
         return true;
     }
 
-    private boolean hasCaptureVideoPermission(String packageName) {
-        if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT,
-                packageName) == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-        if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT,
-                packageName) == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-        return false;
-    }
-
     private int findProvider(String packageName, String className) {
         int count = mProviders.size();
         for (int i = 0; i < count; i++) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0d6dadf..af55605 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -270,14 +270,12 @@
  * enforcement.
  *
  * <p>
- * This class uses 2-3 locks to synchronize state:
+ * This class uses 2 locks to synchronize state:
  * <ul>
  * <li>{@code mUidRulesFirstLock}: used to guard state related to individual UIDs (such as firewall
  * rules).
  * <li>{@code mNetworkPoliciesSecondLock}: used to guard state related to network interfaces (such
  * as network policies).
- * <li>{@code allLocks}: not a "real" lock, but an indication (through @GuardedBy) that all locks
- * must be held.
  * </ul>
  *
  * <p>
@@ -419,7 +417,8 @@
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
 
-    @GuardedBy("allLocks") volatile boolean mSystemReady;
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
+    volatile boolean mSystemReady;
 
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
@@ -558,7 +557,7 @@
 
     private final ServiceThread mUidEventThread;
 
-    @GuardedBy("allLocks")
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     private final AtomicFile mPolicyFile;
 
     private final AppOpsManager mAppOps;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index dc3bfbc..8c274e4 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -368,7 +368,7 @@
         mStatsObservers = checkNotNull(statsObservers, "missing NetworkStatsObservers");
         mSystemDir = checkNotNull(systemDir, "missing systemDir");
         mBaseDir = checkNotNull(baseDir, "missing baseDir");
-        mUseBpfTrafficStats = new File("/sys/fs/bpf/traffic_uid_stats_map").exists();
+        mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists();
     }
 
     private void registerLocalService() {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 88d73fb..c222e69 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -767,6 +767,23 @@
         return installed;
     }
 
+    protected Set<String> getAllowedPackages() {
+        final Set<String> allowedPackages = new ArraySet<>();
+        for (int k = 0; k < mApproved.size(); k++) {
+            ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
+            for (int i = 0; i < allowedByType.size(); i++) {
+                final ArraySet<String> allowed = allowedByType.valueAt(i);
+                for (int j = 0; j < allowed.size(); j++) {
+                    String pkgName = getPackageName(allowed.valueAt(j));
+                    if (!TextUtils.isEmpty(pkgName)) {
+                        allowedPackages.add(pkgName);
+                    }
+                }
+            }
+        }
+        return allowedPackages;
+    }
+
     private void trimApprovedListsAccordingToInstalledServices() {
         int N = mApproved.size();
         for (int i = 0 ; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c2c8825..1798617 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -77,6 +77,7 @@
 
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
+import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
@@ -765,8 +766,7 @@
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
                         .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
-                                (Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
-                                        == action.getSemanticAction()) ? 1 : 0)
+                                action.isContextual() ? 1 : 0)
                         .addTaggedData(
                                 MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
                                 generatedByAssistant ? 1 : 0));
@@ -970,7 +970,7 @@
                             r.getNumSmartActionsAdded())
                     .addTaggedData(
                             MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
-                            r.getSuggestionsGeneratedByAssistant());
+                            r.getSuggestionsGeneratedByAssistant() ? 1 : 0);
             mMetricsLogger.write(logMaker);
         }
     }
@@ -1581,6 +1581,9 @@
 
         mIsAutomotive =
                 mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
+
+        mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray(
+                com.android.internal.R.array.config_nonBlockableNotificationPackages));
     }
 
     @Override
@@ -1702,8 +1705,16 @@
     }
 
     private void sendRegisteredOnlyBroadcast(String action) {
-        getContext().sendBroadcastAsUser(new Intent(action)
-                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
+        Intent intent = new Intent(action);
+        getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                UserHandle.ALL, null);
+        // explicitly send the broadcast to all DND packages, even if they aren't currently running
+        intent.setFlags(0);
+        final Set<String> dndApprovedPackages = mConditionProviders.getAllowedPackages();
+        for (String pkg : dndApprovedPackages) {
+            intent.setPackage(pkg);
+            getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 
     @Override
@@ -2300,22 +2311,22 @@
         }
 
         @Override
-        public boolean areAppOverlaysAllowed(String pkg) {
-            return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid());
+        public boolean areBubblesAllowed(String pkg) {
+            return areBubblesAllowedForPackage(pkg, Binder.getCallingUid());
         }
 
         @Override
-        public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) {
-            checkCallerIsSystemOrSameApp(pkg);
-
-            return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid);
+        public boolean areBubblesAllowedForPackage(String pkg, int uid) {
+            enforceSystemOrSystemUIOrSamePackage(pkg,
+                    "Caller not system or systemui or same package");
+            return mPreferencesHelper.areBubblessAllowed(pkg, uid);
         }
 
         @Override
-        public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+        public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
             checkCallerIsSystem();
 
-            mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed);
+            mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed);
             handleSavePolicyFile();
         }
 
@@ -2642,6 +2653,9 @@
             // Zen
             mConditionProviders.onPackagesChanged(true, packages, uids);
 
+            // Snoozing
+            mSnoozeHelper.clearData(UserHandle.getUserId(uid), packageName);
+
             // Reset notification preferences
             if (!fromApp) {
                 mPreferencesHelper.onPackagesChanged(
@@ -3495,7 +3509,7 @@
                     getContext().sendBroadcastAsUser(new Intent(
                             NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
                                     .setPackage(pkg)
-                                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
                             UserHandle.of(userId), null);
                     handleSavePolicyFile();
                 }
@@ -4442,7 +4456,8 @@
                         am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
                                 WHITELIST_TOKEN, duration);
                         am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
-                                WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER));
+                                WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
+                                        | FLAG_SERVICE_SENDER));
                     }
                 }
             }
@@ -5698,19 +5713,17 @@
             }
             int indexBefore = findNotificationRecordIndexLocked(record);
             boolean interceptBefore = record.isIntercepted();
-            float contactAffinityBefore = record.getContactAffinity();
             int visibilityBefore = record.getPackageVisibilityOverride();
             recon.applyChangesLocked(record);
             applyZenModeLocked(record);
             mRankingHelper.sort(mNotificationList);
             int indexAfter = findNotificationRecordIndexLocked(record);
             boolean interceptAfter = record.isIntercepted();
-            float contactAffinityAfter = record.getContactAffinity();
             int visibilityAfter = record.getPackageVisibilityOverride();
             changed = indexBefore != indexAfter || interceptBefore != interceptAfter
                     || visibilityBefore != visibilityAfter;
             if (interceptBefore && !interceptAfter
-                    && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
+                    && record.isNewEnoughForAlerting(System.currentTimeMillis())) {
                 buzzBeepBlinkLocked(record);
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e2c64ca..5598741 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -91,7 +91,8 @@
 public final class NotificationRecord {
     static final String TAG = "NotificationRecord";
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final int MAX_LOGTAG_LENGTH = 35;
+    // the period after which a notification is updated where it can make sound
+    private static final int MAX_SOUND_DELAY_MS = 2000;
     final StatusBarNotification sbn;
     IActivityManager mAm;
     UriGrantsManagerInternal mUgmInternal;
@@ -125,7 +126,8 @@
     private long mVisibleSinceMs;
 
     // The most recent update time, or the creation time if no updates.
-    private long mUpdateTimeMs;
+    @VisibleForTesting
+    final long mUpdateTimeMs;
 
     // The most recent interruption time, or the creation time if no updates. Differs from the
     // above value because updates are filtered based on whether they actually interrupted the
@@ -159,13 +161,10 @@
     private ArrayList<String> mPeopleOverride;
     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
     private boolean mShowBadge;
-    private LogMaker mLogMaker;
     private Light mLight;
-    private String mGroupLogTag;
-    private String mChannelIdLogTag;
     /**
      * This list contains system generated smart actions from NAS, app-generated smart actions are
-     * stored in Notification.actions marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION.
+     * stored in Notification.actions with isContextual() set to true.
      */
     private ArrayList<Notification.Action> mSystemGeneratedSmartActions;
     private ArrayList<CharSequence> mSmartReplies;
@@ -786,7 +785,8 @@
             mImportanceExplanation = "user";
         }
         if (!getChannel().hasUserSetImportance()
-                && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+                && mAssistantImportance != IMPORTANCE_UNSPECIFIED
+                && !getChannel().isImportanceLockedByOEM()) {
             mImportance = mAssistantImportance;
             mImportanceExplanation = "asst";
         }
@@ -826,6 +826,10 @@
         return mIntercept;
     }
 
+    public boolean isNewEnoughForAlerting(long now) {
+        return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
+    }
+
     public void setHidden(boolean hidden) {
         mHidden = hidden;
     }
@@ -962,33 +966,6 @@
 
     public void setOverrideGroupKey(String overrideGroupKey) {
         sbn.setOverrideGroupKey(overrideGroupKey);
-        mGroupLogTag = null;
-    }
-
-    private String getGroupLogTag() {
-        if (mGroupLogTag == null) {
-            mGroupLogTag = shortenTag(sbn.getGroup());
-        }
-        return mGroupLogTag;
-    }
-
-    private String getChannelIdLogTag() {
-        if (mChannelIdLogTag == null) {
-            mChannelIdLogTag = shortenTag(mChannel.getId());
-        }
-        return mChannelIdLogTag;
-    }
-
-    private String shortenTag(String longTag) {
-        if (longTag == null) {
-            return null;
-        }
-        if (longTag.length() < MAX_LOGTAG_LENGTH) {
-            return longTag;
-        } else {
-            return longTag.substring(0, MAX_LOGTAG_LENGTH - 8) + "-" +
-                    Integer.toHexString(longTag.hashCode());
-        }
     }
 
     public NotificationChannel getChannel() {
@@ -1265,24 +1242,9 @@
     }
 
     public LogMaker getLogMaker(long now) {
-        if (mLogMaker == null) {
-            // initialize fields that only change on update (so a new record)
-            mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
-                    .setPackageName(sbn.getPackageName())
-                    .addTaggedData(MetricsEvent.NOTIFICATION_ID, sbn.getId())
-                    .addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag())
-                    .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
-        }
-        // reset fields that can change between updates, or are used by multiple logs
-        return mLogMaker
-                .clearCategory()
-                .clearType()
-                .clearSubtype()
+        return sbn.getLogMaker()
                 .clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
-                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
-                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
-                        sbn.getNotification().isGroupSummary() ? 1 : 0)
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 7c0e0b0..7a21aa2 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -68,6 +68,7 @@
     private static final String TAG = "NotificationPrefHelper";
     private static final int XML_VERSION = 1;
     private static final int UNKNOWN_UID = UserHandle.USER_NULL;
+    private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
 
     @VisibleForTesting
     static final String TAG_RANKING = "ranking";
@@ -80,7 +81,7 @@
     private static final String ATT_NAME = "name";
     private static final String ATT_UID = "uid";
     private static final String ATT_ID = "id";
-    private static final String ATT_APP_OVERLAY = "overlay";
+    private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
@@ -93,7 +94,9 @@
     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_SHOW_BADGE = true;
-    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+    private static final boolean DEFAULT_ALLOW_BUBBLE = true;
+    private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE  = false;
+
     /**
      * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
      * fields.
@@ -106,7 +109,7 @@
     @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
     public @interface LockableAppFields {
         int USER_LOCKED_IMPORTANCE = 0x00000001;
-        int USER_LOCKED_APP_OVERLAY = 0x00000002;
+        int USER_LOCKED_BUBBLE = 0x00000002;
     }
 
     // pkg|uid => PackagePreferences
@@ -174,7 +177,7 @@
                                     XmlUtils.readBooleanAttribute(
                                             parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
                                     XmlUtils.readBooleanAttribute(
-                                            parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
+                                            parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
                             r.importance = XmlUtils.readIntAttribute(
                                     parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                             r.priority = XmlUtils.readIntAttribute(
@@ -270,11 +273,11 @@
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
         return getOrCreatePackagePreferences(pkg, uid,
                 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
-                DEFAULT_ALLOW_APP_OVERLAY);
+                DEFAULT_ALLOW_BUBBLE);
     }
 
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
-            int priority, int visibility, boolean showBadge, boolean allowAppOverlay) {
+            int priority, int visibility, boolean showBadge, boolean allowBubble) {
         final String key = packagePreferencesKey(pkg, uid);
         synchronized (mPackagePreferences) {
             PackagePreferences
@@ -288,7 +291,7 @@
                 r.priority = priority;
                 r.visibility = visibility;
                 r.showBadge = showBadge;
-                r.appOverlay = allowAppOverlay;
+                r.allowBubble = allowBubble;
 
                 try {
                     createDefaultChannelIfNeeded(r);
@@ -390,7 +393,7 @@
                                 || r.channels.size() > 0
                                 || r.groups.size() > 0
                                 || r.delegate != null
-                                || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY;
+                                || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
                 if (hasNonDefaultSettings) {
                     out.startTag(null, TAG_PACKAGE);
                     out.attribute(null, ATT_NAME, r.pkg);
@@ -403,8 +406,8 @@
                     if (r.visibility != DEFAULT_VISIBILITY) {
                         out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
                     }
-                    if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) {
-                        out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay));
+                    if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
+                        out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
                     }
                     out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
                     out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
@@ -450,14 +453,28 @@
         out.endTag(null, TAG_RANKING);
     }
 
-    public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+    /**
+     * Sets whether bubbles are allowed.
+     *
+     * @param pkg the package to allow or not allow bubbles for.
+     * @param uid the uid to allow or not allow bubbles for.
+     * @param allowed whether bubbles are allowed.
+     */
+    public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
         PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
-        p.appOverlay = allowed;
-        p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY;
+        p.allowBubble = allowed;
+        p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
     }
 
-    public boolean areAppOverlaysAllowed(String pkg, int uid) {
-        return getOrCreatePackagePreferences(pkg, uid).appOverlay;
+    /**
+     * Whether bubbles are allowed.
+     *
+     * @param pkg the package to check if bubbles are allowed for
+     * @param uid the uid to check if bubbles are allowed for.
+     * @return whether bubbles are allowed.
+     */
+    public boolean areBubblessAllowed(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid).allowBubble;
     }
 
     public int getAppLockedFields(String pkg, int uid) {
@@ -621,6 +638,12 @@
             channel.setLockscreenVisibility(r.visibility);
         }
         clearLockedFields(channel);
+        channel.setImportanceLockedByOEM(r.oemLockedImportance);
+        if (!channel.isImportanceLockedByOEM()) {
+            if (r.futureOemLockedChannels.remove(channel.getId())) {
+                channel.setImportanceLockedByOEM(true);
+            }
+        }
         if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
             channel.setLockscreenVisibility(
                     NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
@@ -664,6 +687,12 @@
         } else {
             updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
         }
+        // no importance updates are allowed if OEM blocked it
+        updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
+        if (updatedChannel.isImportanceLockedByOEM()) {
+            updatedChannel.setImportance(channel.getImportance());
+        }
+
         r.channels.put(updatedChannel.getId(), updatedChannel);
 
         if (onlyHasDefaultChannel(pkg, uid)) {
@@ -753,6 +782,44 @@
         }
     }
 
+    public void lockChannelsForOEM(String[] appOrChannelList) {
+        if (appOrChannelList == null) {
+            return;
+        }
+        for (String appOrChannel : appOrChannelList) {
+            if (!TextUtils.isEmpty(appOrChannel)) {
+                String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
+                if (appSplit != null && appSplit.length > 0) {
+                    String appName = appSplit[0];
+                    String channelId = appSplit.length == 2 ? appSplit[1] : null;
+
+                    synchronized (mPackagePreferences) {
+                        for (PackagePreferences r : mPackagePreferences.values()) {
+                            if (r.pkg.equals(appName)) {
+                                if (channelId == null) {
+                                    // lock all channels for the app
+                                    r.oemLockedImportance = true;
+                                    for (NotificationChannel channel : r.channels.values()) {
+                                        channel.setImportanceLockedByOEM(true);
+                                    }
+                                } else {
+                                    NotificationChannel channel = r.channels.get(channelId);
+                                    if (channel != null) {
+                                        channel.setImportanceLockedByOEM(true);
+                                    } else {
+                                        // if this channel shows up in the future, make sure it'll
+                                        // be locked immediately
+                                        r.futureOemLockedChannels.add(channelId);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
             int uid, String groupId, boolean includeDeleted) {
         Preconditions.checkNotNull(pkg);
@@ -1180,8 +1247,8 @@
         if (original.canShowBadge() != update.canShowBadge()) {
             update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
         }
-        if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY);
+        if (original.isBubbleAllowed() != update.isBubbleAllowed()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
         }
     }
 
@@ -1602,8 +1669,10 @@
         int priority = DEFAULT_PRIORITY;
         int visibility = DEFAULT_VISIBILITY;
         boolean showBadge = DEFAULT_SHOW_BADGE;
-        boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+        boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
         int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
+        boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
+        List<String> futureOemLockedChannels = new ArrayList<>();
 
         Delegate delegate = null;
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b1e9144..b145e1e 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -106,9 +106,7 @@
 
         synchronized (mProxyByGroupTmp) {
             // record individual ranking result and nominate proxies for each group
-            // Note: iteration is done backwards such that the index can be used as a sort key
-            // in a string compare below
-            for (int i = N - 1; i >= 0; i--) {
+            for (int i = 0; i < N; i++) {
                 final NotificationRecord record = notificationList.get(i);
                 record.setAuthoritativeRank(i);
                 final String groupKey = record.getGroupKey();
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 2b581d6..abc9841 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.app.AlarmManager;
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -163,7 +164,6 @@
                     mSnoozedNotifications.get(userId).get(pkg);
             if (recordsForPkg != null) {
                 final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
-                String key = null;
                 for (Map.Entry<String, NotificationRecord> record : records) {
                     final StatusBarNotification sbn = record.getValue().sbn;
                     if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
@@ -305,6 +305,30 @@
         }
     }
 
+    protected void clearData(int userId, String pkg) {
+        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
+                mSnoozedNotifications.get(userId);
+        if (records == null) {
+            return;
+        }
+        ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
+        if (pkgRecords == null) {
+            return;
+        }
+        for (int i = pkgRecords.size() - 1; i >= 0; i--) {
+            final NotificationRecord r = pkgRecords.removeAt(i);
+            if (r != null) {
+                mPackages.remove(r.getKey());
+                mUsers.remove(r.getKey());
+                final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
+                mAm.cancel(pi);
+                MetricsLogger.action(r.getLogMaker()
+                        .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+                        .setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
+            }
+        }
+    }
+
     private PendingIntent createPendingIntent(String pkg, String key, int userId) {
         return PendingIntent.getBroadcast(mContext,
                 REQUEST_CODE_REPOST,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index e5b1878..afc0b72 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -375,8 +375,7 @@
             newConfig = mConfig.copy();
             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
-                if (rule.component.getPackageName().equals(packageName)
-                        && canManageAutomaticZenRule(rule)) {
+                if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) {
                     newConfig.automaticRules.removeAt(i);
                 }
             }
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 16143d3..74fbea1 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -24,11 +24,14 @@
 
 import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.Build.VERSION_CODES;
 import android.os.IBinder;
 import android.os.IIdmap2;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.Slog;
 
@@ -51,6 +54,13 @@
     private final Installer mInstaller;
     private IIdmap2 mIdmap2Service;
 
+    private static final boolean VENDOR_IS_Q_OR_LATER;
+    static {
+        // STOPSHIP(b/119390857): Check api version once Q sdk version is finalized
+        final String value = SystemProperties.get("ro.vndk.version", "Q");
+        VENDOR_IS_Q_OR_LATER = value.equals("Q") || value.equals("q");
+    }
+
     IdmapManager(final Installer installer) {
         mInstaller = installer;
         if (FEATURE_FLAG_IDMAP2) {
@@ -69,10 +79,13 @@
         final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
         try {
             if (FEATURE_FLAG_IDMAP2) {
-                if (mIdmap2Service.verifyIdmap(overlayPath, userId)) {
+                int policies = determineFulfilledPolicies(overlayPackage);
+                boolean enforce = enforceOverlayable(overlayPackage);
+                if (mIdmap2Service.verifyIdmap(overlayPath, policies, enforce, userId)) {
                     return true;
                 }
-                return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null;
+                return mIdmap2Service.createIdmap(targetPath, overlayPath, policies, enforce,
+                    userId) != null;
             } else {
                 mInstaller.idmap(targetPath, overlayPath, sharedGid);
                 return true;
@@ -156,4 +169,71 @@
             }, SECOND_IN_MILLIS);
         }
     }
+
+    /**
+     * Checks if overlayable and policies should be enforced on the specified overlay for backwards
+     * compatibility with pre-Q overlays.
+     */
+    private boolean enforceOverlayable(@NonNull final PackageInfo overlayPackage) {
+        final ApplicationInfo ai = overlayPackage.applicationInfo;
+        if (ai.targetSdkVersion >= VERSION_CODES.Q) {
+            // Always enforce policies for overlays targeting Q+.
+            return true;
+        }
+
+        if (ai.isVendor() && !VENDOR_IS_Q_OR_LATER) {
+            // If the overlay is on a pre-Q vendor partition, do not enforce overlayable
+            // restrictions on this overlay because the pre-Q platform has no understanding of
+            // overlayable.
+            return false;
+        }
+
+        // Do not enforce overlayable restrictions on pre-Q overlays signed with the
+        // platform signature.
+        return !ai.isSignedWithPlatformKey();
+    }
+
+    /**
+     * Retrieves a bitmask for idmap2 that represents the policies the specified overlay fulfills.
+     * @throws SecurityException if the overlay is not allowed to overlay any resource
+     */
+    private int determineFulfilledPolicies(@NonNull final PackageInfo overlayPackage)
+            throws SecurityException {
+        final ApplicationInfo ai = overlayPackage.applicationInfo;
+        final boolean overlayIsQOrLater = ai.targetSdkVersion >= VERSION_CODES.Q;
+
+        int fulfilledPolicies = 0;
+
+        // TODO(b/119402606) : Add signature policy
+
+        // Vendor partition (/vendor)
+        if (ai.isVendor()) {
+            if (overlayIsQOrLater) {
+                fulfilledPolicies |= IIdmap2.POLICY_VENDOR_PARTITION;
+            } else if (VENDOR_IS_Q_OR_LATER) {
+                throw new SecurityException("Overlay must target Q sdk or higher");
+            }
+        }
+
+        // Product partition (/product)
+        if (ai.isProduct()) {
+            if (overlayIsQOrLater) {
+                fulfilledPolicies |= IIdmap2.POLICY_PRODUCT_PARTITION;
+            } else {
+                throw new SecurityException("Overlay must target Q sdk or higher");
+            }
+        }
+
+        // System partition (/system)
+        if (ai.isSystemApp()) {
+            if (overlayIsQOrLater) {
+                fulfilledPolicies |= IIdmap2.POLICY_SYSTEM_PARTITION;
+            } else {
+                throw new SecurityException("Overlay must target Q sdk or higher");
+            }
+        }
+
+        // All overlays can overlay resources with the public policy
+        return fulfilledPolicies | IIdmap2.POLICY_PUBLIC;
+    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index b0d2704..1cbf0bf 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -270,7 +270,9 @@
             Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, 0);
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
diff --git a/services/core/java/com/android/server/os/BugreportManagerService.java b/services/core/java/com/android/server/os/BugreportManagerService.java
new file mode 100644
index 0000000..e241591
--- /dev/null
+++ b/services/core/java/com/android/server/os/BugreportManagerService.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.os;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/**
+ * Service that provides a privileged API to capture and consume bugreports.
+ *
+ * @hide
+ */
+public class BugreportManagerService extends SystemService {
+    private static final String TAG = "BugreportManagerService";
+
+    private BugreportManagerServiceImpl mService;
+
+    public BugreportManagerService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mService = new BugreportManagerServiceImpl(getContext());
+        // TODO(b/111441001): Needs sepolicy to be submitted first.
+        // publishBinderService(Context.BUGREPORT_SERVICE, mService);
+    }
+}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
new file mode 100644
index 0000000..faa4714
--- /dev/null
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 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.os;
+
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.os.BugreportParams;
+import android.os.IDumpstate;
+import android.os.IDumpstateListener;
+import android.os.IDumpstateToken;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+
+// TODO(b/111441001):
+// 1. Handle the case where another bugreport is in progress
+// 2. Make everything threadsafe
+// 3. Pass validation & other errors on listener
+
+/**
+ * Implementation of the service that provides a privileged API to capture and consume bugreports.
+ *
+ * <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}.
+ */
+class BugreportManagerServiceImpl extends IDumpstate.Stub {
+    private static final String TAG = "BugreportManagerService";
+    private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
+
+    private IDumpstate mDs = null;
+    private final Context mContext;
+
+    BugreportManagerServiceImpl(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    public IDumpstateToken setListener(String name, IDumpstateListener listener,
+            boolean getSectionDetails) throws RemoteException {
+        // TODO(b/111441001): Figure out if lazy setting of listener should be allowed
+        // and if so how to handle it.
+        throw new UnsupportedOperationException("setListener is not allowed on this service");
+    }
+
+
+    @Override
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    public void startBugreport(FileDescriptor bugreportFd, FileDescriptor screenshotFd,
+            int bugreportMode, IDumpstateListener listener) throws RemoteException {
+
+        validate(bugreportMode);
+
+        mDs = getDumpstateService();
+        if (mDs == null) {
+            Slog.w(TAG, "Unable to get bugreport service");
+            // TODO(b/111441001): pass error on listener
+            return;
+        }
+        mDs.startBugreport(bugreportFd, screenshotFd, bugreportMode, listener);
+    }
+
+    private boolean validate(@BugreportParams.BugreportMode int mode) {
+        if (mode != BugreportParams.BUGREPORT_MODE_FULL
+                && mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
+                && mode != BugreportParams.BUGREPORT_MODE_REMOTE
+                && mode != BugreportParams.BUGREPORT_MODE_WEAR
+                && mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
+                && mode != BugreportParams.BUGREPORT_MODE_WIFI) {
+            Slog.w(TAG, "Unknown bugreport mode: " + mode);
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * Start and get a handle to the native implementation of {@code IDumpstate} which does the
+     * actual bugreport generation.
+     *
+     * <p>Generating bugreports requires root privileges. To limit the footprint
+     * of the root access, the actual generation in Dumpstate binary is accessed as a
+     * oneshot service 'bugreport'.
+     */
+    private IDumpstate getDumpstateService() {
+        // Start bugreport service.
+        SystemProperties.set("ctl.start", "bugreport");
+
+        IDumpstate ds = null;
+        boolean timedOut = false;
+        int totalTimeWaitedMillis = 0;
+        int seedWaitTimeMillis = 500;
+        while (!timedOut) {
+            // Note that the binder service on the native side is "dumpstate".
+            ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
+            if (ds != null) {
+                Slog.i(TAG, "Got bugreport service handle.");
+                break;
+            }
+            SystemClock.sleep(seedWaitTimeMillis);
+            Slog.i(TAG,
+                    "Waiting to get dumpstate service handle (" + totalTimeWaitedMillis + "ms)");
+            totalTimeWaitedMillis += seedWaitTimeMillis;
+            seedWaitTimeMillis *= 2;
+            timedOut = totalTimeWaitedMillis > DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS;
+        }
+        if (timedOut) {
+            Slog.w(TAG,
+                    "Timed out waiting to get dumpstate service handle ("
+                    + totalTimeWaitedMillis + "ms)");
+        }
+        return ds;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index d6ab5f7..65fc982 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -27,24 +27,29 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.os.BatteryManager;
 import android.os.Environment;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.StatsLog;
 
-import com.android.server.pm.dex.DexManager;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 import com.android.server.PinnerService;
+import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 
 import java.io.File;
+import java.nio.file.Paths;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * {@hide}
@@ -289,6 +294,50 @@
         return result;
     }
 
+    /**
+     * Get the size of the directory. It uses recursion to go over all files.
+     * @param f
+     * @return
+     */
+    private long getDirectorySize(File f) {
+        long size = 0;
+        if (f.isDirectory()) {
+            for (File file: f.listFiles()) {
+                size += getDirectorySize(file);
+            }
+        } else {
+            size = f.length();
+        }
+        return size;
+    }
+
+    /**
+     * Get the size of a package.
+     * @param pkg
+     */
+    private long getPackageSize(PackageManagerService pm, String pkg) {
+        PackageInfo info = pm.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+        long size = 0;
+        if (info != null && info.applicationInfo != null) {
+            File path = Paths.get(info.applicationInfo.sourceDir).toFile();
+            if (path.isFile()) {
+                path = path.getParentFile();
+            }
+            size += getDirectorySize(path);
+            if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
+                for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
+                    path = Paths.get(splitSourceDir).toFile();
+                    if (path.isFile()) {
+                        path = path.getParentFile();
+                    }
+                    size += getDirectorySize(path);
+                }
+            }
+            return size;
+        }
+        return 0;
+    }
+
     private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
             long lowStorageThreshold, boolean is_for_primary_dex,
             ArraySet<String> failedPackageNames) {
@@ -315,8 +364,10 @@
 
             int reason;
             boolean downgrade;
+            long package_size_before = 0; //used when the app is downgraded
             // Downgrade unused packages.
             if (unusedPackages.contains(pkg) && shouldDowngrade) {
+                package_size_before = getPackageSize(pm, pkg);
                 // This applies for system apps or if packages location is not a directory, i.e.
                 // monolithic install.
                 if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
@@ -366,6 +417,10 @@
                 synchronized (failedPackageNames) {
                     failedPackageNames.remove(pkg);
                 }
+                if (downgrade) {
+                    StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before,
+                            getPackageSize(pm, pkg), /*aggressive=*/ false);
+                }
             }
         }
         notifyPinService(updatedPackages);
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 3b11525..8facce1 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -42,6 +42,7 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.DebugUtils;
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.Pair;
@@ -60,6 +61,7 @@
 
 /** Resolves all Android component types [activities, services, providers and receivers]. */
 public class ComponentResolver {
+    private static final boolean DEBUG = false;
     private static final String TAG = "PackageManager";
     private static final boolean DEBUG_FILTERS = false;
     private static final boolean DEBUG_SHOW_INFO = false;
@@ -1198,22 +1200,48 @@
             return packageName.equals(info.activity.owner.packageName);
         }
 
+        private void log(String reason, ActivityIntentInfo info, int match,
+                int userId) {
+            Slog.w(TAG, reason
+                    + "; match: "
+                    + DebugUtils.flagsToString(IntentFilter.class, "MATCH_", match)
+                    + "; userId: " + userId
+                    + "; intent info: " + info);
+        }
+
         @Override
         protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
                 int match, int userId) {
-            if (!sUserManager.exists(userId)) return null;
+            if (!sUserManager.exists(userId)) {
+                if (DEBUG) {
+                    log("User doesn't exist", info, match, userId);
+                }
+                return null;
+            }
             if (!sPackageManagerInternal.isEnabledAndMatches(info.activity.info, mFlags, userId)) {
+                if (DEBUG) {
+                    log("!PackageManagerInternal.isEnabledAndMatches; mFlags="
+                            + DebugUtils.flagsToString(PackageManager.class, "MATCH_", mFlags),
+                            info, match, userId);
+                }
                 return null;
             }
             final PackageParser.Activity activity = info.activity;
             PackageSetting ps = (PackageSetting) activity.owner.mExtras;
             if (ps == null) {
+                if (DEBUG) {
+                    log("info.activity.owner.mExtras == null", info, match, userId);
+                }
                 return null;
             }
             final PackageUserState userState = ps.readUserState(userId);
             ActivityInfo ai =
                     PackageParser.generateActivityInfo(activity, mFlags, userState, userId);
             if (ai == null) {
+                if (DEBUG) {
+                    log("Failed to create ActivityInfo based on " + info.activity, info, match,
+                            userId);
+                }
                 return null;
             }
             final boolean matchExplicitlyVisibleOnly =
@@ -1227,15 +1255,31 @@
             final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to ephemeral apps
             if (matchVisibleToInstantApp && !(componentVisible || userState.instantApp)) {
+                if (DEBUG) {
+                    log("Filter(s) not visible to ephemeral apps"
+                            + "; matchVisibleToInstantApp=" + matchVisibleToInstantApp
+                            + "; matchInstantApp=" + matchInstantApp
+                            + "; info.isVisibleToInstantApp()=" + info.isVisibleToInstantApp()
+                            + "; matchExplicitlyVisibleOnly=" + matchExplicitlyVisibleOnly
+                            + "; info.isExplicitlyVisibleToInstantApp()="
+                                    + info.isExplicitlyVisibleToInstantApp(),
+                            info, match, userId);
+                }
                 return null;
             }
             // throw out instant app filters if we're not explicitly requesting them
             if (!matchInstantApp && userState.instantApp) {
+                if (DEBUG) {
+                    log("Instant app filter is not explicitly requested", info, match, userId);
+                }
                 return null;
             }
             // throw out instant app filters if updates are available; will trigger
             // instant app resolution
             if (userState.instantApp && ps.isUpdateAvailable()) {
+                if (DEBUG) {
+                    log("Instant app update is available", info, match, userId);
+                }
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 7f1fb6c..b6dae19 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -24,6 +24,7 @@
 import android.app.AppGlobals;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -115,6 +116,7 @@
         private final ShortcutServiceInternal mShortcutServiceInternal;
         private final PackageCallbackList<IOnAppsChangedListener> mListeners
                 = new PackageCallbackList<IOnAppsChangedListener>();
+        private final DevicePolicyManager mDpm;
 
         private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
@@ -133,6 +135,7 @@
                     LocalServices.getService(ShortcutServiceInternal.class));
             mShortcutServiceInternal.addListener(mPackageMonitor);
             mCallbackHandler = BackgroundThread.getHandler();
+            mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
         }
 
         @VisibleForTesting
@@ -301,6 +304,17 @@
         }
 
         @Override
+        public boolean shouldHideFromSuggestions(String packageName, UserHandle user) {
+            if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
+                return false;
+            }
+            final PackageManagerInternal pmi = LocalServices.getService(
+                    PackageManagerInternal.class);
+            int flags = pmi.getDistractingPackageRestrictions(packageName, user.getIdentifier());
+            return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
+        }
+
+        @Override
         public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
                 String packageName, UserHandle user) throws RemoteException {
             ParceledListSlice<ResolveInfo> launcherActivities = queryActivitiesForUser(
@@ -313,30 +327,42 @@
                     Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
                 return launcherActivities;
             }
-
-            final int callingUid = injectBinderCallingUid();
-            final ArrayList<ResolveInfo> result = new ArrayList<>(launcherActivities.getList());
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
-            if (packageName != null) {
-                // If this hidden app should not be shown, return the original list.
-                // Otherwise, inject hidden activity that forwards user to app details page.
-                if (result.size() > 0) {
-                    return launcherActivities;
-                }
-                ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
-                        callingUid, user.getIdentifier());
-                if (shouldShowHiddenApp(appInfo)) {
-                    ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
-                    if (info != null) {
-                        result.add(info);
-                    }
-                }
-                return new ParceledListSlice<>(result);
+            if (launcherActivities == null) {
+                // Cannot access profile, so we don't even return any hidden apps.
+                return null;
             }
 
-            long ident = injectClearCallingIdentity();
+            final int callingUid = injectBinderCallingUid();
+            final long ident = injectClearCallingIdentity();
             try {
+                if (mUm.getUserInfo(user.getIdentifier()).isManagedProfile()) {
+                    // Managed profile should not show hidden apps
+                    return launcherActivities;
+                }
+                if (mDpm.getDeviceOwnerComponentOnAnyUser() != null) {
+                    // Device owner devices should not show hidden apps
+                    return launcherActivities;
+                }
+
+                final ArrayList<ResolveInfo> result = new ArrayList<>(launcherActivities.getList());
+                final PackageManagerInternal pmInt =
+                        LocalServices.getService(PackageManagerInternal.class);
+                if (packageName != null) {
+                    // If this hidden app should not be shown, return the original list.
+                    // Otherwise, inject hidden activity that forwards user to app details page.
+                    if (result.size() > 0) {
+                        return launcherActivities;
+                    }
+                    ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
+                            callingUid, user.getIdentifier());
+                    if (shouldShowHiddenApp(appInfo)) {
+                        ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
+                        if (info != null) {
+                            result.add(info);
+                        }
+                    }
+                    return new ParceledListSlice<>(result);
+                }
                 final HashSet<String> visiblePackages = new HashSet<>();
                 for (ResolveInfo info : result) {
                     visiblePackages.add(info.activityInfo.packageName);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index f9e31ae..5412e94 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -85,9 +85,6 @@
     // One minute over PM WATCHDOG_TIMEOUT
     private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
 
-    /** Special library name that skips shared libraries check during compilation. */
-    public static final String SKIP_SHARED_LIBRARY_CHECK = "&";
-
     @GuardedBy("mInstallLock")
     private final Installer mInstaller;
     private final Object mInstallLock;
@@ -397,23 +394,23 @@
             Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
             return DEX_OPT_FAILED;
         }
-        Log.d(TAG, "Running dexopt on: " + path
-                + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
-                + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
-                + " target-filter=" + compilerFilter);
-
-        String classLoaderContext;
+        String classLoaderContext = null;
         if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
-            // If we have an unknown (not yet set), or a variable class loader chain, compile
-            // without a context and mark the oat file with SKIP_SHARED_LIBRARY_CHECK. Note that
-            // this might lead to a incorrect compilation.
-            // TODO(calin): We should just extract in this case.
-            classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+            // If we have an unknown (not yet set), or a variable class loader chain. Just extract
+            // the dex file.
+            compilerFilter = "extract";
         } else {
             classLoaderContext = dexUseInfo.getClassLoaderContext();
         }
 
         int reason = options.getCompilationReason();
+        Log.d(TAG, "Running dexopt on: " + path
+                + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
+                + " reason=" + getReasonName(reason)
+                + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+                + " target-filter=" + compilerFilter
+                + " class-loader-context=" + classLoaderContext);
+
         try {
             for (String isa : dexUseInfo.getLoaderIsas()) {
                 // Reuse the same dexopt path as for the primary apks. We don't need all the
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 7ca39df..bf4e272 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -225,6 +225,17 @@
                 Slog.w(TAG, "Deleting orphan icon " + icon);
                 icon.delete();
             }
+
+            // Invalid sessions might have been marked while parsing. Re-write the database with
+            // the updated information.
+            writeSessionsLocked();
+
+            for (int i = 0; i < mSessions.size(); i++) {
+                final PackageInstallerSession session = mSessions.valueAt(i);
+                if (session.isStaged()) {
+                    mStagingManager.restoreSession(session);
+                }
+            }
         }
     }
 
@@ -533,7 +544,7 @@
         session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
                 mInstallThread.getLooper(), mStagingManager, sessionId, userId,
                 installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
-                false, null, SessionInfo.INVALID_ID);
+                false, null, SessionInfo.INVALID_ID, false, false, false, SessionInfo.NO_ERROR);
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
@@ -1119,6 +1130,11 @@
             mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
         }
 
+        public void onStagedSessionChanged(PackageInstallerSession session) {
+            writeSessionsAsync();
+            mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
+        }
+
         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
             mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
 
@@ -1131,7 +1147,9 @@
                         }
                     }
                     synchronized (mSessions) {
-                        mSessions.remove(session.sessionId);
+                        if (!session.isStaged() || !success) {
+                            mSessions.remove(session.sessionId);
+                        }
                         addHistoricalSessionLocked(session);
 
                         final File appIconFile = buildAppIconFile(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 982daa5..12d335d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -57,6 +57,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
@@ -150,6 +151,10 @@
     private static final String ATTR_MULTI_PACKAGE = "multiPackage";
     private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
     private static final String ATTR_STAGED_SESSION = "stagedSession";
+    private static final String ATTR_IS_READY = "isReady";
+    private static final String ATTR_IS_FAILED = "isFailed";
+    private static final String ATTR_IS_APPLIED = "isApplied";
+    private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode";
     private static final String ATTR_MODE = "mode";
     private static final String ATTR_INSTALL_FLAGS = "installFlags";
     private static final String ATTR_INSTALL_LOCATION = "installLocation";
@@ -407,7 +412,8 @@
             int sessionId, int userId,
             String installerPackageName, int installerUid, SessionParams params, long createdMillis,
             File stageDir, String stageCid, boolean prepared, boolean sealed,
-            @Nullable int[] childSessionIds, int parentSessionId) {
+            @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
+            boolean isFailed, boolean isApplied, int stagedSessionErrorCode) {
         mCallback = callback;
         mContext = context;
         mPm = pm;
@@ -437,7 +443,10 @@
         }
 
         mPrepared = prepared;
-
+        mStagedSessionReady = isReady;
+        mStagedSessionFailed = isFailed;
+        mStagedSessionApplied = isApplied;
+        mStagedSessionErrorCode = stagedSessionErrorCode;
         if (sealed) {
             synchronized (mLock) {
                 try {
@@ -982,9 +991,9 @@
         mSealed = true;
 
         // Read transfers from the original owner stay open, but as the session's data
-        // cannot be modified anymore, there is no leak of information.
-        // For staged sessions, the validation is performed by StagingManager.
-        if (!params.isMultiPackage && !params.isStaged) {
+        // cannot be modified anymore, there is no leak of information. For staged sessions,
+        // further validation may be performed by the staging manager.
+        if (!params.isMultiPackage) {
             final PackageInfo pkgInfo = mPm.getPackageInfo(
                     params.appPackageName, PackageManager.GET_SIGNATURES
                             | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
@@ -1328,18 +1337,15 @@
         mSigningDetails = apk.signingDetails;
         mResolvedBaseFile = addedFile;
 
-        assertApkConsistentLocked(String.valueOf(addedFile), apk);
-
-        if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
-            try {
-                // STOPSHIP: For APEX we should also implement proper APK Signature verification.
-                mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
-                    pkgInfo.applicationInfo.sourceDir,
-                    PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
-            } catch (PackageParserException e) {
-                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                    "Couldn't obtain signatures from base APK");
-            }
+        // STOPSHIP: Ensure that we remove the non-staged version of APEX installs in production
+        // because we currently do not verify that signatures are consistent with the previously
+        // installed version in that case.
+        //
+        // When that happens, this hack can be reverted and we can rely on APEXd to map between
+        // APEX files and their package names instead of parsing it out of the AndroidManifest
+        // such as here.
+        if (params.appPackageName == null) {
+            params.appPackageName = mPackageName;
         }
     }
 
@@ -1374,7 +1380,8 @@
                     "Missing existing base package");
         }
         // Default to require only if existing base has fs-verity.
-        mVerityFound = params.mode == SessionParams.MODE_INHERIT_EXISTING
+        mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
+                && params.mode == SessionParams.MODE_INHERIT_EXISTING
                 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
 
         try {
@@ -2002,6 +2009,7 @@
         mCallback.onSessionFinished(this, success);
     }
 
+    /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
             mStagedSessionReady = true;
@@ -2009,13 +2017,52 @@
             mStagedSessionFailed = false;
             mStagedSessionErrorCode = SessionInfo.NO_ERROR;
         }
+        mCallback.onStagedSessionChanged(this);
+    }
+
+    /** {@hide} */
+    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode) {
+        synchronized (mLock) {
+            mStagedSessionReady = false;
+            mStagedSessionApplied = false;
+            mStagedSessionFailed = true;
+            mStagedSessionErrorCode = errorCode;
+        }
+        mCallback.onStagedSessionChanged(this);
+    }
+
+    /** {@hide} */
+    void setStagedSessionApplied() {
+        synchronized (mLock) {
+            mStagedSessionReady = false;
+            mStagedSessionApplied = true;
+            mStagedSessionFailed = false;
+            mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+        }
+        mCallback.onStagedSessionChanged(this);
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionReady() {
+        return mStagedSessionReady;
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionApplied() {
+        return mStagedSessionApplied;
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionFailed() {
+        return mStagedSessionFailed;
     }
 
     private void destroyInternal() {
         synchronized (mLock) {
             mSealed = true;
-            mDestroyed = true;
-
+            if (!params.isStaged) {
+                mDestroyed = true;
+            }
             // Force shut down all bridges
             for (RevocableFileDescriptor fd : mFds) {
                 fd.revoke();
@@ -2122,6 +2169,10 @@
 
             writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
             writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
+            writeBooleanAttribute(out, ATTR_IS_READY, mStagedSessionReady);
+            writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed);
+            writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied);
+            writeIntAttribute(out, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode);
             // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
             //                       we've read all sessions.
             writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId);
@@ -2199,6 +2250,16 @@
         return permissionsArray;
     }
 
+    // Sanity check to be performed when the session is restored from an external file. Only one
+    // of the session states should be true, or none of them.
+    private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
+                                                     boolean isFailed) {
+        return (!isReady && !isApplied && !isFailed)
+                || (isReady && !isApplied && !isFailed)
+                || (!isReady && isApplied && !isFailed)
+                || (!isReady && !isApplied && isFailed);
+    }
+
     /**
      * Read new session from a {@link XmlPullParser xml description} and create it.
      *
@@ -2260,10 +2321,20 @@
             params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
             params.appIconLastModified = appIconFile.lastModified();
         }
+        final boolean isReady = readBooleanAttribute(in, ATTR_IS_READY);
+        final boolean isFailed = readBooleanAttribute(in, ATTR_IS_FAILED);
+        final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
+        final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE);
+
+        if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
+            throw new IllegalArgumentException("Can't restore staged session with invalid state.");
+        }
+
         return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 installerThread, stagingManager, sessionId, userId, installerPackageName,
                 installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
-                EMPTY_CHILD_SESSION_ARRAY, parentSessionId);
+                EMPTY_CHILD_SESSION_ARRAY, parentSessionId, isReady, isFailed, isApplied,
+                stagedSessionErrorCode);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5c4c1ba..3300900 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -84,6 +84,7 @@
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.RESTRICTION_NONE;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -238,6 +239,7 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
+import android.permission.PermissionControllerManager;
 import android.provider.MediaStore;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -316,7 +318,6 @@
 import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionsState;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -369,6 +370,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -442,6 +444,8 @@
     private static final boolean ENABLE_FREE_CACHE_V2 =
             SystemProperties.getBoolean("fw.free_cache_v2", true);
 
+    private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -1338,6 +1342,8 @@
     final @Nullable String mSystemTextClassifierPackage;
     final @Nullable String mWellbeingPackage;
     final @Nullable String mDocumenterPackage;
+    final @Nullable String mConfiguratorPackage;
+    final @Nullable String mAppPredictionServicePackage;
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
@@ -1454,8 +1460,9 @@
                             Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
                                     args.traceCookie);
                         }
-                    } else {
-                        Slog.e(TAG, "Bogus post-install token " + msg.arg1);
+                    } else if (DEBUG_INSTALL) {
+                        // No post-install when we run restore from installExistingPackageForUser
+                        Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
                     }
 
                     Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
@@ -2861,6 +2868,9 @@
 
             mWellbeingPackage = getWellbeingPackageName();
             mDocumenterPackage = getDocumenterPackageName();
+            mConfiguratorPackage =
+                    mContext.getString(R.string.config_deviceConfiguratorPackageName);
+            mAppPredictionServicePackage = getAppPredictionServicePackageName();
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
@@ -3743,7 +3753,7 @@
     /**
      * Returns whether or not a full application can see an instant application.
      * <p>
-     * Currently, there are three cases in which this can occur:
+     * Currently, there are four cases in which this can occur:
      * <ol>
      * <li>The calling application is a "special" process. Special processes
      *     are those with a UID < {@link Process#FIRST_APPLICATION_UID}.</li>
@@ -3751,6 +3761,7 @@
      *     {@link android.Manifest.permission#ACCESS_INSTANT_APPS}.</li>
      * <li>The calling application is the default launcher on the
      *     system partition.</li>
+     * <li>The calling application is the default app prediction service.</li>
      * </ol>
      */
     private boolean canViewInstantApps(int callingUid, int userId) {
@@ -3768,6 +3779,11 @@
                     && isCallerSameApp(homeComponent.getPackageName(), callingUid)) {
                 return true;
             }
+            // TODO(b/122900055) Change/Remove this and replace with new permission role.
+            if (mAppPredictionServicePackage != null
+                    && isCallerSameApp(mAppPredictionServicePackage, callingUid)) {
+                return true;
+            }
         }
         return false;
     }
@@ -5483,13 +5499,13 @@
         final int callingUserId = UserHandle.getUserId(callingUid);
         final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
         // Map to base uids.
-        uid1 = UserHandle.getAppId(uid1);
-        uid2 = UserHandle.getAppId(uid2);
+        final int appId1 = UserHandle.getAppId(uid1);
+        final int appId2 = UserHandle.getAppId(uid2);
         // reader
         synchronized (mPackages) {
             Signature[] s1;
             Signature[] s2;
-            Object obj = mSettings.getSettingLPr(uid1);
+            Object obj = mSettings.getSettingLPr(appId1);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     if (isCallerInstantApp) {
@@ -5508,7 +5524,7 @@
             } else {
                 return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
             }
-            obj = mSettings.getSettingLPr(uid2);
+            obj = mSettings.getSettingLPr(appId2);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     if (isCallerInstantApp) {
@@ -5563,11 +5579,11 @@
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
         // Map to base uids.
-        uid = UserHandle.getAppId(uid);
+        final int appId = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
             final PackageParser.SigningDetails signingDetails;
-            final Object obj = mSettings.getSettingLPr(uid);
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
@@ -5683,10 +5699,10 @@
         final int callingUid = Binder.getCallingUid();
         final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
         final int userId = UserHandle.getUserId(uid);
-        uid = UserHandle.getAppId(uid);
+        final int appId = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getSettingLPr(uid);
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 if (isCallerInstantApp) {
                     return null;
@@ -5721,8 +5737,9 @@
         if (getInstantAppPackageName(callingUid) != null) {
             return null;
         }
+        final int appId = UserHandle.getAppId(uid);
         synchronized (mPackages) {
-            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.name + ":" + sus.userId;
@@ -5749,8 +5766,8 @@
         final String[] names = new String[uids.length];
         synchronized (mPackages) {
             for (int i = uids.length - 1; i >= 0; i--) {
-                final int uid = uids[i];
-                Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
+                final int appId = UserHandle.getAppId(uids[i]);
+                final Object obj = mSettings.getSettingLPr(appId);
                 if (obj instanceof SharedUserSetting) {
                     final SharedUserSetting sus = (SharedUserSetting) obj;
                     names[i] = "shared:" + sus.name;
@@ -5798,8 +5815,9 @@
         if (getInstantAppPackageName(callingUid) != null) {
             return 0;
         }
+        final int appId = UserHandle.getAppId(uid);
         synchronized (mPackages) {
-            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgFlags;
@@ -5820,8 +5838,9 @@
         if (getInstantAppPackageName(callingUid) != null) {
             return 0;
         }
+        final int appId = UserHandle.getAppId(uid);
         synchronized (mPackages) {
-            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgPrivateFlags;
@@ -5841,10 +5860,10 @@
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return false;
         }
-        uid = UserHandle.getAppId(uid);
+        final int appId = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getSettingLPr(uid);
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 final Iterator<PackageSetting> it = sus.packages.iterator();
@@ -8549,16 +8568,16 @@
     }
 
     /**
-     * Returns if full apk verification can be skipped for the whole package, including the splits.
+     * Returns if forced apk verification can be skipped for the whole package, including splits.
      */
-    private boolean canSkipFullPackageVerification(PackageParser.Package pkg) {
-        if (!canSkipFullApkVerification(pkg.baseCodePath)) {
+    private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) {
+        if (!canSkipForcedApkVerification(pkg.baseCodePath)) {
             return false;
         }
         // TODO: Allow base and splits to be verified individually.
         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
             for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                if (!canSkipFullApkVerification(pkg.splitCodePaths[i])) {
+                if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) {
                     return false;
                 }
             }
@@ -8567,14 +8586,17 @@
     }
 
     /**
-     * Returns if full apk verification can be skipped, depending on current FSVerity setup and
+     * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
      * whether the apk contains signed root hash.  Note that the signer's certificate still needs to
      * match one in a trusted source, and should be done separately.
      */
-    private boolean canSkipFullApkVerification(String apkPath) {
-        final byte[] rootHashObserved;
+    private boolean canSkipForcedApkVerification(String apkPath) {
+        if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+            return VerityUtils.hasFsverity(apkPath);
+        }
+
         try {
-            rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+            final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
             if (rootHashObserved == null) {
                 return false;  // APK does not contain Merkle tree root hash.
             }
@@ -8746,7 +8768,7 @@
         // in verified partition, or can be verified on access (when apk verity is enabled). In both
         // cases, only data in Signing Block is verified instead of the whole file.
         final boolean skipVerify = scanSystemPartition
-                || (forceCollect && canSkipFullPackageVerification(pkg));
+                || (forceCollect && canSkipForcedPackageVerification(pkg));
         collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
 
         // Reset profile if the application version is changed
@@ -12654,22 +12676,30 @@
         info.sendPackageRemovedBroadcasts(true /*killApp*/);
     }
 
+    private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
+            int distractionFlags) {
+        final Bundle extras = new Bundle(3);
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+        extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+        sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null);
+    }
+
     private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
             boolean suspended, PersistableBundle launcherExtras) {
-        if (pkgList.length > 0) {
-            Bundle extras = new Bundle(1);
-            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
-            extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
-            if (launcherExtras != null) {
-                extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
-                        new Bundle(launcherExtras.deepCopy()));
-            }
-            sendPackageBroadcast(
-                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
-                            : Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
-                    new int[] {userId}, null);
+        final Bundle extras = new Bundle(3);
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+        if (launcherExtras != null) {
+            extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
+                    new Bundle(launcherExtras.deepCopy()));
         }
+        sendPackageBroadcast(
+                suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+                        : Intent.ACTION_PACKAGES_UNSUSPENDED,
+                null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
+                new int[] {userId}, null);
     }
 
     /**
@@ -12708,6 +12738,11 @@
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
             int installReason) {
+        if (DEBUG_INSTALL) {
+            Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
+                    + " installFlags=" + installFlags + " installReason=" + installReason);
+        }
+
         final int callingUid = Binder.getCallingUid();
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED
@@ -12778,6 +12813,11 @@
                 synchronized (mPackages) {
                     updateSequenceNumberLP(pkgSetting, new int[]{ userId });
                 }
+                // start async restore with no post-install since we finish install here
+                PackageInstalledInfo res =
+                        createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED);
+                res.pkg = pkgSetting.pkg;
+                restoreAndPostInstall(userId, res, null);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -12819,6 +12859,62 @@
     }
 
     @Override
+    public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
+            int restrictionFlags, int userId) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+                "setPackagesSuspendedAsUser");
+
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
+                && UserHandle.getUserId(callingUid) != userId) {
+            throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
+                    + userId);
+        }
+        Preconditions.checkNotNull(packageNames, "packageNames cannot be null");
+
+        final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
+        final IntArray changedUids = new IntArray(packageNames.length);
+        final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+
+        for (int i = 0; i < packageNames.length; i++) {
+            final String packageName = packageNames[i];
+            final PackageSetting pkgSetting;
+            synchronized (mPackages) {
+                pkgSetting = mSettings.mPackages.get(packageName);
+                if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                    Slog.w(TAG, "Could not find package setting for package: " + packageName
+                            + ". Skipping...");
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+            }
+            if (restrictionFlags != 0 && !canSuspendPackageForUserInternal(packageName, userId)) {
+                unactionedPackages.add(packageName);
+                continue;
+            }
+            synchronized (mPackages) {
+                final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId);
+                if (restrictionFlags != oldDistractionFlags) {
+                    pkgSetting.setDistractionFlags(restrictionFlags, userId);
+                    changedPackagesList.add(packageName);
+                    changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+                }
+            }
+        }
+
+        if (!changedPackagesList.isEmpty()) {
+            final String[] changedPackages = changedPackagesList.toArray(
+                    new String[changedPackagesList.size()]);
+            sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
+                    restrictionFlags);
+            synchronized (mPackages) {
+                scheduleWritePackageRestrictionsLocked(userId);
+            }
+        }
+        return unactionedPackages.toArray(new String[0]);
+    }
+
+    @Override
     public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
             PersistableBundle appExtras, PersistableBundle launcherExtras,
             SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
@@ -12843,44 +12939,37 @@
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            for (int i = 0; i < packageNames.length; i++) {
-                final String packageName = packageNames[i];
-                if (callingPackage.equals(packageName)) {
-                    Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
-                            + (suspended ? "" : "un") + "suspend itself. Ignoring");
-                    unactionedPackages.add(packageName);
-                    continue;
-                }
-                PackageSetting pkgSetting;
-                synchronized (mPackages) {
-                    pkgSetting = mSettings.mPackages.get(packageName);
-                    if (pkgSetting == null
-                            || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
-                        Slog.w(TAG, "Could not find package setting for package: " + packageName
-                                + ". Skipping suspending/un-suspending.");
-                        unactionedPackages.add(packageName);
-                        continue;
-                    }
-                }
-                if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
-                    unactionedPackages.add(packageName);
-                    continue;
-                }
-                synchronized (mPackages) {
-                    pkgSetting = mSettings.mPackages.get(packageName);
-                    if (pkgSetting != null) {
-                        pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
-                                launcherExtras, userId);
-                    }
-                }
-                changedPackagesList.add(packageName);
-                changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+
+        for (int i = 0; i < packageNames.length; i++) {
+            final String packageName = packageNames[i];
+            if (callingPackage.equals(packageName)) {
+                Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+                        + (suspended ? "" : "un") + "suspend itself. Ignoring");
+                unactionedPackages.add(packageName);
+                continue;
             }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
+            final PackageSetting pkgSetting;
+            synchronized (mPackages) {
+                pkgSetting = mSettings.mPackages.get(packageName);
+                if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                    Slog.w(TAG, "Could not find package setting for package: " + packageName
+                            + ". Skipping suspending/un-suspending.");
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+            }
+            if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
+                unactionedPackages.add(packageName);
+                continue;
+            }
+            synchronized (mPackages) {
+                pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
+                        launcherExtras, userId);
+            }
+            changedPackagesList.add(packageName);
+            changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
         }
+
         if (!changedPackagesList.isEmpty()) {
             final String[] changedPackages = changedPackagesList.toArray(
                     new String[changedPackagesList.size()]);
@@ -13032,88 +13121,87 @@
                     + " cannot query getUnsuspendablePackagesForUser for user " + userId);
         }
         final ArraySet<String> unactionablePackages = new ArraySet<>();
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            for (String packageName : packageNames) {
-                if (!canSuspendPackageForUserInternal(packageName, userId)) {
-                    unactionablePackages.add(packageName);
-                }
+        for (String packageName : packageNames) {
+            if (!canSuspendPackageForUserInternal(packageName, userId)) {
+                unactionablePackages.add(packageName);
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
         }
         return unactionablePackages.toArray(new String[unactionablePackages.size()]);
     }
 
     private boolean canSuspendPackageForUserInternal(String packageName, int userId) {
-        if (isPackageDeviceAdmin(packageName, userId)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": has an active device admin");
-            return false;
-        }
-
-        String activeLauncherPackageName = getActiveLauncherPackageName(userId);
-        if (packageName.equals(activeLauncherPackageName)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": contains the active launcher");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredInstallerPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for package installation");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredUninstallerPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for package uninstallation");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredVerifierPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for package verification");
-            return false;
-        }
-
-        if (packageName.equals(getDefaultDialerPackageName(userId))) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": is the default dialer");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredPermissionControllerPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for permissions management");
-            return false;
-        }
-
-        synchronized (mPackages) {
-            if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            if (isPackageDeviceAdmin(packageName, userId)) {
                 Slog.w(TAG, "Cannot suspend package \"" + packageName
-                        + "\": protected package");
+                        + "\": has an active device admin");
                 return false;
             }
 
-            // Cannot suspend static shared libs as they are considered
-            // a part of the using app (emulating static linking). Also
-            // static libs are installed always on internal storage.
-            PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
-                Slog.w(TAG, "Cannot suspend package: " + packageName
-                        + " providing static shared library: "
-                        + pkg.staticSharedLibName);
+            String activeLauncherPackageName = getActiveLauncherPackageName(userId);
+            if (packageName.equals(activeLauncherPackageName)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": contains the active launcher");
                 return false;
             }
-        }
 
-        if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
-            Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
-            return false;
-        }
+            if (packageName.equals(mRequiredInstallerPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for package installation");
+                return false;
+            }
 
-        return true;
+            if (packageName.equals(mRequiredUninstallerPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for package uninstallation");
+                return false;
+            }
+
+            if (packageName.equals(mRequiredVerifierPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for package verification");
+                return false;
+            }
+
+            if (packageName.equals(getDefaultDialerPackageName(userId))) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": is the default dialer");
+                return false;
+            }
+
+            if (packageName.equals(mRequiredPermissionControllerPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for permissions management");
+                return false;
+            }
+
+            synchronized (mPackages) {
+                if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": protected package");
+                    return false;
+                }
+
+                // Cannot suspend static shared libs as they are considered
+                // a part of the using app (emulating static linking). Also
+                // static libs are installed always on internal storage.
+                PackageParser.Package pkg = mPackages.get(packageName);
+                if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
+                    Slog.w(TAG, "Cannot suspend package: " + packageName
+                            + " providing static shared library: "
+                            + pkg.staticSharedLibName);
+                    return false;
+                }
+            }
+
+            if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+                Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
+                return false;
+            }
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
     }
 
     private String getActiveLauncherPackageName(int userId) {
@@ -13559,7 +13647,8 @@
             }
 
             Signature[] callerSignature;
-            Object obj = mSettings.getSettingLPr(callingUid);
+            final int appId = UserHandle.getAppId(callingUid);
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     callerSignature =
@@ -13668,8 +13757,8 @@
                 }
             }
             for (InstallRequest request : installRequests) {
-                resolvePackageInstalledInfo(request.args,
-                        request.installResult);
+                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
+                        new PostInstallData(request.args, request.installResult));
             }
         });
     }
@@ -13684,7 +13773,14 @@
         return res;
     }
 
-    private void resolvePackageInstalledInfo(InstallArgs args, PackageInstalledInfo res) {
+    /** @param data Post-install is performed only if this is non-null. */
+    private void restoreAndPostInstall(
+            int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
+        if (DEBUG_INSTALL) {
+            Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package="
+                    + res.pkg.packageName);
+        }
+
         // A restore should be performed at this point if (a) the install
         // succeeded, (b) the operation is not an update, and (c) the new
         // package has not opted out of backup participation.
@@ -13700,9 +13796,12 @@
         int token;
         if (mNextInstallToken < 0) mNextInstallToken = 1;
         token = mNextInstallToken++;
+        if (data != null) {
+            mRunningInstalls.put(token, data);
+        } else if (DEBUG_INSTALL) {
+            Log.v(TAG, "No post-install required for " + token);
+        }
 
-        PostInstallData data = new PostInstallData(args, res);
-        mRunningInstalls.put(token, data);
         if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
 
         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
@@ -13713,14 +13812,19 @@
             IBackupManager bm = IBackupManager.Stub.asInterface(
                     ServiceManager.getService(Context.BACKUP_SERVICE));
             if (bm != null) {
+                // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
+                // in the BackupManager. USER_ALL is used in compatibility tests.
+                if (userId == UserHandle.USER_ALL) {
+                    userId = UserHandle.USER_SYSTEM;
+                }
                 if (DEBUG_INSTALL) {
-                    Log.v(TAG, "token " + token + " to BM for possible restore");
+                    Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
                 }
                 Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
                 try {
-                    // TODO: http://b/22388012
-                    if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
-                        bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
+                    if (bm.isBackupServiceActive(userId)) {
+                        bm.restoreAtInstallForUser(
+                                userId, res.pkg.applicationInfo.packageName, token);
                     } else {
                         doRestore = false;
                     }
@@ -15615,13 +15719,25 @@
         if (!hasDynamicLibraries) {
             return null;
         }
+        final boolean isUpdatedSystemApp = pkg.isUpdatedSystemApp();
+        // We may not yet have disabled the updated package yet, so be sure to grab the
+        // current setting if that's the case.
+        final PackageSetting updatedSystemPs = isUpdatedSystemApp
+                ? scanResult.request.disabledPkgSetting == null
+                        ? scanResult.request.oldPkgSetting
+                        : scanResult.request.disabledPkgSetting
+                : null;
+        if (isUpdatedSystemApp && (updatedSystemPs.pkg == null
+                || updatedSystemPs.pkg.libraryNames == null)) {
+            Slog.w(TAG, "Package " + pkg.packageName + " declares libraries that are not "
+                    + "declared on the system image; skipping");
+            return null;
+        }
         final ArrayList<SharedLibraryInfo> infos =
                 new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size());
-        final boolean updatedSystemApp = pkg.isUpdatedSystemApp();
         for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) {
-            String name = info.getName();
-            boolean allowed = false;
-            if (updatedSystemApp) {
+            final String name = info.getName();
+            if (isUpdatedSystemApp) {
                 // New library entries can only be added through the
                 // system image.  This is important to get rid of a lot
                 // of nasty edge cases: for example if we allowed a non-
@@ -15631,36 +15747,20 @@
                 // have allowed apps on the device which aren't compatible
                 // with it.  Better to just have the restriction here, be
                 // conservative, and create many fewer cases that can negatively
-                // impact the user experience. We may not yet have disabled the
-                // updated package yet, so be sure to grab the current setting if
-                // that's the case.
-                final PackageSetting sysPs = scanResult.request.disabledPkgSetting == null
-                        ? scanResult.request.oldPkgSetting
-                        : scanResult.request.disabledPkgSetting;
-                if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
-                    for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
-                        if (name.equals(sysPs.pkg.libraryNames.get(j))) {
-                            allowed = true;
-                            break;
-                        }
-                    }
-                }
-            } else {
-                allowed = true;
-            }
-            if (allowed) {
-                if (sharedLibExists(
-                        name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
-                    Slog.w(TAG, "Package " + pkg.packageName + " library "
-                            + name + " already exists; skipping");
+                // impact the user experience.
+                if (!updatedSystemPs.pkg.libraryNames.contains(name)) {
+                    Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+                            + " that is not declared on system image; skipping");
                     continue;
                 }
-                infos.add(info);
-            } else {
-                Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
-                        + name + " that is not declared on system image; skipping");
+            }
+            if (sharedLibExists(
+                    name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
+                Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+                        + " that already exists; skipping");
                 continue;
             }
+            infos.add(info);
         }
         return infos;
     }
@@ -16863,10 +16963,11 @@
      */
     private void setUpFsVerityIfPossible(PackageParser.Package pkg) throws InstallerException,
             PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
-        if (!PackageManagerServiceUtils.isApkVerityEnabled()) {
+        final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+        final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+        if (!standardMode && !legacyMode) {
             return;
         }
-        final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityMode();
 
         // Collect files we care for fs-verity setup.
         ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
@@ -16910,19 +17011,29 @@
             final String filePath = entry.getKey();
             final String signaturePath = entry.getValue();
 
-            final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(
-                    filePath, signaturePath, legacyMode);
+            if (!legacyMode) {
+                // fs-verity is optional for now.  Only set up if signature is provided.
+                if (new File(signaturePath).exists()) {
+                    try {
+                        VerityUtils.setUpFsverity(filePath, signaturePath);
+                    } catch (IOException | DigestException | NoSuchAlgorithmException
+                            | SecurityException e) {
+                        throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+                                "Failed to enable fs-verity: " + e);
+                    }
+                }
+                continue;
+            }
+
+            // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
+            final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);
             if (result.isOk()) {
                 if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
                 final FileDescriptor fd = result.getUnownedFileDescriptor();
                 try {
                     mInstaller.installApkVerity(filePath, fd, result.getContentSize());
-
-                    // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
-                    if (legacyMode) {
-                        final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
-                        mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
-                    }
+                    final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
+                    mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
                 } finally {
                     IoUtils.closeQuietly(fd);
                 }
@@ -18414,6 +18525,7 @@
                     true /*stopped*/,
                     true /*notLaunched*/,
                     false /*hidden*/,
+                    0 /*distractionFlags*/,
                     false /*suspended*/,
                     null /*suspendingPackage*/,
                     null /*dialogInfo*/,
@@ -18871,7 +18983,8 @@
 
     @GuardedBy("mPackages")
     private int getUidTargetSdkVersionLockedLPr(int uid) {
-        Object obj = mSettings.getSettingLPr(uid);
+        final int appId = UserHandle.getAppId(uid);
+        final Object obj = mSettings.getSettingLPr(appId);
         if (obj instanceof SharedUserSetting) {
             final SharedUserSetting sus = (SharedUserSetting) obj;
             int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -19062,7 +19175,7 @@
         // writer
         synchronized (mPackages) {
             PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg == null || pkg.applicationInfo.uid != callingUid) {
+            if (pkg == null || !isCallerSameApp(packageName, callingUid)) {
                 if (mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                         != PackageManager.PERMISSION_GRANTED) {
@@ -19503,28 +19616,32 @@
             throw new SecurityException("Only the system may call getPermissionGrantBackup()");
         }
 
-        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-        try {
-            final XmlSerializer serializer = new FastXmlSerializer();
-            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_PERMISSION_BACKUP);
+        AtomicReference<byte[]> backup = new AtomicReference<>();
+        mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup(
+                UserHandle.of(userId), mContext.getMainExecutor(), (b) -> {
+                    synchronized (backup) {
+                        backup.set(b);
+                        backup.notifyAll();
+                    }
+                });
 
-            synchronized (mPackages) {
-                serializeRuntimePermissionGrantsLPr(serializer, userId);
-            }
+        long start = System.currentTimeMillis();
+        synchronized (backup) {
+            while (backup.get() == null) {
+                long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis();
+                if (timeLeft <= 0) {
+                    return null;
+                }
 
-            serializer.endTag(null, TAG_PERMISSION_BACKUP);
-            serializer.endDocument();
-            serializer.flush();
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Unable to write default apps for backup", e);
+                try {
+                    backup.wait(timeLeft);
+                } catch (InterruptedException ignored) {
+                    return null;
+                }
             }
-            return null;
         }
 
-        return dataStream.toByteArray();
+        return backup.get();
     }
 
     @Override
@@ -19550,66 +19667,6 @@
     }
 
     @GuardedBy("mPackages")
-    private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
-            throws IOException {
-        serializer.startTag(null, TAG_ALL_GRANTS);
-
-        final int N = mSettings.mPackages.size();
-        for (int i = 0; i < N; i++) {
-            final PackageSetting ps = mSettings.mPackages.valueAt(i);
-            boolean pkgGrantsKnown = false;
-
-            PermissionsState packagePerms = ps.getPermissionsState();
-
-            for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) {
-                final int grantFlags = state.getFlags();
-                // only look at grants that are not system/policy fixed
-                if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) {
-                    final boolean isGranted = state.isGranted();
-                    // And only back up the user-twiddled state bits
-                    if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) {
-                        final String packageName = mSettings.mPackages.keyAt(i);
-                        if (!pkgGrantsKnown) {
-                            serializer.startTag(null, TAG_GRANT);
-                            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
-                            pkgGrantsKnown = true;
-                        }
-
-                        final boolean userSet =
-                                (grantFlags & FLAG_PERMISSION_USER_SET) != 0;
-                        final boolean userFixed =
-                                (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0;
-                        final boolean revoke =
-                                (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
-
-                        serializer.startTag(null, TAG_PERMISSION);
-                        serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName());
-                        if (isGranted) {
-                            serializer.attribute(null, ATTR_IS_GRANTED, "true");
-                        }
-                        if (userSet) {
-                            serializer.attribute(null, ATTR_USER_SET, "true");
-                        }
-                        if (userFixed) {
-                            serializer.attribute(null, ATTR_USER_FIXED, "true");
-                        }
-                        if (revoke) {
-                            serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
-                        }
-                        serializer.endTag(null, TAG_PERMISSION);
-                    }
-                }
-            }
-
-            if (pkgGrantsKnown) {
-                serializer.endTag(null, TAG_GRANT);
-            }
-        }
-
-        serializer.endTag(null, TAG_ALL_GRANTS);
-    }
-
-    @GuardedBy("mPackages")
     private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
             throws XmlPullParserException, IOException {
         String pkgName = null;
@@ -19791,6 +19848,14 @@
                         .setPackage(launcherComponent.getPackageName());
                 mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
             }
+            // TODO(b/122900055) Change/Remove this and replace with new permission role.
+            if (mAppPredictionServicePackage != null) {
+                Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
+                        .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
+                        .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+                        .setPackage(mAppPredictionServicePackage);
+                mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
+            }
         }
     }
 
@@ -19943,6 +20008,20 @@
         return mContext.getString(R.string.config_defaultWellbeingPackage);
     }
 
+    private String getAppPredictionServicePackageName() {
+        String flattenedAppPredictionServiceComponentName =
+                mContext.getString(R.string.config_defaultAppPredictionService);
+        if (flattenedAppPredictionServiceComponentName == null) {
+            return null;
+        }
+        ComponentName appPredictionServiceComponentName =
+                ComponentName.unflattenFromString(flattenedAppPredictionServiceComponentName);
+        if (appPredictionServiceComponentName == null) {
+            return null;
+        }
+        return appPredictionServiceComponentName.getPackageName();
+    }
+
     @Override
     public void setApplicationEnabledSetting(String appPackageName,
             int newState, int flags, int userId, String callingPackage) {
@@ -23105,6 +23184,8 @@
                     return mWellbeingPackage;
                 case PackageManagerInternal.PACKAGE_DOCUMENTER:
                     return mDocumenterPackage;
+                case PackageManagerInternal.PACKAGE_CONFIGURATOR:
+                    return mConfiguratorPackage;
             }
             return null;
         }
@@ -23121,6 +23202,11 @@
         }
 
         @Override
+        public void setLocationExtraPackagesProvider(PackagesProvider provider) {
+            mDefaultPermissionPolicy.setLocationExtraPackagesProvider(provider);
+        }
+
+        @Override
         public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
             mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider);
         }
@@ -23229,6 +23315,14 @@
         }
 
         @Override
+        public int getDistractingPackageRestrictions(String packageName, int userId) {
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.mPackages.get(packageName);
+                return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE;
+            }
+        }
+
+        @Override
         public int getPackageUid(String packageName, int flags, int userId) {
             return PackageManagerService.this
                     .getPackageUid(packageName, flags, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 25169a2..6134d30 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -555,19 +555,19 @@
     /** Standard fs-verity. */
     private static final int FSVERITY_ENABLED = 2;
 
-    /** Returns true if APK Verity is enabled. */
+    /** Returns true if standard APK Verity is enabled. */
     static boolean isApkVerityEnabled() {
-        int mode = SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED);
-        return mode == FSVERITY_LEGACY || mode == FSVERITY_ENABLED;
+        return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
     }
 
-    static boolean isLegacyApkVerityMode() {
+    static boolean isLegacyApkVerityEnabled() {
         return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
     }
 
     /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
     static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
-        return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled();
+        return disabledPs != null && disabledPs.isPrivileged() && (
+                isApkVerityEnabled() || isLegacyApkVerityEnabled());
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2e9d26a..c9d298c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2293,9 +2293,7 @@
                     break;
                 case "--apex":
                     sessionParams.installFlags |= PackageManager.INSTALL_APEX;
-                    // TODO(b/118865310): APEX packages should always imply
-                    //                    sessionParams.isStaged(). Enforce this when the staged
-                    //                    install workflow is complete.
+                    sessionParams.setStaged();
                     break;
                 case "--multi-package":
                     sessionParams.setMultiPackage();
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 3c22f07..58f262c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -392,6 +392,14 @@
         modifyUserState(userId).hidden = hidden;
     }
 
+    int getDistractionFlags(int userId) {
+        return readUserState(userId).distractionFlags;
+    }
+
+    void setDistractionFlags(int distractionFlags, int userId) {
+        modifyUserState(userId).distractionFlags = distractionFlags;
+    }
+
     boolean getSuspended(int userId) {
         return readUserState(userId).suspended;
     }
@@ -423,7 +431,8 @@
     }
 
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
-            boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
+            boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
+            String suspendingPackage,
             SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
             PersistableBundle suspendedLauncherExtras, boolean instantApp,
             boolean virtualPreload, String lastDisableAppCaller,
@@ -437,6 +446,7 @@
         state.stopped = stopped;
         state.notLaunched = notLaunched;
         state.hidden = hidden;
+        state.distractionFlags = distractionFlags;
         state.suspended = suspended;
         state.suspendingPackage = suspendingPackage;
         state.dialogInfo = dialogInfo;
@@ -607,6 +617,7 @@
             }
             proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
             proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
+            proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags);
             proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
             if (state.suspended) {
                 proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b47d966..b4154c7 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,8 +17,8 @@
 package com.android.server.pm;
 
 import android.content.pm.PackageParser;
-import android.content.pm.Signature;
 import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.Signature;
 import android.os.Environment;
 import android.util.Slog;
 import android.util.Xml;
@@ -81,6 +81,13 @@
         sMacPermissions.add(new File(
             Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
 
+        // Product mac permissions (optional).
+        final File productMacPermission = new File(
+                Environment.getProductDirectory(), "/etc/selinux/product_mac_permissions.xml");
+        if (productMacPermission.exists()) {
+            sMacPermissions.add(productMacPermission);
+        }
+
         // Vendor mac permissions.
         // The filename has been renamed from nonplat_mac_permissions to
         // vendor_mac_permissions. Either of them should exist.
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c524dba..b0f2326 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -223,6 +223,7 @@
     private static final String ATTR_BLOCKED = "blocked";
     // New name for the above attribute.
     private static final String ATTR_HIDDEN = "hidden";
+    private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags";
     private static final String ATTR_SUSPENDED = "suspended";
     private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
     /**
@@ -734,6 +735,7 @@
                                 true /*stopped*/,
                                 true /*notLaunched*/,
                                 false /*hidden*/,
+                                0 /*distractionFlags*/,
                                 false /*suspended*/,
                                 null /*suspendingPackage*/,
                                 null /*dialogInfo*/,
@@ -1628,6 +1630,7 @@
                                 false /*stopped*/,
                                 false /*notLaunched*/,
                                 false /*hidden*/,
+                                0 /*distractionFlags*/,
                                 false /*suspended*/,
                                 null /*suspendingPackage*/,
                                 null /*dialogInfo*/,
@@ -1703,6 +1706,8 @@
                     hidden = hiddenStr == null
                             ? hidden : Boolean.parseBoolean(hiddenStr);
 
+                    final int distractionFlags = XmlUtils.readIntAttribute(parser,
+                            ATTR_DISTRACTION_FLAGS, 0);
                     final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
                             false);
                     String suspendingPackage = parser.getAttributeValue(null,
@@ -1781,7 +1786,8 @@
                         setBlockUninstallLPw(userId, name, true);
                     }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
-                            hidden, suspended, suspendingPackage, suspendDialogInfo,
+                            hidden, distractionFlags, suspended, suspendingPackage,
+                            suspendDialogInfo,
                             suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload,
                             enabledCaller, enabledComponents, disabledComponents, verifState,
                             linkGeneration, installReason, harmfulAppWarning);
@@ -2089,6 +2095,10 @@
                 if (ustate.hidden) {
                     serializer.attribute(null, ATTR_HIDDEN, "true");
                 }
+                if (ustate.distractionFlags != 0) {
+                    serializer.attribute(null, ATTR_DISTRACTION_FLAGS,
+                            Integer.toString(ustate.distractionFlags));
+                }
                 if (ustate.suspended) {
                     serializer.attribute(null, ATTR_SUSPENDED, "true");
                     if (ustate.suspendingPackage != null) {
@@ -2780,13 +2790,13 @@
                 // dataPath   - path to package's data path
                 // seinfo     - seinfo label for the app (assigned at install time)
                 // gids       - supplementary gids this app launches with
+                // profileableFromShellFlag  - 0 or 1 if the package is profileable from shell.
                 //
                 // NOTE: We prefer not to expose all ApplicationInfo flags for now.
                 //
                 // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
                 // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
-                //   frameworks/base/libs/packagelistparser
-                //   system/core/run-as/run-as.c
+                //   system/core/libpackagelistparser
                 //
                 sb.setLength(0);
                 sb.append(ai.packageName);
@@ -2806,6 +2816,8 @@
                 } else {
                     sb.append("none");
                 }
+                sb.append(" ");
+                sb.append(ai.isProfileableByShell() ? "1" : "0");
                 sb.append("\n");
                 writer.append(sb);
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 83f0fde..563fd7f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -20,8 +20,10 @@
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.content.res.Resources;
 import android.os.PersistableBundle;
 import android.text.format.Formatter;
@@ -640,6 +642,55 @@
     }
 
     /**
+     * Returns a list of ShortcutInfos that match the given intent filter and the category of
+     * available ShareTarget definitions in this package.
+     */
+    public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
+            @NonNull IntentFilter filter) {
+        final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
+        for (int i = 0; i < mShareTargets.size(); i++) {
+            final ShareTargetInfo target = mShareTargets.get(i);
+            for (ShareTargetInfo.TargetData data : target.mTargetData) {
+                if (filter.hasDataType(data.mMimeType)) {
+                    // Matched at least with one data type
+                    matchedTargets.add(target);
+                    break;
+                }
+            }
+        }
+
+        if (matchedTargets.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // Get the list of all dynamic shortcuts in this package
+        final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        findAll(shortcuts, ShortcutInfo::isDynamicVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+        final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
+        for (int i = 0; i < shortcuts.size(); i++) {
+            final ShortcutInfo si = shortcuts.get(i);
+            for (int j = 0; j < matchedTargets.size(); j++) {
+                // Shortcut must have all of share target categories
+                boolean hasAllCategories = true;
+                final ShareTargetInfo target = matchedTargets.get(j);
+                for (int q = 0; q < target.mCategories.length; q++) {
+                    if (!si.getCategories().contains(target.mCategories[q])) {
+                        hasAllCategories = false;
+                        break;
+                    }
+                }
+                if (hasAllCategories) {
+                    result.add(new ShortcutManager.ShareShortcutInfo(si, new ComponentName(
+                            getPackageName(), target.mTargetClass)));
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
      * Return the filenames (excluding path names) of icon bitmap files from this package.
      */
     public ArraySet<String> getUsedBitmapFiles() {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 2b773f4..fdbaba2 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -46,6 +46,7 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
 import android.content.res.Resources;
@@ -2149,6 +2150,23 @@
         }
     }
 
+    @Override
+    public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName,
+            IntentFilter filter, @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+
+        synchronized (mLock) {
+            throwIfUserLockedL(userId);
+
+            final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
+
+            final ShortcutUser user = getUserShortcutsLocked(userId);
+            user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
+
+            return new ParceledListSlice<>(shortcutInfoList);
+        }
+    }
+
     @GuardedBy("mLock")
     private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
             @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 3beda6a..7bab0bb 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,10 +17,25 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
+import android.apex.ApexInfo;
+import android.apex.ApexInfoList;
+import android.apex.ApexSessionInfo;
+import android.apex.IApexService;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.Signature;
 import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Slog;
 import android.util.SparseArray;
+import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
@@ -39,9 +54,6 @@
     private final PackageManagerService mPm;
     private final Handler mBgHandler;
 
-    // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
-    //           shouldn't be needed at all.
-    // TODO(b/118865310): Implement staged sessions logic.
     @GuardedBy("mStagedSessions")
     private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
 
@@ -71,15 +83,121 @@
         return new ParceledListSlice<>(result);
     }
 
-    void commitSession(@NonNull PackageInstallerSession sessionInfo) {
-        updateStoredSession(sessionInfo);
+    private static boolean validateApexSignatureLocked(String apexPath, String packageName) {
+        final SigningDetails signingDetails;
+        try {
+            signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
+        } catch (PackageParserException e) {
+            Slog.e(TAG, "Unable to parse APEX package: " + apexPath, e);
+            return false;
+        }
 
-        mBgHandler.post(() -> {
-            // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For
-            //                    now we directly mark it as ready.
-            sessionInfo.setStagedSessionReady();
-            mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId);
-        });
+        final IApexService apex = IApexService.Stub.asInterface(
+                ServiceManager.getService("apexservice"));
+        final ApexInfo apexInfo;
+        try {
+            apexInfo = apex.getActivePackage(packageName);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact APEXD", re);
+            return false;
+        }
+
+        if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) {
+            // TODO: What is the right thing to do here ? This implies there's no active package
+            // with the given name. This should never be the case in production (where we only
+            // accept updates to existing APEXes) but may be required for testing.
+            return true;
+        }
+
+        final SigningDetails existingSigningDetails;
+        try {
+            existingSigningDetails = ApkSignatureVerifier.verify(
+                apexInfo.packagePath, SignatureSchemeVersion.JAR);
+        } catch (PackageParserException e) {
+            Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e);
+            return false;
+        }
+
+        // Now that we have both sets of signatures, demand that they're an exact match.
+        if (Signature.areExactMatch(existingSigningDetails.signatures, signingDetails.signatures)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static boolean submitSessionToApexService(int sessionId, ApexInfoList apexInfoList) {
+        final IApexService apex = IApexService.Stub.asInterface(
+                ServiceManager.getService("apexservice"));
+        boolean success;
+        try {
+            success = apex.submitStagedSession(sessionId, new int[0], apexInfoList);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact apexservice", re);
+            return false;
+        }
+        return success;
+    }
+
+    private void preRebootVerification(@NonNull PackageInstallerSession session) {
+        boolean success = true;
+        if ((session.params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+
+            final ApexInfoList apexInfoList = new ApexInfoList();
+
+            if (!submitSessionToApexService(session.sessionId, apexInfoList)) {
+                success = false;
+            } else {
+                // For APEXes, we validate the signature here before we mark the session as ready,
+                // so we fail the session early if there is a signature mismatch. For APKs, the
+                // signature verification will be done by the package manager at the point at which
+                // it applies the staged install.
+                //
+                // TODO: Decide whether we want to fail fast by detecting signature mismatches right
+                // away.
+                for (ApexInfo apexPackage : apexInfoList.apexInfos) {
+                    if (!validateApexSignatureLocked(apexPackage.packagePath,
+                            apexPackage.packageName)) {
+                        success = false;
+                        break;
+                    }
+                }
+            }
+        }
+        if (success) {
+            session.setStagedSessionReady();
+        } else {
+            session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED);
+        }
+    }
+
+    private void resumeSession(@NonNull PackageInstallerSession session) {
+        // Check with apexservice whether the apex
+        // packages have been activated.
+        final IApexService apex = IApexService.Stub.asInterface(
+                ServiceManager.getService("apexservice"));
+        ApexSessionInfo apexSessionInfo;
+        try {
+            apexSessionInfo = apex.getStagedSessionInfo(session.sessionId);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact apexservice", re);
+            // TODO should we retry here? Mark the session as failed?
+            return;
+        }
+        if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
+            session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED);
+        }
+        if (apexSessionInfo.isActivated) {
+            session.setStagedSessionApplied();
+            // TODO(b/118865310) if multi-package proceed with the installation of APKs.
+        }
+        // TODO(b/118865310) if (apexSessionInfo.isVerified) { /* mark this as staged in apexd */ }
+        // In every other case apexd will retry to apply the session at next boot.
+    }
+
+    void commitSession(@NonNull PackageInstallerSession session) {
+        updateStoredSession(session);
+        mBgHandler.post(() -> preRebootVerification(session));
     }
 
     void createSession(@NonNull PackageInstallerSession sessionInfo) {
@@ -94,4 +212,22 @@
             mStagedSessions.remove(sessionInfo.sessionId);
         }
     }
+
+    void restoreSession(@NonNull PackageInstallerSession session) {
+        updateStoredSession(session);
+        // Check the state of the session and decide what to do next.
+        if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) {
+            // Final states, nothing to do.
+            return;
+        }
+        if (!session.isStagedSessionReady()) {
+            // The framework got restarted before the pre-reboot verification could complete,
+            // restart the verification.
+            mBgHandler.post(() -> preRebootVerification(session));
+        } else {
+            // Session had already being marked ready. Start the checks to verify if there is any
+            // follow-up work.
+            mBgHandler.post(() -> resumeSession(session));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index dd04652..aaa1874 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -620,9 +620,6 @@
                         && callingUid != Process.SYSTEM_UID) {
                     return true;
                 } else if (String.valueOf(Settings.Secure.LOCATION_MODE_OFF).equals(value)) {
-                    // Note LOCATION_MODE will be converted into LOCATION_PROVIDERS_ALLOWED
-                    // in android.provider.Settings.Secure.putStringForUser(), so we shouldn't come
-                    // here normally, but we still protect it here from a direct provider write.
                     return false;
                 }
                 restriction = UserManager.DISALLOW_SHARE_LOCATION;
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
index 68a755b..78fa82c 100644
--- a/services/core/java/com/android/server/pm/dex/DexLogger.java
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -28,7 +28,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
@@ -53,21 +52,18 @@
 
     private final IPackageManager mPackageManager;
     private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
-    private final Object mInstallLock;
-    @GuardedBy("mInstallLock")
     private final Installer mInstaller;
 
-    public DexLogger(IPackageManager pms, Installer installer, Object installLock) {
-        this(pms, installer, installLock, new PackageDynamicCodeLoading());
+    public DexLogger(IPackageManager pms, Installer installer) {
+        this(pms, installer, new PackageDynamicCodeLoading());
     }
 
     @VisibleForTesting
-    DexLogger(IPackageManager pms, Installer installer, Object installLock,
+    DexLogger(IPackageManager pms, Installer installer,
             PackageDynamicCodeLoading packageDynamicCodeLoading) {
         mPackageManager = pms;
         mPackageDynamicCodeLoading = packageDynamicCodeLoading;
         mInstaller = installer;
-        mInstallLock = installLock;
     }
 
     public Set<String> getAllPackagesWithDynamicCodeLoading() {
@@ -131,14 +127,16 @@
             }
 
             byte[] hash = null;
-            synchronized (mInstallLock) {
-                try {
-                    hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid,
-                            appInfo.volumeUuid, storageFlags);
-                } catch (InstallerException e) {
-                    Slog.e(TAG, "Got InstallerException when hashing file " + filePath
-                            + ": " + e.getMessage());
-                }
+            try {
+                // Note that we do not take the install lock here. Hashing should never interfere
+                // with app update/compilation/removal. We may get anomalous results if a file
+                // changes while we hash it, but that can happen anyway and is harmless for our
+                // purposes.
+                hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid,
+                        appInfo.volumeUuid, storageFlags);
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Got InstallerException when hashing file " + filePath
+                        + ": " + e.getMessage());
             }
 
             String fileName = new File(filePath).getName();
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index e57d9d7..b546836 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -129,7 +129,7 @@
         mPackageDexOptimizer = pdo;
         mInstaller = installer;
         mInstallLock = installLock;
-        mDexLogger = new DexLogger(pms, installer, installLock);
+        mDexLogger = new DexLogger(pms, installer);
     }
 
     public DexLogger getDexLogger() {
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 93ee44c..91ad11e 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -22,7 +22,6 @@
 import android.util.SparseArray;
 
 import com.android.internal.os.ClassLoaderFactory;
-import com.android.server.pm.PackageDexOptimizer;
 
 import java.io.File;
 import java.util.List;
@@ -275,15 +274,11 @@
     /**
      * Encodes a single class loader dependency starting from {@param path} and
      * {@param classLoaderName}.
-     * When classpath is {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns
-     * the same. This special property is used only during OTA.
      * NOTE: Keep this in sync with the dexopt expectations! Right now that is either "PCL[path]"
      * for a PathClassLoader or "DLC[path]" for a DelegateLastClassLoader.
      */
     /*package*/ static String encodeClassLoader(String classpath, String classLoaderName) {
-        if (classpath.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
-            return classpath;
-        }
+        classpath.getClass();  // Throw NPE if classpath is null
         String classLoaderDexoptEncoding = classLoaderName;
         if (ClassLoaderFactory.isPathClassLoaderName(classLoaderName)) {
             classLoaderDexoptEncoding = "PCL";
@@ -306,16 +301,10 @@
     /**
      * Links to dependencies together in a format accepted by dexopt.
      * For the special case when either of cl1 or cl2 equals
-     * {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns the same. This
-     * property is used only during OTA.
      * NOTE: Keep this in sync with the dexopt expectations! Right now that is a list of split
      * dependencies {@see encodeClassLoader} separated by ';'.
      */
     /*package*/ static String encodeClassLoaderChain(String cl1, String cl2) {
-        if (cl1.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK) ||
-                cl2.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
-            return PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
-        }
         if (cl1.isEmpty()) return cl2;
         if (cl2.isEmpty()) return cl1;
         return cl1 + ";" + cl2;
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 3a49412..17f8347 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -247,6 +247,10 @@
     public boolean isDocumenter() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
     }
+    public boolean isConfigurator() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
+            != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
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 789664d..ceaf69d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -225,6 +225,7 @@
     private final Handler mHandler;
 
     private PackagesProvider mLocationPackagesProvider;
+    private PackagesProvider mLocationExtraPackagesProvider;
     private PackagesProvider mVoiceInteractionPackagesProvider;
     private PackagesProvider mSmsAppPackagesProvider;
     private PackagesProvider mDialerAppPackagesProvider;
@@ -270,6 +271,13 @@
         }
     }
 
+    /** Sets the provider for loction extra packages. */
+    public void setLocationExtraPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mLocationExtraPackagesProvider = provider;
+        }
+    }
+
     public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
         synchronized (mLock) {
             mVoiceInteractionPackagesProvider = provider;
@@ -403,6 +411,7 @@
         Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
 
         final PackagesProvider locationPackagesProvider;
+        final PackagesProvider locationExtraPackagesProvider;
         final PackagesProvider voiceInteractionPackagesProvider;
         final PackagesProvider smsAppPackagesProvider;
         final PackagesProvider dialerAppPackagesProvider;
@@ -412,6 +421,7 @@
 
         synchronized (mLock) {
             locationPackagesProvider = mLocationPackagesProvider;
+            locationExtraPackagesProvider = mLocationExtraPackagesProvider;
             voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
             smsAppPackagesProvider = mSmsAppPackagesProvider;
             dialerAppPackagesProvider = mDialerAppPackagesProvider;
@@ -424,6 +434,8 @@
                 ? voiceInteractionPackagesProvider.getPackages(userId) : null;
         String[] locationPackageNames = (locationPackagesProvider != null)
                 ? locationPackagesProvider.getPackages(userId) : null;
+        String[] locationExtraPackageNames = (locationExtraPackagesProvider != null)
+                ? locationExtraPackagesProvider.getPackages(userId) : null;
         String[] smsAppPackageNames = (smsAppPackagesProvider != null)
                 ? smsAppPackagesProvider.getPackages(userId) : null;
         String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
@@ -638,6 +650,12 @@
                         LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
             }
         }
+        if (locationExtraPackageNames != null) {
+            // Also grant location permission to location extra packages.
+            for (String packageName : locationExtraPackageNames) {
+                grantPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS);
+            }
+        }
 
         // Music
         Intent musicIntent = new Intent(Intent.ACTION_VIEW)
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 93964cb..30b5e49 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1640,6 +1640,13 @@
                 // Special permissions for the system default text classifier.
                 allowed = true;
             }
+            if (!allowed && bp.isConfigurator()
+                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    PackageManagerInternal.PACKAGE_CONFIGURATOR,
+                    UserHandle.USER_SYSTEM))) {
+                // Special permissions for the device configurator.
+                allowed = true;
+            }
             if (!allowed && bp.isWellbeing()
                     && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
                     PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c792863..41cab2d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -24,6 +24,7 @@
 import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Context.DISPLAY_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -83,8 +84,10 @@
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs
+        .CAMERA_LENS_COVER_ABSENT;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs
+        .CAMERA_LENS_UNCOVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
@@ -126,6 +129,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
+import android.hardware.hdmi.HdmiAudioSystemClient;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
@@ -195,6 +199,7 @@
 import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.os.RoSystemProperties;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.PhoneWindow;
@@ -373,6 +378,7 @@
     private ScreenshotHelper mScreenshotHelper;
     private boolean mHasFeatureWatch;
     private boolean mHasFeatureLeanback;
+    private boolean mHasFeatureHdmiCec;
 
     // Assigned on main thread, accessed on UI thread
     volatile VrManagerInternal mVrManagerInternal;
@@ -474,6 +480,7 @@
     int mShortPressOnSleepBehavior;
     int mShortPressOnWindowBehavior;
     boolean mHasSoftInput = false;
+    boolean mHapticTextHandleEnabled;
     boolean mUseTvRouting;
     int mVeryLongPressTimeout;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
@@ -561,6 +568,10 @@
     private boolean mScreenshotChordPowerKeyTriggered;
     private long mScreenshotChordPowerKeyTime;
 
+    private static final long MOVING_DISPLAY_TO_TOP_DURATION_MILLIS = 10;
+    private volatile boolean mMovingDisplayToTopKeyTriggered;
+    private volatile long mMovingDisplayToTopKeyTime;
+
     // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
     private int mRingerToggleChord = VOLUME_HUSH_OFF;
 
@@ -600,7 +611,7 @@
     private boolean mAodShowing;
 
     private boolean mPerDisplayFocusEnabled = false;
-    private int mTopFocusedDisplayId = INVALID_DISPLAY;
+    private volatile int mTopFocusedDisplayId = INVALID_DISPLAY;
 
     private static final int MSG_ENABLE_POINTER_LOCATION = 1;
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
@@ -628,6 +639,7 @@
     private static final int MSG_POWER_VERY_LONG_PRESS = 25;
     private static final int MSG_NOTIFY_USER_ACTIVITY = 26;
     private static final int MSG_RINGER_TOGGLE_CHORD = 27;
+    private static final int MSG_MOVE_DISPLAY_TO_TOP = 28;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -723,6 +735,10 @@
                 case MSG_RINGER_TOGGLE_CHORD:
                     handleRingerChordGesture();
                     break;
+                case MSG_MOVE_DISPLAY_TO_TOP:
+                    mWindowManagerFuncs.moveDisplayToTop(msg.arg1);
+                    mMovingDisplayToTopKeyTriggered = false;
+                    break;
             }
         }
     }
@@ -804,6 +820,13 @@
         }
     };
 
+    private Runnable mPossibleVeryLongPressReboot = new Runnable() {
+        @Override
+        public void run() {
+            mActivityManagerInternal.prepareForPossibleShutdown();
+        }
+    };
+
     private void handleRingerChordGesture() {
         if (mRingerToggleChord == VOLUME_HUSH_OFF) {
             return;
@@ -949,6 +972,8 @@
         // Inform the StatusBar; but do not allow it to consume the event.
         sendSystemKeyToStatusBarAsync(event.getKeyCode());
 
+        schedulePossibleVeryLongPressReboot();
+
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
         mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
@@ -1052,6 +1077,7 @@
         if (hasVeryLongPressOnPowerBehavior()) {
             mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS);
         }
+        cancelPossibleVeryLongPressReboot();
     }
 
     private void cancelPendingBackKeyAction() {
@@ -1453,7 +1479,7 @@
      */
     private HdmiControl getHdmiControl() {
         if (null == mHdmiControl) {
-            if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
+            if (!mHasFeatureHdmiCec) {
                 return null;
             }
             HdmiControlManager manager = (HdmiControlManager) mContext.getSystemService(
@@ -1689,6 +1715,7 @@
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
         mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
+        mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
         mAccessibilityShortcutController =
                 new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
         mLogger = new MetricsLogger();
@@ -1810,6 +1837,9 @@
         mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
 
+        mHapticTextHandleEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enableHapticTextHandle);
+
         mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
 
         mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
@@ -2265,9 +2295,8 @@
         }
 
         final LayoutParams attrs = win.getAttrs();
-        final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw() &&
-                ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
-                        || !canBeHiddenByKeyguardLw(imeTarget));
+        final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw()
+                && (imeTarget.canShowWhenLocked() || !canBeHiddenByKeyguardLw(imeTarget));
 
         // Show IME over the keyguard if the target allows it
         boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this)
@@ -2275,7 +2304,7 @@
 
         if (isKeyguardLocked() && isKeyguardOccluded()) {
             // Show SHOW_WHEN_LOCKED windows if Keyguard is occluded.
-            allowWhenLocked |= (attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
+            allowWhenLocked |= win.canShowWhenLocked()
                     // Show error dialogs over apps that are shown on lockscreen
                     || (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
         }
@@ -2553,12 +2582,25 @@
         final int eventDisplayId = event.getDisplayId();
         if (result == 0 && !mPerDisplayFocusEnabled
                 && eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) {
-            // Someone tries to send a key event to a display which doesn't have a focused window.
-            // We drop the event here, or it will cause ANR.
-            // TODO (b/121057974): The user may be confused about why the key doesn't work, so we
-            // may need to deal with this problem.
-            Slog.i(TAG, "Dropping this event targeting display #" + eventDisplayId
-                    + " because the focus is on display #" + mTopFocusedDisplayId);
+            // An event is targeting a non-focused display. Try to move the display to top so that
+            // it can become the focused display to interact with the user.
+            final long eventDownTime = event.getDownTime();
+            if (mMovingDisplayToTopKeyTime < eventDownTime) {
+                // We have not handled this event yet. Move the display to top, and then tell
+                // dispatcher to try again later.
+                mMovingDisplayToTopKeyTime = eventDownTime;
+                mMovingDisplayToTopKeyTriggered = true;
+                mHandler.sendMessage(
+                        mHandler.obtainMessage(MSG_MOVE_DISPLAY_TO_TOP, eventDisplayId, 0));
+                return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS;
+            } else if (mMovingDisplayToTopKeyTriggered) {
+                // The message has not been handled yet. Tell dispatcher to try again later.
+                return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS;
+            }
+            // The target display is still not the top focused display. Drop the event because the
+            // display may not contain any window which can receive keys.
+            Slog.w(TAG, "Dropping key targeting non-focused display #" + eventDisplayId
+                    + " keyCode=" + KeyEvent.keyCodeToString(event.getKeyCode()));
             return -1;
         }
         return result;
@@ -4116,6 +4158,19 @@
     }
 
     private void dispatchDirectAudioEvent(KeyEvent event) {
+        // When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
+        // or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
+        HdmiControlManager hdmiControlManager = getHdmiControlManager();
+        if (null != hdmiControlManager
+                && !hdmiControlManager.getSystemAudioMode()
+                && shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
+            HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
+            if (audioSystemClient != null) {
+                audioSystemClient.sendKeyEvent(
+                        event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
+                return;
+            }
+        }
         if (event.getAction() != KeyEvent.ACTION_DOWN) {
             return;
         }
@@ -4123,6 +4178,7 @@
         int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
                 | AudioManager.FLAG_FROM_KEY;
         String pkgName = mContext.getOpPackageName();
+
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
                 try {
@@ -4154,6 +4210,18 @@
         }
     }
 
+    @Nullable
+    private HdmiControlManager getHdmiControlManager() {
+        if (!mHasFeatureHdmiCec) {
+            return null;
+        }
+        return (HdmiControlManager) mContext.getSystemService(HdmiControlManager.class);
+    }
+
+    private boolean shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff() {
+        return RoSystemProperties.CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF;
+    }
+
     void dispatchMediaKeyWithWakeLock(KeyEvent event) {
         if (DEBUG_INPUT) {
             Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event);
@@ -4870,6 +4938,15 @@
         }
     }
 
+    private void schedulePossibleVeryLongPressReboot() {
+        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
+        mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout);
+    }
+
+    private void cancelPossibleVeryLongPressReboot() {
+        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
+    }
+
     // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
     private void updateScreenOffSleepToken(boolean acquire) {
         if (acquire) {
@@ -5127,8 +5204,11 @@
             case HapticFeedbackConstants.CLOCK_TICK:
             case HapticFeedbackConstants.CONTEXT_CLICK:
                 return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
-            case HapticFeedbackConstants.KEYBOARD_RELEASE:
             case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+                if (!mHapticTextHandleEnabled) {
+                    return null;
+                }
+            case HapticFeedbackConstants.KEYBOARD_RELEASE:
             case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
             case HapticFeedbackConstants.ENTRY_BUMP:
             case HapticFeedbackConstants.DRAG_CROSSING:
@@ -5293,11 +5373,13 @@
                 pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
         pw.print(prefix);
                 pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
-                pw.print(" mDismissImeOnBackKeyPressed="); pw.println(mDismissImeOnBackKeyPressed);
+                pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
         pw.print(prefix);
-                pw.print("mIncallPowerBehavior=");
-                pw.print(incallPowerBehaviorToString(mIncallPowerBehavior));
-                pw.print(" mIncallBackBehavior=");
+                pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
+                pw.print(" mIncallPowerBehavior=");
+                pw.println(incallPowerBehaviorToString(mIncallPowerBehavior));
+        pw.print(prefix);
+                pw.print("mIncallBackBehavior=");
                 pw.print(incallBackBehaviorToString(mIncallBackBehavior));
                 pw.print(" mEndcallBehavior=");
                 pw.println(endcallBehaviorToString(mEndcallBehavior));
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index e212b04..437ef73 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -10,7 +10,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3da325c..e1a911e 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -468,6 +468,9 @@
         /** @return true if this window desires key events. */
         boolean canReceiveKeys();
 
+        /** @return true if the window can show over keyguard. */
+        boolean canShowWhenLocked();
+
         /**
          * Writes {@link com.android.server.wm.IdentifierProto} to stream.
          */
@@ -634,6 +637,12 @@
          * Notifies window manager that user is switched.
          */
         void onUserSwitched();
+
+        /**
+         * Hint to window manager that the user is interacting with a display that should be treated
+         * as the top display.
+         */
+        void moveDisplayToTop(int displayId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
new file mode 100644
index 0000000..055c941
--- /dev/null
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -0,0 +1,99 @@
+/*
+ * 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.policy.role;
+
+import android.annotation.NonNull;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.Debug;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.telephony.SmsApplication;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.role.RoleManagerService;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Logic to retrieve the various legacy(pre-Q) equivalents of role holders.
+ *
+ * Unlike {@link RoleManagerService} this is meant to be pretty high-level to allow for depending
+ * on all kinds of various systems that are historically involved in legacy role resolution,
+ * e.g. {@link SmsApplication}
+ *
+ * @see RoleManagerService#migrateRoleIfNecessary
+ */
+public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHoldersResolver {
+
+    private static final boolean DEBUG = false;
+    private static final String LOG_TAG = "LegacyRoleResolutionPol";
+
+    @NonNull
+    private final Context mContext;
+
+    public LegacyRoleResolutionPolicy(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public List<String> getRoleHolders(String roleName, int userId) {
+        switch (roleName) {
+            case RoleManager.ROLE_SMS: {
+                // Moved over from SmsApplication#getApplication
+                String result = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
+
+                // TODO: STOPSHIP: Remove the following code once we remove default_sms_application
+                // and use the new config_defaultRoleHolders.
+                if (result == null) {
+                    Collection<SmsApplication.SmsApplicationData> applications =
+                            SmsApplication.getApplicationCollectionAsUser(mContext, userId);
+                    SmsApplication.SmsApplicationData applicationData;
+                    String defaultPackage = mContext.getResources()
+                            .getString(com.android.internal.R.string.default_sms_application);
+                    applicationData =
+                            SmsApplication.getApplicationForPackage(applications, defaultPackage);
+
+                    if (applicationData == null) {
+                        // Are there any applications?
+                        if (applications.size() != 0) {
+                            applicationData =
+                                    (SmsApplication.SmsApplicationData) applications.toArray()[0];
+                        }
+                    }
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Found default sms app: " + applicationData
+                                + " among: " + applications + " from " + Debug.getCallers(4));
+                    }
+                    SmsApplication.SmsApplicationData app = applicationData;
+                    result = app == null ? null : app.mPackageName;
+                }
+
+                return CollectionUtils.singletonOrEmpty(result);
+            }
+            default: {
+                Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName);
+                return Collections.emptyList();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5adc248..c3f20aa 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -755,9 +755,10 @@
     };
 
     /**
-     * Plays the wireless charging sound for both wireless and non-wireless charging
+     * If enabled, plays a sound and/or vibration when wireless or non-wireless charging has started
      */
-    private void playChargingStartedSound(@UserIdInt int userId) {
+    private void playChargingStartedFeedback(@UserIdInt int userId) {
+        playChargingStartedVibration(userId);
         final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.CHARGING_STARTED_SOUND);
         if (isChargingFeedbackEnabled(userId) && soundPath != null) {
@@ -773,8 +774,7 @@
     }
 
     private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
-        playWirelessChargingVibration(userId);
-        playChargingStartedSound(userId);
+        playChargingStartedFeedback(userId);
         if (mStatusBarManagerInternal != null) {
             mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
         }
@@ -782,7 +782,7 @@
     }
 
     private void showWiredChargingStarted(@UserIdInt int userId) {
-        playChargingStartedSound(userId);
+        playChargingStartedFeedback(userId);
         mSuspendBlocker.release();
     }
 
@@ -790,7 +790,7 @@
         mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
     }
 
-    private void playWirelessChargingVibration(@UserIdInt int userId) {
+    private void playChargingStartedVibration(@UserIdInt int userId) {
         final boolean vibrateEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
         if (vibrateEnabled && isChargingFeedbackEnabled(userId)) {
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index 244ccb6..5cbe74c 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -1,6 +1,4 @@
 michaelwr@google.com
 santoscordon@google.com
 
-per-file BatterySaverPolicy.java=omakoto@google.com
-per-file ShutdownThread.java=fkupolov@google.com
-per-file ThermalManagerService.java=wvw@google.com
\ No newline at end of file
+per-file ThermalManagerService.java=wvw@google.com
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 565bb706..a027873 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -96,6 +96,7 @@
 import com.android.server.lights.LightsManager;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.server.power.batterysaver.BatterySaverPolicy;
 import com.android.server.power.batterysaver.BatterySaverStateMachine;
 import com.android.server.power.batterysaver.BatterySavingStats;
 
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 02689a9..7d03d82 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -134,10 +134,14 @@
             if (!halConnected) {
                 mHalWrapper = new ThermalHal20Wrapper();
                 halConnected = mHalWrapper.connectToHal();
-                if (!halConnected) {
-                    mHalWrapper = new ThermalHal11Wrapper();
-                    halConnected = mHalWrapper.connectToHal();
-                }
+            }
+            if (!halConnected) {
+                mHalWrapper = new ThermalHal11Wrapper();
+                halConnected = mHalWrapper.connectToHal();
+            }
+            if (!halConnected) {
+                mHalWrapper = new ThermalHal10Wrapper();
+                halConnected = mHalWrapper.connectToHal();
             }
             mHalWrapper.setCallback(this::onTemperatureChangedCallback);
             if (!halConnected) {
@@ -616,6 +620,81 @@
         }
     }
 
+
+    static class ThermalHal10Wrapper extends ThermalHalWrapper {
+        /** Proxy object for the Thermal HAL 1.0 service. */
+        @GuardedBy("mHalLock")
+        private android.hardware.thermal.V1_0.IThermal mThermalHal10 = null;
+
+        @Override
+        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+                int type) {
+            synchronized (mHalLock) {
+                List<Temperature> ret = new ArrayList<>();
+                if (mThermalHal10 == null) {
+                    return ret;
+                }
+                try {
+                    mThermalHal10.getTemperatures(
+                            (ThermalStatus status,
+                                    ArrayList<android.hardware.thermal.V1_0.Temperature>
+                                            temperatures) -> {
+                                if (ThermalStatusCode.SUCCESS == status.code) {
+                                    for (android.hardware.thermal.V1_0.Temperature
+                                            temperature : temperatures) {
+                                        if (shouldFilter && type != temperature.type) {
+                                            continue;
+                                        }
+                                        // Thermal HAL 1.0 doesn't report current throttling status
+                                        ret.add(new Temperature(
+                                                temperature.currentValue, temperature.type,
+                                                temperature.name,
+                                                Temperature.THROTTLING_NONE));
+                                    }
+                                } else {
+                                    Slog.e(TAG,
+                                            "Couldn't get temperatures because of HAL error: "
+                                                    + status.debugMessage);
+                                }
+
+                            });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+                    connectToHal();
+                }
+                return ret;
+            }
+        }
+
+        @Override
+        protected boolean connectToHal() {
+            synchronized (mHalLock) {
+                try {
+                    mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService();
+                    mThermalHal10.linkToDeath(new DeathRecipient(),
+                            THERMAL_HAL_DEATH_COOKIE);
+                    Slog.i(TAG,
+                            "Thermal HAL 1.0 service connected, no thermal call back will be "
+                                    + "called due to legacy API.");
+                } catch (NoSuchElementException | RemoteException e) {
+                    Slog.e(TAG,
+                            "Thermal HAL 1.0 service not connected.");
+                    mThermalHal10 = null;
+                }
+                return (mThermalHal10 != null);
+            }
+        }
+
+        @Override
+        protected void dump(PrintWriter pw, String prefix) {
+            synchronized (mHalLock) {
+                pw.print(prefix);
+                pw.println("ThermalHAL 1.0 connected: " + (mThermalHal10 != null ? "yes"
+                        : "no"));
+            }
+        }
+    }
+
     static class ThermalHal11Wrapper extends ThermalHalWrapper {
         /** Proxy object for the Thermal HAL 1.1 service. */
         @GuardedBy("mHalLock")
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 6400c88..1d74350 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -35,13 +35,13 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
-import com.android.server.power.BatterySaverPolicy;
-import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener;
 import com.android.server.power.PowerManagerService;
+import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener;
 import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
 import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
@@ -158,10 +158,9 @@
         mBatterySavingStats = batterySavingStats;
 
         // Initialize plugins.
-        final ArrayList<Plugin> plugins = new ArrayList<>();
-        plugins.add(new BatterySaverLocationPlugin(mContext));
-
-        mPlugins = plugins.toArray(new Plugin[plugins.size()]);
+        mPlugins = new Plugin[] {
+                new BatterySaverLocationPlugin(mContext)
+        };
     }
 
     /**
@@ -217,7 +216,7 @@
             super(looper);
         }
 
-        public void postStateChanged(boolean sendBroadcast, int reason) {
+        void postStateChanged(boolean sendBroadcast, int reason) {
             obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
                     ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget();
         }
@@ -244,9 +243,8 @@
         }
     }
 
-    /**
-     * Called by {@link PowerManagerService} to update the battery saver state.
-     */
+    /** Enable or disable full battery saver. */
+    @VisibleForTesting
     public void enableBatterySaver(boolean enable, int reason) {
         synchronized (mLock) {
             if (mEnabled == enable) {
@@ -311,7 +309,7 @@
                     reason);
             mPreviouslyEnabled = mEnabled;
 
-            listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
+            listeners = mListeners.toArray(new LowPowerModeListener[0]);
 
             enabled = mEnabled;
             mIsInteractive = isInteractive;
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
similarity index 98%
rename from services/core/java/com/android/server/power/BatterySaverPolicy.java
rename to services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index cedb548..48a041e 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.power;
+package com.android.server.power.batterysaver;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -36,21 +36,19 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ConcurrentUtils;
-import com.android.server.power.batterysaver.BatterySavingStats;
-import com.android.server.power.batterysaver.CpuFrequencies;
+import com.android.server.power.PowerManagerService;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Class to decide whether to turn on battery saver mode for specific service
+ * Class to decide whether to turn on battery saver mode for specific services.
  *
  * IMPORTANT: This class shares the power manager lock, which is very low in the lock hierarchy.
  * Do not call out with the lock held, such as AccessibilityManager. (Settings provider is okay.)
  *
- * Test:
- atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+ * Test: atest com.android.server.power.batterysaver.BatterySaverPolicyTest.java
  */
 public class BatterySaverPolicy extends ContentObserver {
     private static final String TAG = "BatterySaverPolicy";
@@ -200,8 +198,10 @@
         onChange(true, null);
     }
 
+    @VisibleForTesting
     public void addListener(BatterySaverPolicyListener listener) {
         synchronized (mLock) {
+            // TODO: set this in the constructor instead
             mListeners.add(listener);
         }
     }
@@ -244,7 +244,7 @@
             // Update.
             updateConstantsLocked(setting, deviceSpecificSetting);
 
-            listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
+            listeners = mListeners.toArray(new BatterySaverPolicyListener[0]);
         }
 
         // Notify the listeners.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index f262f6d..b7f28da 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -15,8 +15,14 @@
  */
 package com.android.server.power.batterysaver;
 
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -26,11 +32,11 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
-import com.android.server.power.BatterySaverPolicy;
 import com.android.server.power.BatterySaverStateMachineProto;
 
 import java.io.PrintWriter;
@@ -46,6 +52,8 @@
  */
 public class BatterySaverStateMachine {
     private static final String TAG = "BatterySaverStateMachine";
+    private static final String DYNAMIC_MODE_NOTIF_CHANNEL_ID = "dynamic_mode_notification";
+    private static final int DYNAMIC_MODE_NOTIFICATION_ID = 1992;
     private final Object mLock;
 
     private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
@@ -220,8 +228,8 @@
     }
 
     /**
-     * Run a {@link Runnable} on a background handler, but lazily. If the same {@link Runnable},
-     * it'll be first removed before a new one is posted.
+     * Run a {@link Runnable} on a background handler, but lazily. If the same {@link Runnable} is
+     * already registered, it'll be first removed before being re-posted.
      */
     @VisibleForTesting
     void runOnBgThreadLazy(Runnable r, int delayMillis) {
@@ -484,6 +492,13 @@
         }
         mBatterySaverController.enableBatterySaver(enable, intReason);
 
+        // Handle triggering the notification to show/hide when appropriate
+        if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
+            runOnBgThread(this::triggerDynamicModeNotification);
+        } else if (!enable) {
+            runOnBgThread(this::hideDynamicModeNotification);
+        }
+
         if (DEBUG) {
             Slog.d(TAG, "Battery saver: Enabled=" + enable
                     + " manual=" + manual
@@ -491,6 +506,44 @@
         }
     }
 
+    private void triggerDynamicModeNotification() {
+        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+        ensureNotificationChannelExists(manager);
+
+        manager.notify(DYNAMIC_MODE_NOTIFICATION_ID, buildNotification());
+    }
+
+    private void ensureNotificationChannelExists(NotificationManager manager) {
+        NotificationChannel channel = new NotificationChannel(
+                DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                mContext.getText(
+                        R.string.dynamic_mode_notification_channel_name),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setSound(null, null);
+        manager.createNotificationChannel(channel);
+    }
+
+    private Notification buildNotification() {
+        Resources res = mContext.getResources();
+        Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        PendingIntent batterySaverIntent = PendingIntent.getActivity(
+                mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        return new Notification.Builder(mContext, DYNAMIC_MODE_NOTIF_CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_battery)
+                .setContentTitle(res.getString(R.string.dynamic_mode_notification_title))
+                .setContentText(res.getString(R.string.dynamic_mode_notification_summary))
+                .setContentIntent(batterySaverIntent)
+                .setOnlyAlertOnce(true)
+                .build();
+    }
+
+    private void hideDynamicModeNotification() {
+        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+        manager.cancel(DYNAMIC_MODE_NOTIFICATION_ID);
+    }
+
     @GuardedBy("mLock")
     private void updateSnoozingLocked(boolean snoozing, String reason) {
         if (mBatterySaverSnoozing == snoozing) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 8213205..79b44eb 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -29,7 +29,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
-import com.android.server.power.BatterySaverPolicy;
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index f37ca12..5516b23 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -35,6 +35,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -92,6 +93,15 @@
     @NonNull
     private final Object mLock = new Object();
 
+    @NonNull
+    private final RoleHoldersResolver mLegacyRoleResolver;
+
+    /** @see #getRoleHolders(String, int) */
+    public interface RoleHoldersResolver {
+        /** @return a list of packages that hold a given role for a given user */
+        List<String> getRoleHolders(String roleName, int userId);
+    }
+
     /**
      * Maps user id to its state.
      */
@@ -118,9 +128,12 @@
     @NonNull
     private final Handler mListenerHandler = FgThread.getHandler();
 
-    public RoleManagerService(@NonNull Context context) {
+    public RoleManagerService(@NonNull Context context,
+            @NonNull RoleHoldersResolver legacyRoleResolver) {
         super(context);
 
+        mLegacyRoleResolver = legacyRoleResolver;
+
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
@@ -175,10 +188,17 @@
     private void performInitialGrantsIfNecessary(@UserIdInt int userId) {
         RoleUserState userState;
         userState = getOrCreateUserState(userId);
+
         String packagesHash = computeComponentStateHash(userId);
         String oldPackagesHash = userState.getPackagesHash();
         boolean needGrant = !Objects.equals(packagesHash, oldPackagesHash);
         if (needGrant) {
+
+            //TODO gradually add more role migrations statements here for remaining roles
+            // Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders
+            // for a given role before adding a migration statement for it here
+            migrateRoleIfNecessary(RoleManager.ROLE_SMS, userId);
+
             // Some vital packages state has changed since last role grant
             // Run grants again
             Slog.i(LOG_TAG, "Granting default permissions...");
@@ -205,6 +225,23 @@
         }
     }
 
+    private void migrateRoleIfNecessary(String role, @UserIdInt int userId) {
+        // Any role for which we have a record are already migrated
+        RoleUserState userState = getOrCreateUserState(userId);
+        if (!userState.isRoleAvailable(role)) {
+            List<String> roleHolders = mLegacyRoleResolver.getRoleHolders(role, userId);
+            if (roleHolders.isEmpty()) {
+                return;
+            }
+            Slog.i(LOG_TAG, "Migrating " + role + ", legacy holders: " + roleHolders);
+            userState.addRoleName(role);
+            int size = roleHolders.size();
+            for (int i = 0; i < size; i++) {
+                userState.addRoleHolder(role, roleHolders.get(i));
+            }
+        }
+    }
+
     @Nullable
     private static String computeComponentStateHash(@UserIdInt int userId) {
         PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
@@ -530,6 +567,17 @@
         }
 
         @Override
+        public String getDefaultSmsPackage(int userId) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                return CollectionUtils.firstOrNull(
+                        getRoleHoldersAsUser(RoleManager.ROLE_SMS, userId));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
                 @Nullable String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), LOG_TAG, fout)) {
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 630a39c..02dcc49 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -193,14 +193,40 @@
      *
      * @param roleName the name of the role to query for
      *
-     * @return the set of role holders. {@code null} should not be returned and indicates an issue.
+     * @return the set of role holders, or {@code null} if and only if the role is not found
      */
     @Nullable
     public ArraySet<String> getRoleHolders(@NonNull String roleName) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
 
-            return new ArraySet<>(mRoles.get(roleName));
+            ArraySet<String> packageNames = mRoles.get(roleName);
+            if (packageNames == null) {
+                return null;
+            }
+            return new ArraySet<>(packageNames);
+        }
+    }
+
+    /**
+     * Adds the given role, effectively marking it as {@link #isRoleAvailable available}
+     *
+     * @param roleName the name of the role
+     *
+     * @return whether any changes were made
+     */
+    public boolean addRoleName(@NonNull String roleName) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+
+            if (!mRoles.containsKey(roleName)) {
+                mRoles.put(roleName, new ArraySet<>());
+                Slog.i(LOG_TAG, "Added new role: " + roleName);
+                scheduleWriteFileLocked();
+                return true;
+            } else {
+                return false;
+            }
         }
     }
 
@@ -231,13 +257,7 @@
 
             int roleNamesSize = roleNames.size();
             for (int i = 0; i < roleNamesSize; i++) {
-                String roleName = roleNames.get(i);
-
-                if (!mRoles.containsKey(roleName)) {
-                    mRoles.put(roleName, new ArraySet<>());
-                    Slog.i(LOG_TAG, "Added new role: " + roleName);
-                    changed = true;
-                }
+                changed |= addRoleName(roleNames.get(i));
             }
 
             if (changed) {
@@ -252,8 +272,7 @@
      * @param roleName the name of the role to add the holder to
      * @param packageName the package name of the new holder
      *
-     * @return {@code false} only if the set of role holders is null, which should not happen and
-     *         indicates an issue.
+     * @return {@code false} if and only if the role is not found
      */
     @CheckResult
     public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
@@ -286,8 +305,7 @@
      * @param roleName the name of the role to remove the holder from
      * @param packageName the package name of the holder to remove
      *
-     * @return {@code false} only if the set of role holders is null, which should not happen and
-     *         indicates an issue.
+     * @return {@code false} if and only if the role is not found
      */
     @CheckResult
     public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
diff --git a/services/core/java/com/android/server/rollback/PackageRollbackData.java b/services/core/java/com/android/server/rollback/RollbackData.java
similarity index 65%
rename from services/core/java/com/android/server/rollback/PackageRollbackData.java
rename to services/core/java/com/android/server/rollback/RollbackData.java
index 15d1242..f0589aa 100644
--- a/services/core/java/com/android/server/rollback/PackageRollbackData.java
+++ b/services/core/java/com/android/server/rollback/RollbackData.java
@@ -20,17 +20,21 @@
 
 import java.io.File;
 import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Information about a rollback available for a particular package.
- * This is similar to {@link PackageRollbackInfo}, but extended with
- * additional information for internal bookkeeping.
+ * Information about a rollback available for a set of atomically installed
+ * packages.
  */
-class PackageRollbackData {
-    public final PackageRollbackInfo info;
+class RollbackData {
+    /**
+     * The per-package rollback information.
+     */
+    public final List<PackageRollbackInfo> packages = new ArrayList<>();
 
     /**
-     * The directory where the apk backup is stored.
+     * The directory where the rollback data is stored.
      */
     public final File backupDir;
 
@@ -38,12 +42,9 @@
      * The time when the upgrade occurred, for purposes of expiring
      * rollback data.
      */
-    public final Instant timestamp;
+    public Instant timestamp;
 
-    PackageRollbackData(PackageRollbackInfo info,
-            File backupDir, Instant timestamp) {
-        this.info = info;
+    RollbackData(File backupDir) {
         this.backupDir = backupDir;
-        this.timestamp = timestamp;
     }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java
index 8c3f04f..4b5e764 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerService.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java
@@ -17,8 +17,6 @@
 package com.android.server.rollback;
 
 import android.content.Context;
-import android.os.SELinux;
-import android.util.Log;
 
 import com.android.server.SystemService;
 
@@ -30,8 +28,6 @@
  */
 public final class RollbackManagerService extends SystemService {
 
-    private static final String TAG = "RollbackManager";
-
     private RollbackManagerServiceImpl mService;
 
     public RollbackManagerService(Context context) {
@@ -41,12 +37,6 @@
     @Override
     public void onStart() {
         mService = new RollbackManagerServiceImpl(getContext());
-
-        // TODO: Set up sepolicy to allow publishing the service.
-        if (SELinux.isSELinuxEnforced()) {
-            Log.w(TAG, "RollbackManager disabled pending selinux policy updates");
-        } else {
-            publishBinderService(Context.ROLLBACK_SERVICE, mService);
-        }
+        publishBinderService(Context.ROLLBACK_SERVICE, mService);
     }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 0c21312..d12f7ed 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -63,9 +63,11 @@
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -86,10 +88,20 @@
     // mLock is held when they are called.
     private final Object mLock = new Object();
 
+    // Package rollback data for rollback-enabled installs that have not yet
+    // been committed. Maps from sessionId to rollback data.
+    @GuardedBy("mLock")
+    private final Map<Integer, RollbackData> mPendingRollbacks = new HashMap<>();
+
+    // Map from child session id's for enabled rollbacks to their
+    // corresponding parent session ids.
+    @GuardedBy("mLock")
+    private final Map<Integer, Integer> mChildSessions = new HashMap<>();
+
     // Package rollback data available to be used for rolling back a package.
     // This list is null until the rollback data has been loaded.
     @GuardedBy("mLock")
-    private List<PackageRollbackData> mAvailableRollbacks;
+    private List<RollbackData> mAvailableRollbacks;
 
     // The list of recently executed rollbacks.
     // This list is null until the rollback data has been loaded.
@@ -102,14 +114,25 @@
     // to store this data:
     //   /data/rollback/
     //      available/
-    //          com.package.A-XXX/
-    //              base.apk
-    //              rollback.json
-    //          com.package.B-YYY/
-    //              base.apk
-    //              rollback.json
+    //          XXX/
+    //              com.package.A/
+    //                  base.apk
+    //                  info.json
+    //              enabled.txt
+    //          YYY/
+    //              com.package.B/
+    //                  base.apk
+    //                  info.json
+    //              enabled.txt
     //      recently_executed.json
-    // TODO: Use AtomicFile for rollback.json and recently_executed.json.
+    //
+    // * XXX, YYY are random strings from Files.createTempDirectory
+    // * info.json contains the package version to roll back from/to.
+    // * enabled.txt contains a timestamp for when the rollback was first
+    //   made available. This file is not written until the rollback is made
+    //   available.
+    //
+    // TODO: Use AtomicFile for all the .json files?
     private final File mRollbackDataDir;
     private final File mAvailableRollbacksDir;
     private final File mRecentlyExecutedRollbacksFile;
@@ -135,6 +158,9 @@
         // expiration.
         getHandler().post(() -> ensureRollbackDataLoaded());
 
+        PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
+        installer.registerSessionCallback(new SessionCallback(), getHandler());
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
@@ -199,27 +225,23 @@
                 android.Manifest.permission.MANAGE_ROLLBACKS,
                 "getAvailableRollback");
 
-        PackageRollbackInfo.PackageVersion installedVersion =
-                getInstalledPackageVersion(packageName);
-        if (installedVersion == null) {
+        RollbackData data = getRollbackForPackage(packageName);
+        if (data == null) {
             return null;
         }
 
-        synchronized (mLock) {
-            // TODO: Have ensureRollbackDataLoadedLocked return the list of
-            // available rollbacks, to hopefully avoid forgetting to call it?
-            ensureRollbackDataLoadedLocked();
-            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData data = mAvailableRollbacks.get(i);
-                if (data.info.packageName.equals(packageName)
-                        && data.info.higherVersion.equals(installedVersion)) {
-                    // TODO: For atomic installs, check all dependent packages
-                    // for available rollbacks and include that info here.
-                    return new RollbackInfo(data.info);
-                }
+        // Note: The rollback for the package ought to be for the currently
+        // installed version, otherwise the rollback data is out of date. In
+        // that rare case, we'll check when we execute the rollback whether
+        // it's out of date or not, so no need to check package versions here.
+
+        for (PackageRollbackInfo info : data.packages) {
+            if (info.packageName.equals(packageName)) {
+                // TODO: Once the RollbackInfo API supports info about
+                // dependant packages, add that info here.
+                return new RollbackInfo(info);
             }
         }
-
         return null;
     }
 
@@ -229,16 +251,14 @@
                 android.Manifest.permission.MANAGE_ROLLBACKS,
                 "getPackagesWithAvailableRollbacks");
 
-        // TODO: This may return packages whose rollback is out of date or
-        // expired.  Presumably that's okay because the package rollback could
-        // be expired anyway between when the caller calls this method and
-        // when the caller calls getAvailableRollback for more details.
         final Set<String> packageNames = new HashSet<>();
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
             for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData data = mAvailableRollbacks.get(i);
-                packageNames.add(data.info.packageName);
+                RollbackData data = mAvailableRollbacks.get(i);
+                for (PackageRollbackInfo info : data.packages) {
+                    packageNames.add(info.packageName);
+                }
             }
         }
         return new StringParceledListSlice(new ArrayList<>(packageNames));
@@ -279,47 +299,49 @@
      */
     private void executeRollbackInternal(RollbackInfo rollback,
             String callerPackageName, IntentSender statusReceiver) {
-        String packageName = rollback.targetPackage.packageName;
-        Log.i(TAG, "Initiating rollback of " + packageName);
+        String targetPackageName = rollback.targetPackage.packageName;
+        Log.i(TAG, "Initiating rollback of " + targetPackageName);
 
-        PackageRollbackInfo.PackageVersion installedVersion =
-                getInstalledPackageVersion(packageName);
-        if (installedVersion == null) {
-            // TODO: Test this case
-            sendFailure(statusReceiver, "Target package to roll back is not installed");
+        // Get the latest RollbackData for the target package.
+        RollbackData data = getRollbackForPackage(targetPackageName);
+        if (data == null) {
+            sendFailure(statusReceiver, "No rollback available for package.");
             return;
         }
 
-        if (!rollback.targetPackage.higherVersion.equals(installedVersion)) {
-            // TODO: Test this case
-            sendFailure(statusReceiver, "Target package version to roll back not installed.");
-            return;
+        // Verify the latest rollback matches the version requested.
+        // TODO: Check dependant packages too once RollbackInfo includes that
+        // information.
+        for (PackageRollbackInfo info : data.packages) {
+            if (info.packageName.equals(targetPackageName)
+                    && !rollback.targetPackage.higherVersion.equals(info.higherVersion)) {
+                sendFailure(statusReceiver, "Rollback is out of date.");
+                return;
+            }
         }
 
+        // Verify the RollbackData is up to date with what's installed on
+        // device.
         // TODO: We assume that between now and the time we commit the
         // downgrade install, the currently installed package version does not
         // change. This is not safe to assume, particularly in the case of a
         // rollback racing with a roll-forward fix of a buggy package.
         // Figure out how to ensure we don't commit the rollback if
         // roll forward happens at the same time.
-        PackageRollbackData data = null;
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData available = mAvailableRollbacks.get(i);
-                // TODO: Check if available.info.lowerVersion matches
-                // rollback.targetPackage.lowerVersion?
-                if (available.info.packageName.equals(packageName)
-                        && available.info.higherVersion.equals(installedVersion)) {
-                    data = available;
-                    break;
-                }
+        for (PackageRollbackInfo info : data.packages) {
+            PackageRollbackInfo.PackageVersion installedVersion =
+                    getInstalledPackageVersion(info.packageName);
+            if (installedVersion == null) {
+                // TODO: Test this case
+                sendFailure(statusReceiver, "Package to roll back is not installed");
+                return;
             }
-        }
 
-        if (data == null) {
-            sendFailure(statusReceiver, "Rollback not available");
-            return;
+            if (!info.higherVersion.equals(installedVersion)) {
+                // TODO: Test this case
+                sendFailure(statusReceiver, "Package version to roll back not installed.");
+                return;
+            }
         }
 
         // Get a context for the caller to use to install the downgraded
@@ -334,29 +356,39 @@
 
         PackageManager pm = context.getPackageManager();
         try {
-            PackageInstaller.Session session = null;
-
             PackageInstaller packageInstaller = pm.getPackageInstaller();
-            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+            PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
                     PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-            params.setAllowDowngrade(true);
-            int sessionId = packageInstaller.createSession(params);
-            session = packageInstaller.openSession(sessionId);
+            parentParams.setAllowDowngrade(true);
+            parentParams.setMultiPackage();
+            int parentSessionId = packageInstaller.createSession(parentParams);
+            PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
 
-            // TODO: Will it always be called "base.apk"? What about splits?
-            File baseApk = new File(data.backupDir, "base.apk");
-            try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
-                    ParcelFileDescriptor.MODE_READ_ONLY)) {
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    session.write("base.apk", 0, baseApk.length(), fd);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            for (PackageRollbackInfo info : data.packages) {
+                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+                params.setAllowDowngrade(true);
+                int sessionId = packageInstaller.createSession(params);
+                PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+
+                // TODO: Will it always be called "base.apk"? What about splits?
+                // What about apex?
+                File packageDir = new File(data.backupDir, info.packageName);
+                File baseApk = new File(packageDir, "base.apk");
+                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
+                        ParcelFileDescriptor.MODE_READ_ONLY)) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        session.write("base.apk", 0, baseApk.length(), fd);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+                parentSession.addChildSessionId(sessionId);
             }
 
             final LocalIntentReceiver receiver = new LocalIntentReceiver();
-            session.commit(receiver.getIntentSender());
+            parentSession.commit(receiver.getIntentSender());
 
             Intent result = receiver.getResult();
             int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
@@ -371,13 +403,14 @@
             sendSuccess(statusReceiver);
 
             Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
-                    Uri.fromParts("package", packageName, Manifest.permission.MANAGE_ROLLBACKS));
+                    Uri.fromParts("package", targetPackageName,
+                        Manifest.permission.MANAGE_ROLLBACKS));
 
             // TODO: This call emits the warning "Calling a method in the
             // system process without a qualified user". Fix that.
             mContext.sendBroadcast(broadcast);
         } catch (IOException e) {
-            Log.e(TAG, "Unable to roll back " + packageName, e);
+            Log.e(TAG, "Unable to roll back " + targetPackageName, e);
             sendFailure(statusReceiver, "IOException: " + e.toString());
             return;
         }
@@ -408,12 +441,15 @@
         // testing anyway.
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
             while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
-                if (data.info.packageName.equals(packageName)) {
-                    iter.remove();
-                    removeFile(data.backupDir);
+                RollbackData data = iter.next();
+                for (PackageRollbackInfo info : data.packages) {
+                    if (info.packageName.equals(packageName)) {
+                        iter.remove();
+                        removeFile(data.backupDir);
+                        break;
+                    }
                 }
             }
         }
@@ -453,27 +489,41 @@
         mAvailableRollbacksDir.mkdirs();
         mAvailableRollbacks = new ArrayList<>();
         for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
-            if (rollbackDir.isDirectory()) {
-                // TODO: How to detect and clean up an invalid rollback
-                // directory? We don't know if it's invalid because something
-                // went wrong, or if it's only temporarily invalid because
-                // it's in the process of being created.
+            File enabledFile = new File(rollbackDir, "enabled.txt");
+            // TODO: Delete any directories without an enabled.txt? That could
+            // potentially delete pending rollback data if reloadPersistedData
+            // is called, though there's no reason besides testing for that to
+            // be called.
+            if (rollbackDir.isDirectory() && enabledFile.isFile()) {
+                RollbackData data = new RollbackData(rollbackDir);
                 try {
-                    File jsonFile = new File(rollbackDir, "rollback.json");
-                    String jsonString = IoUtils.readFileAsString(jsonFile.getAbsolutePath());
-                    JSONObject jsonObject = new JSONObject(jsonString);
-                    String packageName = jsonObject.getString("packageName");
-                    long higherVersionCode = jsonObject.getLong("higherVersionCode");
-                    long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
-                    Instant timestamp = Instant.parse(jsonObject.getString("timestamp"));
-                    PackageRollbackData data = new PackageRollbackData(
-                            new PackageRollbackInfo(packageName,
-                                new PackageRollbackInfo.PackageVersion(higherVersionCode),
-                                new PackageRollbackInfo.PackageVersion(lowerVersionCode)),
-                            rollbackDir, timestamp);
+                    PackageRollbackInfo info = null;
+                    for (File packageDir : rollbackDir.listFiles()) {
+                        if (packageDir.isDirectory()) {
+                            File jsonFile = new File(packageDir, "info.json");
+                            String jsonString = IoUtils.readFileAsString(
+                                    jsonFile.getAbsolutePath());
+                            JSONObject jsonObject = new JSONObject(jsonString);
+                            String packageName = jsonObject.getString("packageName");
+                            long higherVersionCode = jsonObject.getLong("higherVersionCode");
+                            long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
+
+                            data.packages.add(new PackageRollbackInfo(packageName,
+                                        new PackageRollbackInfo.PackageVersion(higherVersionCode),
+                                        new PackageRollbackInfo.PackageVersion(lowerVersionCode)));
+                        }
+                    }
+
+                    if (data.packages.isEmpty()) {
+                        throw new IOException("No package rollback info found");
+                    }
+
+                    String enabledString = IoUtils.readFileAsString(enabledFile.getAbsolutePath());
+                    data.timestamp = Instant.parse(enabledString.trim());
                     mAvailableRollbacks.add(data);
                 } catch (IOException | JSONException | DateTimeParseException e) {
                     Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+                    removeFile(rollbackDir);
                 }
             }
         }
@@ -521,13 +571,16 @@
 
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
             while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
-                if (data.info.packageName.equals(packageName)
-                        && !data.info.higherVersion.equals(installedVersion)) {
-                    iter.remove();
-                    removeFile(data.backupDir);
+                RollbackData data = iter.next();
+                for (PackageRollbackInfo info : data.packages) {
+                    if (info.packageName.equals(packageName)
+                            && !info.higherVersion.equals(installedVersion)) {
+                        iter.remove();
+                        removeFile(data.backupDir);
+                        break;
+                    }
                 }
             }
         }
@@ -643,9 +696,9 @@
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
 
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
             while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
+                RollbackData data = iter.next();
                 if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
                     iter.remove();
                     removeFile(data.backupDir);
@@ -673,6 +726,23 @@
         return mHandlerThread.getThreadHandler();
     }
 
+    // Returns true if <code>session</code> has installFlags and code path
+    // matching the installFlags and new package code path given to
+    // enableRollback.
+    private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
+            int installFlags, File newPackageCodePath) {
+        if (session == null || session.resolvedBaseCodePath == null) {
+            return false;
+        }
+
+        File packageCodePath = new File(session.resolvedBaseCodePath).getParentFile();
+        if (newPackageCodePath.equals(packageCodePath) && installFlags == session.installFlags) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Called via broadcast by the package manager when a package is being
      * staged for install with rollback enabled. Called before the package has
@@ -705,6 +775,34 @@
         String packageName = newPackage.packageName;
         Log.i(TAG, "Enabling rollback for install of " + packageName);
 
+        // Figure out the session id associated with this install.
+        int parentSessionId = PackageInstaller.SessionInfo.INVALID_ID;
+        int childSessionId = PackageInstaller.SessionInfo.INVALID_ID;
+        PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
+        for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
+            if (info.isMultiPackage()) {
+                for (int childId : info.getChildSessionIds()) {
+                    PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
+                    if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
+                        // TODO: Check we only have one matching session?
+                        parentSessionId = info.getSessionId();
+                        childSessionId = childId;
+                    }
+                }
+            } else {
+                if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
+                    // TODO: Check we only have one matching session?
+                    parentSessionId = info.getSessionId();
+                    childSessionId = parentSessionId;
+                }
+            }
+        }
+
+        if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) {
+            Log.e(TAG, "Unable to find session id for enabled rollback.");
+            return false;
+        }
+
         PackageRollbackInfo.PackageVersion newVersion =
                 new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
 
@@ -720,52 +818,53 @@
         PackageRollbackInfo.PackageVersion installedVersion =
                 new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
 
-        File backupDir;
+        PackageRollbackInfo info = new PackageRollbackInfo(
+                packageName, newVersion, installedVersion);
+
+        RollbackData data;
         try {
-            backupDir = Files.createTempDirectory(
-                mAvailableRollbacksDir.toPath(), packageName + "-").toFile();
+            synchronized (mLock) {
+                mChildSessions.put(childSessionId, parentSessionId);
+                data = mPendingRollbacks.get(parentSessionId);
+                if (data == null) {
+                    File backupDir = Files.createTempDirectory(
+                            mAvailableRollbacksDir.toPath(), null).toFile();
+                    data = new RollbackData(backupDir);
+                    mPendingRollbacks.put(parentSessionId, data);
+                }
+                data.packages.add(info);
+            }
         } catch (IOException e) {
             Log.e(TAG, "Unable to create rollback for " + packageName, e);
             return false;
         }
 
-        // TODO: Should the timestamp be for when we commit the install, not
-        // when we create the pending one?
-        Instant timestamp = Instant.now();
+        File packageDir = new File(data.backupDir, packageName);
+        packageDir.mkdirs();
         try {
             JSONObject json = new JSONObject();
             json.put("packageName", packageName);
             json.put("higherVersionCode", newVersion.versionCode);
             json.put("lowerVersionCode", installedVersion.versionCode);
-            json.put("timestamp", timestamp.toString());
 
-            File jsonFile = new File(backupDir, "rollback.json");
+            File jsonFile = new File(packageDir, "info.json");
             PrintWriter pw = new PrintWriter(jsonFile);
             pw.println(json.toString());
             pw.close();
         } catch (IOException | JSONException e) {
             Log.e(TAG, "Unable to create rollback for " + packageName, e);
-            removeFile(backupDir);
+            removeFile(packageDir);
             return false;
         }
 
         // TODO: Copy by hard link instead to save on cpu and storage space?
-        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, backupDir);
+        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, packageDir);
         if (status != PackageManager.INSTALL_SUCCEEDED) {
             Log.e(TAG, "Unable to copy package for rollback for " + packageName);
-            removeFile(backupDir);
+            removeFile(packageDir);
             return false;
         }
 
-        PackageRollbackData data = new PackageRollbackData(
-                new PackageRollbackInfo(packageName, newVersion, installedVersion),
-                backupDir, timestamp);
-
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            mAvailableRollbacks.add(data);
-        }
-
         return true;
     }
 
@@ -829,4 +928,89 @@
 
         return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
     }
+
+    private class SessionCallback extends PackageInstaller.SessionCallback {
+
+        @Override
+        public void onCreated(int sessionId) { }
+
+        @Override
+        public void onBadgingChanged(int sessionId) { }
+
+        @Override
+        public void onActiveChanged(int sessionId, boolean active) { }
+
+        @Override
+        public void onProgressChanged(int sessionId, float progress) { }
+
+        @Override
+        public void onFinished(int sessionId, boolean success) {
+            RollbackData data = null;
+            synchronized (mLock) {
+                Integer parentSessionId = mChildSessions.remove(sessionId);
+                if (parentSessionId != null) {
+                    sessionId = parentSessionId;
+                }
+                data = mPendingRollbacks.remove(sessionId);
+            }
+
+            if (data != null) {
+                if (success) {
+                    try {
+                        data.timestamp = Instant.now();
+                        File enabledFile = new File(data.backupDir, "enabled.txt");
+                        PrintWriter pw = new PrintWriter(enabledFile);
+                        pw.println(data.timestamp.toString());
+                        pw.close();
+
+                        synchronized (mLock) {
+                            // Note: There is a small window of time between when
+                            // the session has been committed by the package
+                            // manager and when we make the rollback available
+                            // here. Presumably the window is small enough that
+                            // nobody will want to roll back the newly installed
+                            // package before we make the rollback available.
+                            // TODO: We'll lose the rollback data if the
+                            // device reboots between when the session is
+                            // committed and this point. Revisit this after
+                            // adding support for rollback of staged installs.
+                            ensureRollbackDataLoadedLocked();
+                            mAvailableRollbacks.add(data);
+                        }
+
+                        scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Unable to enable rollback", e);
+                        removeFile(data.backupDir);
+                    }
+                } else {
+                    // The install session was aborted, clean up the pending
+                    // install.
+                    removeFile(data.backupDir);
+                }
+            }
+        }
+    }
+
+    /*
+     * Returns the RollbackData, if any, for an available rollback that would
+     * roll back the given package. Note: This assumes we have at most one
+     * available rollback for a given package at any one time.
+     */
+    private RollbackData getRollbackForPackage(String packageName) {
+        synchronized (mLock) {
+            // TODO: Have ensureRollbackDataLoadedLocked return the list of
+            // available rollbacks, to hopefully avoid forgetting to call it?
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                RollbackData data = mAvailableRollbacks.get(i);
+                for (PackageRollbackInfo info : data.packages) {
+                    if (info.packageName.equals(packageName)) {
+                        return data;
+                    }
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/rollback/TEST_MAPPING b/services/core/java/com/android/server/rollback/TEST_MAPPING
new file mode 100644
index 0000000..c1d95ac
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "RollbackTest"
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 839ed30..b1db46f 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -36,6 +36,7 @@
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -59,6 +60,8 @@
     /** The maximum size of signature file.  This is just to avoid potential abuse. */
     private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
 
+    private static final int COMMON_LINUX_PAGE_SIZE_IN_BYTES = 4096;
+
     private static final boolean DEBUG = false;
 
     /** Returns true if the given file looks like containing an fs-verity signature. */
@@ -71,6 +74,48 @@
         return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
     }
 
+    /** Generates Merkle tree and fs-verity metadata then enables fs-verity. */
+    public static void setUpFsverity(@NonNull String filePath, String signaturePath)
+            throws IOException, DigestException, NoSuchAlgorithmException {
+        final PKCS7 pkcs7 = new PKCS7(Files.readAllBytes(Paths.get(signaturePath)));
+        final byte[] expectedMeasurement = pkcs7.getContentInfo().getContentBytes();
+        if (DEBUG) {
+            Slog.d(TAG, "Enabling fs-verity with signed fs-verity measurement "
+                    + bytesToString(expectedMeasurement));
+            Slog.d(TAG, "PKCS#7 info: " + pkcs7);
+        }
+
+        final TrackedBufferFactory bufferFactory = new TrackedBufferFactory();
+        final byte[] actualMeasurement = generateFsverityMetadata(filePath, signaturePath,
+                bufferFactory);
+        try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw")) {
+            FileChannel ch = raf.getChannel();
+            ch.position(roundUpToNextMultiple(ch.size(), COMMON_LINUX_PAGE_SIZE_IN_BYTES));
+            ByteBuffer buffer = bufferFactory.getBuffer();
+
+            long offset = buffer.position();
+            long size = buffer.limit();
+            while (offset < size) {
+                long s = ch.write(buffer);
+                offset += s;
+                size -= s;
+            }
+        }
+
+        if (!Arrays.equals(expectedMeasurement, actualMeasurement)) {
+            throw new SecurityException("fs-verity measurement mismatch: "
+                    + bytesToString(actualMeasurement) + " != "
+                    + bytesToString(expectedMeasurement));
+        }
+
+        // This can fail if the public key is not already in .fs-verity kernel keyring.
+        int errno = enableFsverityNative(filePath);
+        if (errno != 0) {
+            throw new IOException("Failed to enable fs-verity on " + filePath + ": "
+                    + Os.strerror(errno));
+        }
+    }
+
     /** Returns whether the file has fs-verity enabled. */
     public static boolean hasFsverity(@NonNull String filePath) {
         // NB: only measure but not check the actual measurement here. As long as this succeeds,
@@ -87,36 +132,18 @@
     }
 
     /**
-     * Generates Merkle tree and fs-verity metadata.
+     * Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
      *
      * @return {@code SetupResult} that contains the result code, and when success, the
      *         {@code FileDescriptor} to read all the data from.
      */
-    public static SetupResult generateApkVeritySetupData(@NonNull String apkPath,
-            String signaturePath, boolean skipSigningBlock) {
+    public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) {
         if (DEBUG) {
-            Slog.d(TAG, "Trying to install apk verity to " + apkPath + " with signature file "
-                    + signaturePath);
+            Slog.d(TAG, "Trying to install legacy apk verity to " + apkPath);
         }
         SharedMemory shm = null;
         try {
-            byte[] signedVerityHash;
-            if (skipSigningBlock) {
-                signedVerityHash = ApkSignatureVerifier.getVerityRootHash(apkPath);
-            } else {
-                Path path = Paths.get(signaturePath);
-                if (Files.exists(path)) {
-                    // TODO(112037636): fail early if the signing key is not in .fs-verity keyring.
-                    PKCS7 pkcs7 = new PKCS7(Files.readAllBytes(path));
-                    signedVerityHash = pkcs7.getContentInfo().getContentBytes();
-                    if (DEBUG) {
-                        Slog.d(TAG, "fs-verity measurement = " + bytesToString(signedVerityHash));
-                    }
-                } else {
-                    signedVerityHash = null;
-                }
-            }
-
+            final byte[] signedVerityHash = ApkSignatureVerifier.getVerityRootHash(apkPath);
             if (signedVerityHash == null) {
                 if (DEBUG) {
                     Slog.d(TAG, "Skip verity tree generation since there is no signed root hash");
@@ -124,8 +151,8 @@
                 return SetupResult.skipped();
             }
 
-            Pair<SharedMemory, Integer> result = generateFsVerityIntoSharedMemory(apkPath,
-                    signaturePath, signedVerityHash, skipSigningBlock);
+            Pair<SharedMemory, Integer> result =
+                    generateFsVerityIntoSharedMemory(apkPath, signedVerityHash);
             shm = result.first;
             int contentSize = result.second;
             FileDescriptor rfd = shm.getFileDescriptor();
@@ -156,7 +183,7 @@
      * {@see ApkSignatureVerifier#getVerityRootHash(String)}.
      */
     public static byte[] getVerityRootHash(@NonNull String apkPath)
-            throws IOException, SignatureNotFoundException, SecurityException {
+            throws IOException, SignatureNotFoundException {
         return ApkSignatureVerifier.getVerityRootHash(apkPath);
     }
 
@@ -172,9 +199,8 @@
      *         includes SHA-256 of fs-verity descriptor and authenticated extensions.
      */
     private static byte[] generateFsverityMetadata(String filePath, String signaturePath,
-            @NonNull TrackedShmBufferFactory trackedBufferFactory)
-            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
-                   NoSuchAlgorithmException {
+            @NonNull ByteBufferFactory trackedBufferFactory)
+            throws IOException, DigestException, NoSuchAlgorithmException {
         try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
             VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree(
                     file, trackedBufferFactory);
@@ -184,6 +210,7 @@
 
             final byte[] measurement = generateFsverityDescriptorAndMeasurement(file,
                     result.rootHash, signaturePath, buffer);
+            buffer.flip();
             return constructFsveritySignedDataNative(measurement);
         }
     }
@@ -243,6 +270,7 @@
         return md.digest();
     }
 
+    private static native int enableFsverityNative(@NonNull String filePath);
     private static native int measureFsverityNative(@NonNull String filePath);
     private static native byte[] constructFsveritySignedDataNative(@NonNull byte[] measurement);
     private static native byte[] constructFsverityDescriptorNative(long fileSize);
@@ -256,18 +284,13 @@
      * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has
      * length equals to the returned {@code Integer}.
      */
-    private static Pair<SharedMemory, Integer> generateFsVerityIntoSharedMemory(
-            String apkPath, String signaturePath, @NonNull byte[] expectedRootHash,
-            boolean skipSigningBlock)
-            throws IOException, SecurityException, DigestException, NoSuchAlgorithmException,
+    private static Pair<SharedMemory, Integer> generateFsVerityIntoSharedMemory(String apkPath,
+            @NonNull byte[] expectedRootHash)
+            throws IOException, DigestException, NoSuchAlgorithmException,
                    SignatureNotFoundException {
         TrackedShmBufferFactory shmBufferFactory = new TrackedShmBufferFactory();
-        byte[] generatedRootHash;
-        if (skipSigningBlock) {
-            generatedRootHash = ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory);
-        } else {
-            generatedRootHash = generateFsverityMetadata(apkPath, signaturePath, shmBufferFactory);
-        }
+        byte[] generatedRootHash =
+                ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory);
         // We only generate Merkle tree once here, so it's important to make sure the root hash
         // matches the signed one in the apk.
         if (!Arrays.equals(expectedRootHash, generatedRootHash)) {
@@ -345,7 +368,7 @@
         private ByteBuffer mBuffer;
 
         @Override
-        public ByteBuffer create(int capacity) throws SecurityException {
+        public ByteBuffer create(int capacity) {
             try {
                 if (DEBUG) Slog.d(TAG, "Creating shared memory for apk verity");
                 // NB: This method is supposed to be called once according to the contract with
@@ -378,4 +401,30 @@
             return mBuffer == null ? -1 : mBuffer.limit();
         }
     }
+
+    /** A {@code ByteBufferFactory} that tracks the {@code ByteBuffer} it creates. */
+    private static class TrackedBufferFactory implements ByteBufferFactory {
+        private ByteBuffer mBuffer;
+
+        @Override
+        public ByteBuffer create(int capacity) {
+            if (mBuffer != null) {
+                throw new IllegalStateException("Multiple instantiation from this factory");
+            }
+            mBuffer = ByteBuffer.allocate(capacity);
+            return mBuffer;
+        }
+
+        public ByteBuffer getBuffer() {
+            return mBuffer;
+        }
+    }
+
+    /** Round up the number to the next multiple of the divisor. */
+    private static long roundUpToNextMultiple(long number, long divisor) {
+        if (number > (Long.MAX_VALUE - divisor)) {
+            throw new IllegalArgumentException("arithmetic overflow");
+        }
+        return ((number + (divisor - 1)) / divisor) * divisor;
+    }
 }
diff --git a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
index 438c303..d77cf90 100644
--- a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
@@ -23,6 +23,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import java.security.GeneralSecurityException;
 import java.util.Arrays;
@@ -66,12 +67,14 @@
 
     private final Context mContext;
     private final String mSourcePackage;
+    private final SignedConfigEvent mEvent;
     private final SignatureVerifier mVerifier;
 
-    GlobalSettingsConfigApplicator(Context context, String sourcePackage) {
+    GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) {
         mContext = context;
         mSourcePackage = sourcePackage;
-        mVerifier = new SignatureVerifier();
+        mEvent = event;
+        mVerifier = new SignatureVerifier(mEvent);
     }
 
     private boolean checkSignature(String data, String signature) {
@@ -79,6 +82,7 @@
             return mVerifier.verifySignature(data, signature);
         } catch (GeneralSecurityException e) {
             Slog.e(TAG, "Failed to verify signature", e);
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION;
             return false;
         }
     }
@@ -109,14 +113,17 @@
         SignedConfig config;
         try {
             config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
+            mEvent.version = config.version;
         } catch (InvalidConfigException e) {
             Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e);
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG;
             return;
         }
         int currentVersion = getCurrentConfigVersion();
         if (currentVersion >= config.version) {
             Slog.i(TAG, "Global settings from package " + mSourcePackage
                     + " is older than existing: " + config.version + "<=" + currentVersion);
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG;
             return;
         }
         // We have new config!
@@ -126,10 +133,12 @@
                 config.getMatchingConfig(Build.VERSION.SDK_INT);
         if (matchedConfig == null) {
             Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring");
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE;
             return;
         }
 
         Slog.i(TAG, "Updating global settings to version " + config.version);
         updateCurrentConfig(config.version, matchedConfig.values);
+        mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED;
     }
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
index 944db84..56db32a 100644
--- a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import java.nio.charset.StandardCharsets;
 import java.security.InvalidKeyException;
@@ -42,11 +43,18 @@
     private static final String DEBUG_KEY =
             "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60"
             + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==";
+    private static final String PROD_KEY =
+            "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc+tzC8MvnrzVxodvTp"
+            + "VY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g==";
 
+    private final SignedConfigEvent mEvent;
     private final PublicKey mDebugKey;
+    private final PublicKey mProdKey;
 
-    public SignatureVerifier() {
-        mDebugKey = createKey(DEBUG_KEY);
+    public SignatureVerifier(SignedConfigEvent event) {
+        mEvent = event;
+        mDebugKey = Build.IS_DEBUGGABLE ? createKey(DEBUG_KEY) : null;
+        mProdKey = createKey(PROD_KEY);
     }
 
     private static PublicKey createKey(String base64) {
@@ -67,6 +75,14 @@
         }
     }
 
+    private boolean verifyWithPublicKey(PublicKey key, byte[] data, byte[] signature)
+            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+        Signature verifier = Signature.getInstance("SHA256withECDSA");
+        verifier.initVerify(key);
+        verifier.update(data);
+        return verifier.verify(signature);
+    }
+
     /**
      * Verify a signature for signed config.
      *
@@ -80,6 +96,7 @@
         try {
             signature = Base64.getDecoder().decode(base64Signature);
         } catch (IllegalArgumentException e) {
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE;
             Slog.e(TAG, "Failed to base64 decode signature");
             return false;
         }
@@ -89,11 +106,9 @@
         if (Build.IS_DEBUGGABLE) {
             if (mDebugKey != null) {
                 if (DBG) Slog.w(TAG, "Trying to verify signature using debug key");
-                Signature verifier = Signature.getInstance("SHA256withECDSA");
-                verifier.initVerify(mDebugKey);
-                verifier.update(data);
-                if (verifier.verify(signature)) {
+                if (verifyWithPublicKey(mDebugKey, data, signature)) {
                     Slog.i(TAG, "Verified config using debug key");
+                    mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG;
                     return true;
                 } else {
                     if (DBG) Slog.i(TAG, "Config verification failed using debug key");
@@ -102,8 +117,20 @@
                 Slog.w(TAG, "Debuggable build, but have no debug key");
             }
         }
-        // TODO verify production key.
-        Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION");
-        return false;
+        if (mProdKey ==  null) {
+            Slog.e(TAG, "No prod key; construction failed?");
+            mEvent.status =
+                    StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT;
+            return false;
+        }
+        if (verifyWithPublicKey(mProdKey, data, signature)) {
+            Slog.i(TAG, "Verified config using production key");
+            mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__PRODUCTION;
+            return true;
+        } else {
+            if (DBG) Slog.i(TAG, "Verification failed using production key");
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED;
+            return false;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
new file mode 100644
index 0000000..2f2062c
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.signedconfig;
+
+import android.util.StatsLog;
+
+/**
+ * Helper class to allow a SignedConfigReported event to be built up in stages.
+ */
+public class SignedConfigEvent {
+
+    public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE;
+    public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS;
+    public int version = 0;
+    public String fromPackage = null;
+    public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY;
+
+    /**
+     * Write this event to statslog.
+     */
+    public void send() {
+        StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED,
+                type, status, version, fromPackage, verifiedWith);
+    }
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 6bcee14..dc39542 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -26,6 +26,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.server.LocalServices;
 
@@ -82,21 +83,30 @@
         }
         if (metaData.containsKey(KEY_GLOBAL_SETTINGS)
                 && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) {
-            String config = metaData.getString(KEY_GLOBAL_SETTINGS);
-            String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
+            SignedConfigEvent event = new SignedConfigEvent();
             try {
-                // Base64 encoding is standard (not URL safe) encoding: RFC4648
-                config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
-            } catch (IllegalArgumentException iae) {
-                Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName);
-                return;
+                event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS;
+                event.fromPackage = packageName;
+                String config = metaData.getString(KEY_GLOBAL_SETTINGS);
+                String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
+                try {
+                    // Base64 encoding is standard (not URL safe) encoding: RFC4648
+                    config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
+                } catch (IllegalArgumentException iae) {
+                    Slog.e(TAG, "Failed to base64 decode global settings config from "
+                            + packageName);
+                    event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG;
+                    return;
+                }
+                if (DBG) {
+                    Slog.d(TAG, "Got global settings config: " + config);
+                    Slog.d(TAG, "Got global settings signature: " + signature);
+                }
+                new GlobalSettingsConfigApplicator(mContext, packageName, event).applyConfig(
+                        config, signature);
+            } finally {
+                event.send();
             }
-            if (DBG) {
-                Slog.d(TAG, "Got global settings config: " + config);
-                Slog.d(TAG, "Got global settings signature: " + signature);
-            }
-            new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig(
-                    config, signature);
         } else {
             if (DBG) Slog.d(TAG, "Package has no global settings config/signature.");
         }
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index 315d5e3..1d1c28f 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -175,18 +175,24 @@
                 handlePersist();
             }
             for (String file : new File(mSliceDir.getAbsolutePath()).list()) {
-                if (file.isEmpty()) continue;
                 try (ParserHolder parser = getParser(file)) {
-                    Persistable p;
-                    while (parser.parser.getEventType() != XmlPullParser.START_TAG) {
+                    Persistable p = null;
+                    while (parser.parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if (parser.parser.getEventType() == XmlPullParser.START_TAG) {
+                            if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
+                                p = SliceClientPermissions.createFrom(parser.parser, tracker);
+                            } else {
+                                p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+                            }
+                            break;
+                        }
                         parser.parser.next();
                     }
-                    if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
-                        p = SliceClientPermissions.createFrom(parser.parser, tracker);
+                    if (p != null) {
+                        p.writeTo(out);
                     } else {
-                        p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+                        Slog.w(TAG, "Invalid or empty slice permissions file: " + file);
                     }
-                    p.writeTo(out);
                 }
             }
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 30aa528..4e71a05 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -43,6 +43,7 @@
 import android.content.pm.UserInfo;
 import android.hardware.fingerprint.FingerprintManager;
 import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
 import android.net.Network;
 import android.net.NetworkRequest;
 import android.net.NetworkStats;
@@ -90,7 +91,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.IProcessStats;
 import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
@@ -210,6 +210,7 @@
 
     private final Context mContext;
     private final AlarmManager mAlarmManager;
+    private final INetworkStatsService mNetworkStatsService;
     @GuardedBy("sStatsdLock")
     private static IStatsManager sStatsd;
     private static final Object sStatsdLock = new Object();
@@ -257,6 +258,8 @@
         super();
         mContext = context;
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mNetworkStatsService = INetworkStatsService.Stub.asInterface(
+              ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         mBaseDir.mkdirs();
         mAppUpdateReceiver = new AppUpdateReceiver();
         mUserUpdateReceiver = new BroadcastReceiver() {
@@ -788,14 +791,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             // Combine all the metrics per Uid into one record.
-            NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null)
-                            .groupedByUid();
+            NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -812,12 +815,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null));
+                    mNetworkStatsService.getDetailedUidStats(ifaces));
             addNetworkStats(tagId, pulledData, stats, true);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -834,14 +839,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             // Combine all the metrics per Uid into one record.
-            NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null)
-                            .groupedByUid();
+            NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -874,12 +879,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null));
+                    mNetworkStatsService.getDetailedUidStats(ifaces));
             addNetworkStats(tagId, pulledData, stats, true);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -2058,6 +2065,17 @@
         mContext.unregisterReceiver(mShutdownEventReceiver);
         cancelAnomalyAlarm();
         cancelPullingAlarm();
+
+        BinderCallsStatsService.Internal binderStats =
+                LocalServices.getService(BinderCallsStatsService.Internal.class);
+        if (binderStats != null) {
+            binderStats.reset();
+        }
+
+        LooperStats looperStats = LocalServices.getService(LooperStats.class);
+        if (looperStats != null) {
+            looperStats.reset();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index fc21adb..7c1e619 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -598,11 +598,11 @@
     }
 
     @Override
-    public void onBiometricAuthenticated() {
+    public void onBiometricAuthenticated(boolean authenticated) {
         enforceBiometricDialog();
         if (mBar != null) {
             try {
-                mBar.onBiometricAuthenticated();
+                mBar.onBiometricAuthenticated(authenticated);
             } catch (RemoteException ex) {
             }
         }
@@ -641,17 +641,6 @@
         }
     }
 
-    @Override
-    public void showBiometricTryAgain() {
-        enforceBiometricDialog();
-        if (mBar != null) {
-            try {
-                mBar.showBiometricTryAgain();
-            } catch (RemoteException ex) {
-            }
-        }
-    }
-
     // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
     public void disable(int what, IBinder token, String pkg) {
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index f7cc443..2700f9d 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -24,7 +24,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.TrafficStats;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileObserver;
@@ -42,13 +41,13 @@
 import android.util.ArrayMap;
 import android.util.DataUnit;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.EventLogTags;
-import com.android.server.IoThread;
 import com.android.server.SystemService;
 import com.android.server.pm.InstructionSets;
 import com.android.server.pm.PackageManagerService;
@@ -499,9 +498,15 @@
             notification.flags |= Notification.FLAG_NO_CLEAR;
             mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
                     notification, UserHandle.ALL);
+            StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED,
+                    Objects.toString(vol.getDescription()),
+                    StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON);
         } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
             mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
                     UserHandle.ALL);
+            StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED,
+                    Objects.toString(vol.getDescription()),
+                    StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF);
         }
     }
 
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 2cab63a..ef77140 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -510,7 +510,8 @@
                 Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
                 willBind = mContext.bindServiceAsUser(
                         serviceIntent, mConnection,
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+                                | Context.BIND_RESTRICT_ASSOCIATIONS,
                         UserHandle.of(mUserId));
                 mBinding = willBind;
             } finally {
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
index 65d5b10..d4aa59d 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -16,7 +16,7 @@
 
 package com.android.server.textservices;
 
-import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -43,6 +43,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.inputmethod.InputMethodSystemProperty;
 import android.view.textservice.SpellCheckerInfo;
 import android.view.textservice.SpellCheckerSubtype;
 
@@ -334,7 +335,7 @@
         mContext = context;
         mUserManager = mContext.getSystemService(UserManager.class);
         mSpellCheckerOwnerUserIdMap = new LazyIntToIntMap(callingUserId -> {
-            if (DISABLE_PER_PROFILE_SPELL_CHECKER) {
+            if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
                 final long token = Binder.clearCallingIdentity();
                 try {
                     final UserInfo parent = mUserManager.getProfileParent(callingUserId);
@@ -355,7 +356,7 @@
     private void initializeInternalStateLocked(@UserIdInt int userId) {
         // When DISABLE_PER_PROFILE_SPELL_CHECKER is true, we make sure here that work profile users
         // will never have non-null TextServicesData for their user ID.
-        if (DISABLE_PER_PROFILE_SPELL_CHECKER
+        if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
                 && userId != mSpellCheckerOwnerUserIdMap.get(userId)) {
             return;
         }
@@ -514,8 +515,8 @@
     // TODO: Save SpellCheckerService by supported languages. Currently only one spell
     // checker is saved.
     @Override
-    public SpellCheckerInfo getCurrentSpellChecker(String locale) {
-        int userId = UserHandle.getCallingUserId();
+    public SpellCheckerInfo getCurrentSpellChecker(@UserIdInt int userId, String locale) {
+        verifyUser(userId);
         synchronized (mLock) {
             final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
             if (tsd == null) return null;
@@ -528,11 +529,12 @@
     // TODO: Save SpellCheckerSubtype by supported languages by looking at "locale".
     @Override
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
-            boolean allowImplicitlySelectedSubtype) {
+            @UserIdInt int userId, boolean allowImplicitlySelectedSubtype) {
+        verifyUser(userId);
+
         final int subtypeHashCode;
         final SpellCheckerInfo sci;
         final Locale systemLocale;
-        final int userId = UserHandle.getCallingUserId();
 
         synchronized (mLock) {
             final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
@@ -592,17 +594,17 @@
     }
 
     @Override
-    public void getSpellCheckerService(String sciId, String locale,
+    public void getSpellCheckerService(@UserIdInt int userId, String sciId, String locale,
             ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
             Bundle bundle) {
+        verifyUser(userId);
         if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) {
             Slog.e(TAG, "getSpellCheckerService: Invalid input.");
             return;
         }
-        int callingUserId = UserHandle.getCallingUserId();
 
         synchronized (mLock) {
-            final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId);
+            final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
             if (tsd == null) return;
 
             HashMap<String, SpellCheckerInfo> spellCheckerMap = tsd.mSpellCheckerMap;
@@ -635,8 +637,8 @@
     }
 
     @Override
-    public boolean isSpellCheckerEnabled() {
-        int userId = UserHandle.getCallingUserId();
+    public boolean isSpellCheckerEnabled(@UserIdInt int userId) {
+        verifyUser(userId);
 
         synchronized (mLock) {
             final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
@@ -672,11 +674,11 @@
     }
 
     @Override
-    public SpellCheckerInfo[] getEnabledSpellCheckers() {
-        int callingUserId = UserHandle.getCallingUserId();
+    public SpellCheckerInfo[] getEnabledSpellCheckers(@UserIdInt int userId) {
+        verifyUser(userId);
 
         synchronized (mLock) {
-            final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId);
+            final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
             if (tsd == null) return null;
 
             ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList;
@@ -692,11 +694,12 @@
     }
 
     @Override
-    public void finishSpellCheckerService(ISpellCheckerSessionListener listener) {
+    public void finishSpellCheckerService(@UserIdInt int userId,
+            ISpellCheckerSessionListener listener) {
         if (DBG) {
             Slog.d(TAG, "FinishSpellCheckerService");
         }
-        int userId = UserHandle.getCallingUserId();
+        verifyUser(userId);
 
         synchronized (mLock) {
             final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
@@ -717,6 +720,15 @@
         }
     }
 
+    private void verifyUser(@UserIdInt int userId) {
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (userId != callingUserId) {
+            mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL,
+                    "Cross-user interaction requires INTERACT_ACROSS_USERS_FULL. userId=" + userId
+                            + " callingUserId=" + callingUserId);
+        }
+    }
+
     private void setCurrentSpellCheckerLocked(@Nullable SpellCheckerInfo sci, TextServicesData tsd) {
         final String sciId = (sci != null) ? sci.getId() : "";
         if (DBG) {
@@ -780,7 +792,7 @@
     private TextServicesData getDataFromCallingUserIdLocked(@UserIdInt int callingUserId) {
         final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(callingUserId);
         final TextServicesData data = mUserData.get(spellCheckerOwnerUserId);
-        if (DISABLE_PER_PROFILE_SPELL_CHECKER) {
+        if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
             if (spellCheckerOwnerUserId != callingUserId) {
                 // Calling process is running under child profile.
                 if (data == null) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index f008770..fcc8284 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -84,11 +84,13 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.EventLog;
+import android.util.FeatureFlagUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
 import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.IWindowManager;
 
 import com.android.internal.R;
@@ -350,12 +352,34 @@
     }
 
     private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
+        if (wallpaper.connection != null) {
+            wallpaper.connection.forEachDisplayConnector(connector -> {
+                notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId);
+            });
+        } else { // Lock wallpaper does not have WallpaperConnection.
+            notifyWallpaperColorsChangedOnDisplay(wallpaper, which, DEFAULT_DISPLAY);
+        }
+    }
+
+    private RemoteCallbackList<IWallpaperManagerCallback> getWallpaperCallbacks(int userId,
+            int displayId) {
+        RemoteCallbackList<IWallpaperManagerCallback> listeners = null;
+        final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> displayListeners =
+                mColorsChangedListeners.get(userId);
+        if (displayListeners != null) {
+            listeners = displayListeners.get(displayId);
+        }
+        return listeners;
+    }
+
+    private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper, int which,
+            int displayId) {
         boolean needsExtraction;
         synchronized (mLock) {
             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
-                    mColorsChangedListeners.get(wallpaper.userId);
+                    getWallpaperCallbacks(wallpaper.userId, displayId);
             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
-                    mColorsChangedListeners.get(UserHandle.USER_ALL);
+                    getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
             // No-op until someone is listening to it.
             if (emptyCallbackList(currentUserColorListeners)  &&
                     emptyCallbackList(userAllColorListeners)) {
@@ -363,7 +387,7 @@
             }
 
             if (DEBUG) {
-                Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
+                Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
             }
 
             needsExtraction = wallpaper.primaryColors == null;
@@ -371,7 +395,7 @@
 
         // Let's notify the current values, it's fine if it's null, it just means
         // that we don't know yet.
-        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
 
         if (needsExtraction) {
             extractColors(wallpaper);
@@ -381,7 +405,7 @@
                     return;
                 }
             }
-            notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+            notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
         }
     }
 
@@ -390,14 +414,14 @@
     }
 
     private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
-            int userId) {
+            int userId, int displayId) {
         final IWallpaperManagerCallback keyguardListener;
         final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
         synchronized (mLock) {
             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
-                    mColorsChangedListeners.get(userId);
+                    getWallpaperCallbacks(userId, displayId);
             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
-                    mColorsChangedListeners.get(UserHandle.USER_ALL);
+                    getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
             keyguardListener = mKeyguardListener;
 
             if (currentUserColorListeners != null) {
@@ -427,7 +451,8 @@
             }
         }
 
-        if (keyguardListener != null) {
+        // Only shows Keyguard on default display
+        if (keyguardListener != null && displayId == DEFAULT_DISPLAY) {
             try {
                 keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
             } catch (RemoteException e) {
@@ -446,6 +471,11 @@
         String cropFile = null;
         int wallpaperId;
 
+        if (wallpaper.equals(mFallbackWallpaper)) {
+            extractDefaultImageWallpaperColors();
+            return;
+        }
+
         synchronized (mLock) {
             // Not having a wallpaperComponent means it's a lock screen wallpaper.
             final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
@@ -482,6 +512,39 @@
         }
     }
 
+    private void extractDefaultImageWallpaperColors() {
+        synchronized (mLock) {
+            if (mFallbackWallpaper.primaryColors != null) return;
+        }
+
+        if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
+        WallpaperColors colors = null;
+        final InputStream is = WallpaperManager.openDefaultWallpaper(mContext, FLAG_SYSTEM);
+        if (is != null) {
+            try {
+                final BitmapFactory.Options options = new BitmapFactory.Options();
+                final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+                if (bitmap != null) {
+                    colors = WallpaperColors.fromBitmap(bitmap);
+                    bitmap.recycle();
+                }
+            } catch (OutOfMemoryError e) {
+                Slog.w(TAG, "Can't decode default wallpaper stream", e);
+            } finally {
+                IoUtils.closeQuietly(is);
+            }
+        }
+
+        if (colors == null) {
+            Slog.e(TAG, "Extract default image wallpaper colors failed");
+            return;
+        }
+
+        synchronized (mLock) {
+            mFallbackWallpaper.primaryColors = colors;
+        }
+    }
+
     /**
      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
      * for display.
@@ -539,6 +602,20 @@
             // scale if the crop height winds up not matching the recommended metrics
             needScale = (wpData.mHeight != cropHint.height());
 
+            //make sure screen aspect ratio is preserved if width is scaled under screen size
+            if (needScale) {
+                final DisplayInfo displayInfo = new DisplayInfo();
+                mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
+                final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
+                final int newWidth = (int) (cropHint.width() * scaleByHeight);
+                if (newWidth < displayInfo.logicalWidth) {
+                    final float screenAspectRatio =
+                            (float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
+                    cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
+                    needCrop = true;
+                }
+            }
+
             if (DEBUG) {
                 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
                 Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
@@ -696,6 +773,11 @@
                     targetWallpaper.connection.removeDisplayConnector(displayId);
                     removeDisplayData(displayId);
                 }
+                for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
+                    final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
+                            mColorsChangedListeners.valueAt(i);
+                    callbacks.delete(displayId);
+                }
             }
         }
 
@@ -706,9 +788,11 @@
 
     /**
      * Map of color listeners per user id.
-     * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+     * The first key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+     * The secondary key will be the display id, which means which display the listener is
+     * interested in.
      */
-    private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+    private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>>
             mColorsChangedListeners;
     private WallpaperData mLastWallpaper;
     private IWallpaperManagerCallback mKeyguardListener;
@@ -1228,9 +1312,10 @@
         /**
          * Called by a live wallpaper if its colors have changed.
          * @param primaryColors representation of wallpaper primary colors
+         * @param displayId for which display
          */
         @Override
-        public void onWallpaperColorsChanged(WallpaperColors primaryColors) {
+        public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) {
             int which;
             synchronized (mLock) {
                 // Do not broadcast changes on ImageWallpaper since it's handled
@@ -1243,14 +1328,16 @@
 
                 // Live wallpapers always are system wallpapers.
                 which = FLAG_SYSTEM;
-                // It's also the lock screen wallpaper when we don't have a bitmap in there
-                WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
-                if (lockedWallpaper == null) {
-                    which |= FLAG_LOCK;
+                // It's also the lock screen wallpaper when we don't have a bitmap in there.
+                if (displayId == DEFAULT_DISPLAY) {
+                    final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
+                    if (lockedWallpaper == null) {
+                        which |= FLAG_LOCK;
+                    }
                 }
             }
             if (which != 0) {
-                notifyWallpaperColorsChanged(mWallpaper, which);
+                notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId);
             }
         }
 
@@ -1277,16 +1364,12 @@
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
                 }
-                // TODO(multi-display) So far, we have shared the same wallpaper on each display.
-                // Once we have multiple wallpapers on multiple displays, please complete here.
-                if (displayId == DEFAULT_DISPLAY) {
-                    try {
-                        // This will trigger onComputeColors in the wallpaper engine.
-                        // It's fine to be locked in here since the binder is oneway.
-                        connector.mEngine.requestWallpaperColors();
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to request wallpaper colors", e);
-                    }
+                try {
+                    // This will trigger onComputeColors in the wallpaper engine.
+                    // It's fine to be locked in here since the binder is oneway.
+                    connector.mEngine.requestWallpaperColors();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to request wallpaper colors", e);
                 }
             }
         }
@@ -1681,6 +1764,7 @@
         FgThread.getHandler().post(() -> {
             notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
             notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         });
     }
 
@@ -1743,6 +1827,7 @@
         // When clearing a wallpaper, broadcast new valid colors
         if (data != null) {
             notifyWallpaperColorsChanged(data, which);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         }
     }
 
@@ -2084,35 +2169,47 @@
     }
 
     @Override
-    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+            int displayId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, true, "registerWallpaperColorsCallback", null);
         synchronized (mLock) {
-            RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
-                    mColorsChangedListeners.get(userId);
-            if (userColorsChangedListeners == null) {
-                userColorsChangedListeners = new RemoteCallbackList<>();
-                mColorsChangedListeners.put(userId, userColorsChangedListeners);
+            SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+                    userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+            if (userDisplayColorsChangedListeners == null) {
+                userDisplayColorsChangedListeners = new SparseArray<>();
+                mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners);
             }
-            userColorsChangedListeners.register(cb);
+            RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+                    userDisplayColorsChangedListeners.get(displayId);
+            if (displayChangedListeners == null) {
+                displayChangedListeners = new RemoteCallbackList<>();
+                userDisplayColorsChangedListeners.put(displayId, displayChangedListeners);
+            }
+            displayChangedListeners.register(cb);
         }
     }
 
     @Override
-    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+            int displayId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, true, "unregisterWallpaperColorsCallback", null);
         synchronized (mLock) {
-            final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
-                    mColorsChangedListeners.get(userId);
-            if (userColorsChangedListeners != null) {
-                userColorsChangedListeners.unregister(cb);
+            SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+                    userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+            if (userDisplayColorsChangedListeners != null) {
+                RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+                        userDisplayColorsChangedListeners.get(displayId);
+                if (displayChangedListeners != null) {
+                    displayChangedListeners.unregister(cb);
+                }
             }
         }
     }
 
     /**
-     * TODO(b/115486823) Extends this method with specific display.
+     * TODO(multi-display) Extends this method with specific display.
      * Propagate ambient state to wallpaper engine.
      *
      * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
@@ -2123,9 +2220,13 @@
         synchronized (mLock) {
             mInAmbientMode = inAmbientMode;
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
-            if (data != null && data.connection != null && data.connection.mInfo != null
-                    && data.connection.mInfo.supportsAmbientMode()) {
-                // TODO(b/115486823) Extends this method with specific display.
+            final boolean hasConnection = data != null && data.connection != null;
+            final WallpaperInfo info = hasConnection ? data.connection.mInfo : null;
+
+            // The wallpaper info is null for image wallpaper, also use the engine in this case.
+            if (hasConnection && (info == null && isAodImageWallpaperEnabled()
+                    || info != null && info.supportsAmbientMode())) {
+                // TODO(multi-display) Extends this method with specific display.
                 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
             } else {
                 engine = null;
@@ -2141,6 +2242,10 @@
         }
     }
 
+    private boolean isAodImageWallpaperEnabled() {
+        return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED);
+    }
+
     @Override
     public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
         checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
@@ -2151,7 +2256,8 @@
     }
 
     @Override
-    public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException {
+    public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
+            throws RemoteException {
         if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
             throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
         }
@@ -2169,7 +2275,7 @@
             // Try to get the system wallpaper anyway since it might
             // also be the lock screen wallpaper
             if (wallpaperData == null) {
-                wallpaperData = mWallpaperMap.get(userId);
+                wallpaperData = findWallpaperAtDisplay(userId, displayId);
             }
 
             if (wallpaperData == null) {
@@ -2187,6 +2293,15 @@
         }
     }
 
+    private WallpaperData findWallpaperAtDisplay(int userId, int displayId) {
+        if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
+                && mFallbackWallpaper.connection.containsDisplay(displayId)) {
+            return mFallbackWallpaper;
+        } else {
+            return mWallpaperMap.get(userId);
+        }
+    }
+
     @Override
     public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
             Rect cropHint, boolean allowBackup, Bundle extras, int which,
@@ -2382,6 +2497,7 @@
 
         if (shouldNotifyColors) {
             notifyWallpaperColorsChanged(wallpaper, which);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index fe0b5c2..caebf15 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1066,93 +1066,19 @@
 
                 final int visibleWindowCount = visibleWindows.size();
                 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
-                for (int i = visibleWindowCount - 1; i >= 0; i--) {
+
+                // Iterate until we figure out what is touchable for the entire screen.
+                for (int i = visibleWindowCount - 1; i >= 0 && !unaccountedSpace.isEmpty(); i--) {
                     final WindowState windowState = visibleWindows.valueAt(i);
-                    final int flags = windowState.mAttrs.flags;
-                    final Task task = windowState.getTask();
 
-                    // If the window is part of a task that we're finished with - ignore.
-                    if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
-                        continue;
-                    }
-
-                    // Ignore non-touchable windows, except the split-screen divider, which is
-                    // occasionally non-touchable but still useful for identifying split-screen
-                    // mode.
-                    if (((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
-                            && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
-                        continue;
-                    }
-
-                    // Compute the bounds in the screen.
                     final Rect boundsInScreen = mTempRect;
                     computeWindowBoundsInScreen(windowState, boundsInScreen);
 
-                    // If the window is completely covered by other windows - ignore.
-                    if (unaccountedSpace.quickReject(boundsInScreen)) {
-                        continue;
-                    }
-
-                    // Add windows of certain types not covered by modal windows.
-                    if (isReportedWindowType(windowState.mAttrs.type)) {
-                        // Add the window to the ones to be reported.
+                    if (windowMattersToAccessibility(windowState, boundsInScreen, unaccountedSpace,
+                            skipRemainingWindowsForTasks)) {
                         addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows);
-                        if (windowState.isFocused()) {
-                            focusedWindowAdded = true;
-                        }
-                    }
-
-                    if (windowState.mAttrs.type !=
-                            WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
-
-                        // Account for the space this window takes if the window
-                        // is not an accessibility overlay which does not change
-                        // the reported windows.
-                        unaccountedSpace.op(boundsInScreen, unaccountedSpace,
-                                Region.Op.REVERSE_DIFFERENCE);
-
-                        // If a window is modal it prevents other windows from being touched
-                        if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
-                            // Account for all space in the task, whether the windows in it are
-                            // touchable or not. The modal window blocks all touches from the task's
-                            // area.
-                            unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
-                                    Region.Op.REVERSE_DIFFERENCE);
-
-                            if (task != null) {
-                                // If the window is associated with a particular task, we can skip the
-                                // rest of the windows for that task.
-                                skipRemainingWindowsForTasks.add(task.mTaskId);
-                                continue;
-                            } else {
-                                // If the window is not associated with a particular task, then it is
-                                // globally modal. In this case we can skip all remaining windows.
-                                break;
-                            }
-                        }
-                    }
-
-                    // We figured out what is touchable for the entire screen - done.
-                    if (unaccountedSpace.isEmpty()) {
-                        break;
-                    }
-                }
-
-                // Always report the focused window.
-                if (!focusedWindowAdded) {
-                    for (int i = visibleWindowCount - 1; i >= 0; i--) {
-                        WindowState windowState = visibleWindows.valueAt(i);
-                        if (windowState.isFocused()) {
-                            // Compute the bounds in the screen.
-                            Rect boundsInScreen = mTempRect;
-                            computeWindowBoundsInScreen(windowState, boundsInScreen);
-
-                            // Add the window to the ones to be reported.
-                            addPopulatedWindowInfo(
-                                    windowState, boundsInScreen, windows, addedWindows);
-                            break;
-                        }
+                        updateUnaccountedSpace(windowState, boundsInScreen, unaccountedSpace,
+                                skipRemainingWindowsForTasks);
                     }
                 }
 
@@ -1221,6 +1147,73 @@
             clearAndRecycleWindows(windows);
         }
 
+        private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen,
+                Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+            if (windowState.isFocused()) {
+                return true;
+            }
+
+            // If the window is part of a task that we're finished with - ignore.
+            final Task task = windowState.getTask();
+            if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
+                return false;
+            }
+
+            // Ignore non-touchable windows, except the split-screen divider, which is
+            // occasionally non-touchable but still useful for identifying split-screen
+            // mode.
+            if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+                    && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
+                return false;
+            }
+
+            // If the window is completely covered by other windows - ignore.
+            if (unaccountedSpace.quickReject(boundsInScreen)) {
+                return false;
+            }
+
+            // Add windows of certain types not covered by modal windows.
+            if (isReportedWindowType(windowState.mAttrs.type)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        private void updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen,
+                Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+            if (windowState.mAttrs.type
+                    != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+
+                // Account for the space this window takes if the window
+                // is not an accessibility overlay which does not change
+                // the reported windows.
+                unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+                        Region.Op.REVERSE_DIFFERENCE);
+
+                // If a window is modal it prevents other windows from being touched
+                if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+                    // Account for all space in the task, whether the windows in it are
+                    // touchable or not. The modal window blocks all touches from the task's
+                    // area.
+                    unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+                            Region.Op.REVERSE_DIFFERENCE);
+
+                    final Task task = windowState.getTask();
+                    if (task != null) {
+                        // If the window is associated with a particular task, we can skip the
+                        // rest of the windows for that task.
+                        skipRemainingWindowsForTasks.add(task.mTaskId);
+                    } else {
+                        // If the window is not associated with a particular task, then it is
+                        // globally modal. In this case we can skip all remaining windows.
+                        unaccountedSpace.setEmpty();
+                    }
+                }
+            }
+        }
+
         private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
             // Get the touchable frame.
             Region touchableRegion = mTempRegion1;
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 1f638c7..65d66f4 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -278,12 +278,12 @@
         }
 
         // Since positionChildAt() is called during the creation process of pinned stacks,
-        // ActivityStack#getWindowContainerController() can be null. In this special case,
+        // ActivityStack#getStack() can be null. In this special case,
         // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
         // we don't have to call WindowContainerController#positionChildAt() here.
-        if (stack.getWindowContainerController() != null && mDisplayContent != null) {
+        if (stack.getTaskStack() != null && mDisplayContent != null) {
             mDisplayContent.positionStackAt(insertPosition,
-                    stack.getWindowContainerController().mContainer, includingParents);
+                    stack.getTaskStack(), includingParents);
         }
         if (!wasContained) {
             stack.setParent(this);
@@ -450,13 +450,12 @@
     @VisibleForTesting
     <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
             int stackId, boolean onTop) {
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            return (T) new PinnedActivityStack(this, stackId,
-                    mRootActivityContainer.mStackSupervisor, onTop);
+        if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
+            throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
+                    + "activity type.");
         }
         return (T) new ActivityStack(this, stackId,
-                mRootActivityContainer.mStackSupervisor, windowingMode, activityType,
-                onTop);
+                mRootActivityContainer.mStackSupervisor, windowingMode, activityType, onTop);
     }
 
     /**
@@ -626,6 +625,10 @@
             return;
         }
 
+        // Collect the stacks that are necessary to be removed instead of performing the removal
+        // by looping mStacks, so that we don't miss any stacks after the stack size changed or
+        // stacks reordered.
+        final ArrayList<ActivityStack> stacks = new ArrayList<>();
         for (int j = windowingModes.length - 1 ; j >= 0; --j) {
             final int windowingMode = windowingModes[j];
             for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -636,9 +639,13 @@
                 if (stack.getWindowingMode() != windowingMode) {
                     continue;
                 }
-                mRootActivityContainer.mStackSupervisor.removeStack(stack);
+                stacks.add(stack);
             }
         }
+
+        for (int i = stacks.size() - 1; i >= 0; --i) {
+            mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i));
+        }
     }
 
     void removeStacksWithActivityTypes(int... activityTypes) {
@@ -646,15 +653,23 @@
             return;
         }
 
+        // Collect the stacks that are necessary to be removed instead of performing the removal
+        // by looping mStacks, so that we don't miss any stacks after the stack size changed or
+        // stacks reordered.
+        final ArrayList<ActivityStack> stacks = new ArrayList<>();
         for (int j = activityTypes.length - 1 ; j >= 0; --j) {
             final int activityType = activityTypes[j];
             for (int i = mStacks.size() - 1; i >= 0; --i) {
                 final ActivityStack stack = mStacks.get(i);
                 if (stack.getActivityType() == activityType) {
-                    mRootActivityContainer.mStackSupervisor.removeStack(stack);
+                    stacks.add(stack);
                 }
             }
         }
+
+        for (int i = stacks.size() - 1; i >= 0; --i) {
+            mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i));
+        }
     }
 
     void onStackWindowingModeChanged(ActivityStack stack) {
@@ -1019,8 +1034,8 @@
         return mSplitScreenPrimaryStack != null;
     }
 
-    PinnedActivityStack getPinnedStack() {
-        return (PinnedActivityStack) mPinnedStack;
+    ActivityStack getPinnedStack() {
+        return mPinnedStack;
     }
 
     boolean hasPinnedStack() {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8884615..b8634d8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -54,6 +54,7 @@
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
 import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
+import static android.content.pm.ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
 import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS;
 import static android.content.pm.ActivityInfo.FLAG_NO_HISTORY;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
@@ -142,6 +143,7 @@
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
@@ -387,9 +389,15 @@
     int mRotationAnimationHint = -1;
 
     private boolean mShowWhenLocked;
+    private boolean mInheritShownWhenLocked;
     private boolean mTurnScreenOn;
 
     /**
+     * Current sequencing integer of the configuration, for skipping old activity configurations.
+     */
+    private int mConfigurationSeq;
+
+    /**
      * Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
      */
     private final Configuration mTmpConfig = new Configuration();
@@ -728,9 +736,10 @@
             mLastReportedMultiWindowMode = inPictureInPictureMode;
             final Configuration newConfig = new Configuration();
             if (targetStackBounds != null && !targetStackBounds.isEmpty()) {
-                task.computeResolvedOverrideConfiguration(newConfig,
-                        task.getParent().getConfiguration(),
-                        task.getRequestedOverrideConfiguration());
+                newConfig.setTo(task.getRequestedOverrideConfiguration());
+                Rect outBounds = newConfig.windowConfiguration.getBounds();
+                task.adjustForMinimalTaskDimensions(outBounds, outBounds);
+                task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
             }
             schedulePictureInPictureModeChanged(newConfig);
             scheduleMultiWindowModeChanged(newConfig);
@@ -996,6 +1005,7 @@
                 null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
 
         mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+        mInheritShownWhenLocked = (aInfo.privateFlags & FLAG_INHERIT_SHOW_WHEN_LOCKED) != 0;
         mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
 
         mRotationAnimationHint = aInfo.rotationAnimation;
@@ -1481,14 +1491,6 @@
         return true;
     }
 
-    /**
-     * @return true if the activity contains windows that have
-     *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
-     */
-    boolean hasDismissKeyguardWindows() {
-        return mAtmService.mWindowManager.containsDismissKeyguardWindow(appToken);
-    }
-
     void makeFinishingLocked() {
         if (finishing) {
             return;
@@ -2503,8 +2505,16 @@
             return;
         }
 
-        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+        final IBinder binder =
+                (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null;
         mAppWindowToken.setOrientation(requestedOrientation, binder, this);
+
+        // Push the new configuration to the requested app in case where it's not pushed, e.g. when
+        // the request is handled at task level with letterbox.
+        if (!getMergedOverrideConfiguration().equals(
+                mLastReportedConfiguration.getMergedConfiguration())) {
+            ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+        }
     }
 
     int getOrientation() {
@@ -2547,7 +2557,6 @@
 
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
     private void updateOverrideConfiguration() {
-        mTmpConfig.unset();
         computeBounds(mTmpBounds);
 
         if (mTmpBounds.equals(getRequestedOverrideBounds())) {
@@ -2558,13 +2567,54 @@
 
         // Bounds changed...update configuration to match.
         if (!matchParentBounds()) {
-            task.computeResolvedOverrideConfiguration(mTmpConfig,
-                    task.getParent().getConfiguration(), getRequestedOverrideConfiguration());
+            mTmpConfig.setTo(getRequestedOverrideConfiguration());
+            task.computeConfigResourceOverrides(mTmpConfig, task.getParent().getConfiguration());
+        } else {
+            mTmpConfig.unset();
         }
 
         onRequestedOverrideConfigurationChanged(mTmpConfig);
     }
 
+    @Override
+    void resolveOverrideConfiguration(Configuration newParentConfiguration) {
+        super.resolveOverrideConfiguration(newParentConfiguration);
+
+        // Assign configuration sequence number into hierarchy because there is a different way than
+        // ensureActivityConfiguration() in this class that uses configuration in WindowState during
+        // layout traversals.
+        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+        getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newParentConfig) {
+        super.onConfigurationChanged(newParentConfig);
+
+        // Configuration's equality doesn't consider seq so if only seq number changes in resolved
+        // override configuration. Therefore ConfigurationContainer doesn't change merged override
+        // configuration, but it's used to push configuration changes so explicitly update that.
+        if (getMergedOverrideConfiguration().seq != getResolvedOverrideConfiguration().seq) {
+            onMergedOverrideConfigurationChanged();
+        }
+
+        // TODO(b/80414790): Remove code below after unification.
+        // Same as above it doesn't notify configuration listeners, and consequently AppWindowToken
+        // can't get updated seq number. However WindowState's merged override configuration needs
+        // to have this seq number because that's also used for activity config pushes during layout
+        // traversal. Therefore explicitly update them here.
+        if (mAppWindowToken == null) {
+            return;
+        }
+        final Configuration appWindowTokenRequestedOverrideConfig =
+                mAppWindowToken.getRequestedOverrideConfiguration();
+        if (appWindowTokenRequestedOverrideConfig.seq != getResolvedOverrideConfiguration().seq) {
+            appWindowTokenRequestedOverrideConfig.seq =
+                    getResolvedOverrideConfiguration().seq;
+            mAppWindowToken.onMergedOverrideConfigurationChanged();
+        }
+    }
+
     /** Returns true if the configuration is compatible with this activity. */
     boolean isConfigurationCompatible(Configuration config) {
         final int orientation = mAppWindowToken != null
@@ -3177,16 +3227,45 @@
                 false /* preserveWindows */);
     }
 
+    void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
+        mInheritShownWhenLocked = inheritShowWhenLocked;
+        mRootActivityContainer.ensureActivitiesVisible(null, 0, false);
+    }
+
     /**
      * @return true if the activity windowing mode is not
-     *         {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and activity contains
-     *         windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity
-     *         has set {@link #mShowWhenLocked}.
+     *         {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and a) activity
+     *         contains windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the
+     *         activity has set {@link #mShowWhenLocked}, or b) if the activity has set
+     *         {@link #mInheritShownWhenLocked} and the activity behind this satisfies the
+     *         conditions a) above.
      *         Multi-windowing mode will be exited if true is returned.
      */
     boolean canShowWhenLocked() {
-        return !inPinnedWindowingMode() && (mShowWhenLocked
-                || mAtmService.mWindowManager.containsShowWhenLockedWindow(appToken));
+        if (!inPinnedWindowingMode() && (mShowWhenLocked
+                || (mAppWindowToken != null && mAppWindowToken.containsShowWhenLockedWindow()))) {
+            return true;
+        } else if (mInheritShownWhenLocked) {
+            ActivityRecord r = getActivityBelow();
+            return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
+                    || (r.mAppWindowToken != null
+                        && r.mAppWindowToken.containsShowWhenLockedWindow()));
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @return an {@link ActivityRecord} of the activity below this activity, or {@code null} if no
+     * such activity exists.
+     */
+    @Nullable
+    private ActivityRecord getActivityBelow() {
+        final int pos = task.mActivities.indexOf(this);
+        if (pos == -1) {
+            throw new IllegalStateException("Activity not found in its task");
+        }
+        return pos == 0 ? null : task.getChildAt(pos - 1);
     }
 
     void setTurnScreenOn(boolean turnScreenOn) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 4581a0f..891c3da 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -114,6 +114,7 @@
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
+import android.app.RemoteAction;
 import android.app.ResultInfo;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
@@ -173,8 +174,7 @@
 /**
  * State and management of a single stack of activities.
  */
-class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
-        implements StackWindowListener {
+class ActivityStack extends ConfigurationContainer {
     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;
@@ -297,8 +297,7 @@
     static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
 
     final ActivityTaskManagerService mService;
-    private final WindowManagerService mWindowManager;
-    T mWindowContainerController;
+    final WindowManagerService mWindowManager;
 
     /**
      * The back history of all previous (and possibly still
@@ -397,6 +396,9 @@
     static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
     static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
 
+    // TODO: remove after unification.
+    TaskStack mTaskStack;
+
     private static class ScheduleDestroyArgs {
         final WindowProcessController mOwner;
         final String mReason;
@@ -495,21 +497,30 @@
         // stacks on a wrong display.
         mDisplayId = display.mDisplayId;
         setActivityType(activityType);
-        mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
-                mTmpRect2);
+        createTaskStack(display.mDisplayId, onTop, mTmpRect2);
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 true /* creating */);
         display.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
     }
 
-    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mRootActivityContainer.mWindowManager);
+    void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
+        final DisplayContent dc = mWindowManager.mRoot.getDisplayContent(displayId);
+        if (dc == null) {
+            throw new IllegalArgumentException("Trying to add stackId=" + mStackId
+                    + " to unknown displayId=" + displayId);
+        }
+        mTaskStack = new TaskStack(mWindowManager, mStackId, this);
+        dc.setStackOnDisplay(mStackId, onTop, mTaskStack);
+        if (mTaskStack.matchParentBounds()) {
+            outBounds.setEmpty();
+        } else {
+            mTaskStack.getRawBounds(outBounds);
+        }
     }
 
-    T getWindowContainerController() {
-        return mWindowContainerController;
+    TaskStack getTaskStack() {
+        return mTaskStack;
     }
 
     /**
@@ -553,6 +564,9 @@
         if (display == null) {
             return;
         }
+        if (getTaskStack() == null) {
+            return;
+        }
 
         // Update bounds if applicable
         boolean hasNewOverrideBounds = false;
@@ -560,8 +574,7 @@
         if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_PINNED) {
             // Pinned calculation already includes rotation
             mTmpRect2.set(mTmpRect);
-            hasNewOverrideBounds = getWindowContainerController().mContainer
-                            .calculatePinnedBoundsForConfigChange(mTmpRect2);
+            hasNewOverrideBounds = getTaskStack().calculatePinnedBoundsForConfigChange(mTmpRect2);
         } else {
             final int newRotation = getWindowConfiguration().getRotation();
             if (!matchParentBounds()) {
@@ -588,7 +601,7 @@
                             || getRequestedOverrideWindowingMode()
                             == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
                         mTmpRect2.set(mTmpRect);
-                        getWindowContainerController().mContainer
+                        getTaskStack()
                                 .calculateDockedBoundsForConfigChange(newParentConfig, mTmpRect2);
                         hasNewOverrideBounds = true;
                     }
@@ -786,7 +799,11 @@
 
             mTmpRect2.setEmpty();
             if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
-                mWindowContainerController.getRawBounds(mTmpRect2);
+                if (mTaskStack.matchParentBounds()) {
+                    mTmpRect2.setEmpty();
+                } else {
+                    mTaskStack.getRawBounds(mTmpRect2);
+                }
             }
 
             if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
@@ -843,7 +860,12 @@
         // Reparent the window container before we try to update the position when adding it to
         // the new display below
         mTmpRect2.setEmpty();
-        mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
+        if (mTaskStack == null) {
+            // TODO: Remove after unification.
+            Log.w(TAG, "Task stack is not valid when reparenting.");
+        } else {
+            mTaskStack.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
+        }
         setBounds(mTmpRect2.isEmpty() ? null : mTmpRect2);
         activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
         if (!displayRemoved) {
@@ -876,8 +898,10 @@
     /** Removes the stack completely. Also calls WindowManager to do the same on its side. */
     void remove() {
         removeFromDisplay();
-        mWindowContainerController.removeContainer();
-        mWindowContainerController = null;
+        if (mTaskStack != null) {
+            mTaskStack.removeIfPossible();
+            mTaskStack = null;
+        }
         onParentChanged();
     }
 
@@ -890,26 +914,35 @@
      */
     void getStackDockedModeBounds(Rect dockedBounds, Rect currentTempTaskBounds,
             Rect outStackBounds, Rect outTempTaskBounds) {
-        mWindowContainerController.getStackDockedModeBounds(getParent().getConfiguration(),
-                dockedBounds, currentTempTaskBounds,
-                outStackBounds, outTempTaskBounds);
+        if (mTaskStack != null) {
+            mTaskStack.getStackDockedModeBoundsLocked(getParent().getConfiguration(), dockedBounds,
+                    currentTempTaskBounds, outStackBounds, outTempTaskBounds);
+        } else {
+            outStackBounds.setEmpty();
+            outTempTaskBounds.setEmpty();
+        }
     }
 
     void prepareFreezingTaskBounds() {
-        mWindowContainerController.prepareFreezingTaskBounds();
+        if (mTaskStack != null) {
+            // TODO: This cannot be false after unification.
+            mTaskStack.prepareFreezingTaskBounds();
+        }
     }
 
     void getWindowContainerBounds(Rect outBounds) {
-        if (mWindowContainerController != null) {
-            mWindowContainerController.getBounds(outBounds);
+        if (mTaskStack != null) {
+            mTaskStack.getBounds(outBounds);
             return;
         }
         outBounds.setEmpty();
     }
 
     void positionChildWindowContainerAtTop(TaskRecord child) {
-        mWindowContainerController.positionChildAtTop(child.getTask(),
-                true /* includingParents */);
+        if (mTaskStack != null) {
+            // TODO: Remove after unification. This cannot be false after that.
+            mTaskStack.positionChildAtTop(child.getTask(), true /* includingParents */);
+        }
     }
 
     void positionChildWindowContainerAtBottom(TaskRecord child) {
@@ -918,14 +951,27 @@
         // task to bottom, the next focusable stack on the same display should be focused.
         final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
                 child.getStack(), true /* ignoreCurrent */);
-        mWindowContainerController.positionChildAtBottom(child.getTask(),
-                nextFocusableStack == null /* includingParents */);
+        if (mTaskStack != null) {
+            // TODO: Remove after unification. This cannot be false after that.
+            mTaskStack.positionChildAtBottom(child.getTask(),
+                    nextFocusableStack == null /* includingParents */);
+        }
     }
 
     /**
      * Returns whether to defer the scheduling of the multi-window mode.
      */
     boolean deferScheduleMultiWindowModeChanged() {
+        if (inPinnedWindowingMode()) {
+            // For the pinned stack, the deferring of the multi-window mode changed is tied to the
+            // transition animation into picture-in-picture, and is called once the animation
+            // completes, or is interrupted in a way that would leave the stack in a non-fullscreen
+            // state.
+            // @see BoundsAnimationController
+            // @see BoundsAnimationControllerTests
+            if (getTaskStack() == null) return false;
+            return getTaskStack().deferScheduleMultiWindowModeChanged();
+        }
         return false;
     }
 
@@ -2204,7 +2250,8 @@
                 .isKeyguardOrAodShowing(displayId);
         final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
         final boolean showWhenLocked = r.canShowWhenLocked();
-        final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
+        final boolean dismissKeyguard = r.mAppWindowToken != null
+                && r.mAppWindowToken.containsDismissKeyguardWindow();
         if (shouldBeVisible) {
             if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
                 mTopDismissingKeyguardActivity = r;
@@ -2561,7 +2608,9 @@
                 final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
                         && next.canShowWhenLocked();
                 final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
-                        && next.hasDismissKeyguardWindows();
+                        && next.mAppWindowToken != null
+                        && next.mAppWindowToken.containsDismissKeyguardWindow();
+
                 if (canShowWhenLocked || mayDismissKeyguard) {
                     ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                             !PRESERVE_WINDOWS);
@@ -2991,7 +3040,10 @@
         position = getAdjustedPositionForTask(task, position, null /* starting */);
         mTaskHistory.remove(task);
         mTaskHistory.add(position, task);
-        mWindowContainerController.positionChildAt(task.getTask(), position);
+        if (mTaskStack != null) {
+            // TODO: this could not be false after unification.
+            mTaskStack.positionChildAt(task.getTask(), position);
+        }
         updateTaskMovement(task, true);
     }
 
@@ -4906,8 +4958,7 @@
     }
 
     // TODO: Figure-out a way to consolidate with resize() method below.
-    @Override
-    public void requestResize(Rect bounds) {
+    void requestResize(Rect bounds) {
         mService.resizeStack(mStackId, bounds,
                 true /* allowResizeInDockedMode */, false /* preserveWindows */,
                 false /* animate */, -1 /* animationDuration */);
@@ -4945,7 +4996,8 @@
     }
 
     void onPipAnimationEndResize() {
-        mWindowContainerController.onPipAnimationEndResize();
+        if (mTaskStack == null) return;
+        mTaskStack.onPipAnimationEndResize();
     }
 
 
@@ -5491,6 +5543,65 @@
         }
     }
 
+
+    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
+        if (getTaskStack() == null) return null;
+        return getTaskStack().getPictureInPictureBounds(aspectRatio, null /* currentStackBounds */);
+    }
+
+    void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
+            boolean fromFullscreen) {
+        if (!inPinnedWindowingMode()) return;
+        if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
+            mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
+        } else {
+            if (getTaskStack() == null) return;
+            getTaskStack().animateResizePinnedStack(toBounds, sourceHintBounds,
+                    animationDuration, fromFullscreen);
+        }
+    }
+
+    private boolean skipResizeAnimation(boolean toFullscreen) {
+        if (!toFullscreen) {
+            return false;
+        }
+        final Configuration parentConfig = getParent().getConfiguration();
+        final ActivityRecord top = topRunningNonOverlayTaskActivity();
+        return top != null && !top.isConfigurationCompatible(parentConfig);
+    }
+
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        if (getTaskStack() == null) return;
+        getTaskStack().setPictureInPictureAspectRatio(aspectRatio);
+    }
+
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        if (getTaskStack() == null) return;
+        getTaskStack().setPictureInPictureActions(actions);
+    }
+
+    boolean isAnimatingBoundsToFullscreen() {
+        if (getTaskStack() == null) return false;
+        return getTaskStack().isAnimatingBoundsToFullscreen();
+    }
+
+    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+            boolean forceUpdate) {
+        // It is guaranteed that the activities requiring the update will be in the pinned stack at
+        // this point (either reparented before the animation into PiP, or before reparenting after
+        // the animation out of PiP)
+        synchronized (mService.mGlobalLock) {
+            if (!isAttached()) {
+                return;
+            }
+            ArrayList<TaskRecord> tasks = getAllTasks();
+            for (int i = 0; i < tasks.size(); i++) {
+                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
+                        forceUpdate);
+            }
+        }
+    }
+
     public int getStackId() {
         return mStackId;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index d5286b9..3a288ca 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -947,6 +947,7 @@
         final WindowProcessController wpc =
                 mService.getProcessController(r.processName, r.info.applicationInfo.uid);
 
+        boolean knownToBeDead = false;
         if (wpc != null && wpc.hasThread()) {
             try {
                 if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
@@ -965,6 +966,7 @@
 
             // If a dead object exception was thrown -- fall through to
             // restart the application.
+            knownToBeDead = true;
         }
 
         // Suppress transition until the new activity becomes ready, otherwise the keyguard can
@@ -978,7 +980,7 @@
         // ATMS lock held.
         final Message msg = PooledLambda.obtainMessage(
                 ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
-                r.info.applicationInfo, true, "activity", r.intent.getComponent());
+                r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
         mService.mH.sendMessage(msg);
     }
 
@@ -1672,8 +1674,8 @@
     }
 
     void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
-        // TODO(multi-display): Pinned stack display should be passed in.
-        final PinnedActivityStack stack =
+        // TODO(multi-display): The display containing the stack should be passed in.
+        final ActivityStack stack =
                 mRootActivityContainer.getDefaultDisplay().getPinnedStack();
         if (stack == null) {
             Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
@@ -1684,7 +1686,7 @@
         // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
         // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
         // for the AMS lock to be freed. So check and make sure these bounds are still good.
-        final PinnedStackWindowController stackController = stack.getWindowContainerController();
+        final TaskStack stackController = stack.getTaskStack();
         if (stackController.pinnedStackResizeDisallowed()) {
             return;
         }
@@ -1728,15 +1730,14 @@
              * invisible as well and added to the stopping list.  After which we process the
              * stopping list by handling the idle.
              */
-            final PinnedActivityStack pinnedStack = (PinnedActivityStack) stack;
-            pinnedStack.mForceHidden = true;
-            pinnedStack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            pinnedStack.mForceHidden = false;
+            stack.mForceHidden = true;
+            stack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            stack.mForceHidden = false;
             activityIdleInternalLocked(null, false /* fromTimeout */,
                     true /* processPausingActivites */, null /* configuration */);
 
             // Move all the tasks to the bottom of the fullscreen stack
-            moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP);
+            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
         } else {
             for (int i = tasks.size() - 1; i >= 0; i--) {
                 removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
@@ -2640,6 +2641,9 @@
                 try {
                     mService.moveTaskToFrontLocked(task.taskId, 0, options,
                             true /* fromRecents */);
+                    // Apply options to prevent pendingOptions be taken by client to make sure
+                    // the override pending app transition will be applied immediately.
+                    targetActivity.applyOptionsLocked();
                 } finally {
                     mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
                             targetActivity);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b100ecd..916baa0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.Activity.RESULT_CANCELED;
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CANCELED;
@@ -50,6 +51,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -745,8 +747,9 @@
         // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
         // on START_ABORTED
         if (!abort) {
-            abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
-                    callerApp, originatingPendingIntent, allowBackgroundActivityStart);
+            abort |= shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage,
+                    realCallingUid, callerApp, originatingPendingIntent,
+                    allowBackgroundActivityStart, intent);
         }
 
         // Merge the two options bundles, while realCallerOptions takes precedence.
@@ -893,9 +896,10 @@
                 true /* doResume */, checkedOptions, inTask, outActivity);
     }
 
-    private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
-            int realCallingUid, WindowProcessController callerApp,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+    private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
+            final String callingPackage, int realCallingUid, WindowProcessController callerApp,
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
+            Intent intent) {
         if (mService.isBackgroundActivityStartsEnabled()) {
             return false;
         }
@@ -908,19 +912,24 @@
             return false;
         }
         // don't abort if the callingUid is in the foreground or is a persistent system process
-        if (isUidForeground(callingUid) || isUidPersistentSystemProcess(callingUid)) {
+        final boolean isCallingUidForeground = isUidForeground(callingUid);
+        final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
+                callingUid);
+        if (isCallingUidForeground || isCallingUidPersistentSystemProcess) {
             return false;
         }
         // take realCallingUid into consideration
+        final boolean isRealCallingUidForeground = isUidForeground(realCallingUid);
+        final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
+                realCallingUid);
         if (realCallingUid != callingUid) {
             // don't abort if the realCallingUid is in the foreground and callingUid isn't
-            if (isUidForeground(realCallingUid)) {
+            if (isRealCallingUidForeground) {
                 return false;
             }
             // if the realCallingUid is a persistent system process, abort if the IntentSender
             // wasn't whitelisted to start an activity
-            if (isUidPersistentSystemProcess(realCallingUid) && (originatingPendingIntent != null)
-                    && allowBackgroundActivityStart) {
+            if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
                 return false;
             }
         }
@@ -928,11 +937,28 @@
         if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) {
             return false;
         }
+        // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
+        if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+                == PERMISSION_GRANTED) {
+            return false;
+        }
         // don't abort if the caller has the same uid as the recents component
         if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
             return false;
         }
         // anything that has fallen through will currently be aborted
+        Slog.w(TAG, "Blocking background activity start [callingPackage: " + callingPackage
+                + "; callingUid: " + callingUid
+                + "; isCallingUidForeground: " + isCallingUidForeground
+                + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
+                + "; realCallingUid: " + realCallingUid
+                + "; isRealCallingUidForeground: " + isRealCallingUidForeground
+                + "; isRealCallingUidPersistentSystemProcess: "
+                        + isRealCallingUidPersistentSystemProcess
+                + "; originatingPendingIntent: " + originatingPendingIntent
+                + "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+                + "; intent: " + intent
+                + "]");
         // TODO: remove this toast after feature development is done
         mService.mUiHandler.post(() -> {
             Toast.makeText(mService.mContext,
@@ -945,7 +971,7 @@
     /** Returns true if uid has a visible window or its process is in a top state. */
     private boolean isUidForeground(int uid) {
         return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
-            || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
+            || mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
     }
 
     /** Returns true if uid is in a persistent state. */
@@ -968,18 +994,19 @@
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
             final int callingUidProcState = mService.getUidStateLocked(callingUid);
             final boolean callingUidHasAnyVisibleWindow =
-                    mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
+                    mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
             final int realCallingUidProcState = (callingUid == realCallingUid)
                     ? callingUidProcState
                     : mService.getUidStateLocked(realCallingUid);
             final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
                     ? callingUidHasAnyVisibleWindow
-                    : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
+                    : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(
+                            realCallingUid);
             final String targetPackage = (r != null) ? r.packageName : null;
             final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
             final int targetUidProcState = mService.getUidStateLocked(targetUid);
             final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
-                    ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
+                    ? mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(targetUid)
                     : false;
             final String targetWhitelistTag = (targetUid != -1)
                     ? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0f286ce..d8644df 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.AppProtoEnums;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
@@ -479,4 +480,10 @@
     public abstract void setProfilerInfo(ProfilerInfo profilerInfo);
 
     public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry();
+
+    /**
+     * Gets bitmap snapshot of the provided task id.
+     */
+    public abstract ActivityManager.TaskSnapshot getTaskSnapshot(int taskId,
+            boolean reducedResolution);
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index efce379..6a495d4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -82,13 +82,10 @@
 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.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.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -210,6 +207,7 @@
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
+import android.sysprop.DisplayProperties;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.text.format.Time;
@@ -245,7 +243,6 @@
 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;
 import com.android.server.AttributeCache;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -261,6 +258,7 @@
 import com.android.server.am.PendingIntentController;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.UserState;
+import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.pm.UserManagerService;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -639,7 +637,8 @@
         }
     }
 
-    ActivityTaskManagerService(Context context) {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public ActivityTaskManagerService(Context context) {
         mContext = context;
         mFactoryTest = FactoryTest.getMode();
         mSystemThread = ActivityThread.currentActivityThread();
@@ -693,7 +692,7 @@
         final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
 
         // Transfer any global setting for forcing RTL layout, into a System Property
-        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+        DisplayProperties.debug_force_rtl(forceRtl);
 
         final Configuration configuration = new Configuration();
         Settings.System.getConfiguration(resolver, configuration);
@@ -2406,7 +2405,7 @@
         try {
             synchronized (mGlobalLock) {
                 if (animate) {
-                    final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId);
+                    final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                     if (stack == null) {
                         Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
                         return;
@@ -3711,7 +3710,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final PinnedActivityStack stack =
+                final ActivityStack stack =
                         mRootActivityContainer.getDefaultDisplay().getPinnedStack();
                 if (stack == null) {
                     Slog.w(TAG, "dismissPip: pinned stack not found.");
@@ -3833,9 +3832,8 @@
 
         // If we are animating to fullscreen then we have already dispatched the PIP mode
         // changed, so we should reflect that check here as well.
-        final PinnedActivityStack stack = r.getActivityStack();
-        final PinnedStackWindowController windowController = stack.getWindowContainerController();
-        return !windowController.mContainer.isAnimatingBoundsToFullscreen();
+        final TaskStack taskStack = r.getActivityStack().getTaskStack();
+        return !taskStack.isAnimatingBoundsToFullscreen();
     }
 
     @Override
@@ -3869,7 +3867,7 @@
                                 r.pictureInPictureArgs.getSourceRectHint());
                         mRootActivityContainer.moveActivityToPinnedStack(
                                 r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
-                        final PinnedActivityStack stack = r.getActivityStack();
+                        final ActivityStack stack = r.getActivityStack();
                         stack.setPictureInPictureAspectRatio(aspectRatio);
                         stack.setPictureInPictureActions(actions);
                         MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
@@ -3913,7 +3911,7 @@
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP
-                    final PinnedActivityStack stack = r.getActivityStack();
+                    final ActivityStack stack = r.getActivityStack();
                     if (!stack.isAnimatingBoundsToFullscreen()) {
                         stack.setPictureInPictureAspectRatio(
                                 r.pictureInPictureArgs.getAspectRatio());
@@ -4333,6 +4331,22 @@
     }
 
     @Override
+    public void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setInheritShowWhenLocked(inheritShowWhenLocked);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
     public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.isInStackLocked(token);
@@ -7011,5 +7025,12 @@
                 return mStackSupervisor.getActivityMetricsLogger().getLaunchObserverRegistry();
             }
         }
+
+        @Override
+        public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
+            synchronized (mGlobalLock) {
+                return ActivityTaskManagerService.this.getTaskSnapshot(taskId, reducedResolution);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 089640b..5f393ef 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -65,6 +65,8 @@
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
+import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -73,8 +75,6 @@
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
-import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
-import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
@@ -84,6 +84,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.ResourceId;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -547,7 +548,7 @@
     }
 
     Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
-        int resId = ResourceId.ID_NULL;
+        int resId = Resources.ID_NULL;
         Context context = mContext;
         if (animAttr >= 0) {
             AttributeCache.Entry ent = getCachedAnimations(lp);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a5341ca..801c1e7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1714,7 +1714,8 @@
             if (mLetterbox == null) {
                 mLetterbox = new Letterbox(() -> makeChildSurface(null));
             }
-            mLetterbox.layout(getParent().getBounds(), w.getFrameLw());
+            getPosition(mTmpPoint);
+            mLetterbox.layout(getParent().getBounds(), w.getFrameLw(), mTmpPoint);
         } else if (mLetterbox != null) {
             mLetterbox.hide();
         }
@@ -2355,7 +2356,7 @@
     public void onAnimationLeashDestroyed(Transaction t) {
         super.onAnimationLeashDestroyed(t);
         if (mAnimationBoundsLayer != null) {
-            t.destroy(mAnimationBoundsLayer);
+            t.reparent(mAnimationBoundsLayer, null);
             mAnimationBoundsLayer = null;
         }
 
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index ded45c9..650d0be 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -37,6 +37,7 @@
 import android.annotation.CallSuper;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
 
@@ -243,6 +244,14 @@
     }
 
     /**
+     * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}.
+     */
+    public void getPosition(Point out) {
+        Rect bounds = getBounds();
+        out.set(bounds.left, bounds.top);
+    }
+
+    /**
      * Returns the bounds requested on this container. These may not be the actual bounds the
      * container ends up with due to policy constraints. The {@link Rect} handed back is
      * shared for all calls to this method and should not be modified.
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index aea071f..c39060e 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -129,7 +129,7 @@
             final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
             mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> {
                 if (!mDimming) {
-                    dimAnimatable.getPendingTransaction().destroy(mDimLayer);
+                    dimAnimatable.getPendingTransaction().reparent(mDimLayer, null);
                 }
             }, mHost.mWmService);
         }
@@ -300,7 +300,7 @@
 
         if (!mDimState.mDimming) {
             if (!mDimState.mAnimateExit) {
-                t.destroy(mDimState.mDimLayer);
+                t.reparent(mDimState.mDimLayer, null);
             } else {
                 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
             }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fecc8da..6527ca0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -328,14 +328,6 @@
     private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
     /**
-     * Flag indicating that the application is receiving an orientation that has different metrics
-     * than it expected. E.g. Portrait instead of Landscape.
-     *
-     * @see #updateRotationUnchecked()
-     */
-    private boolean mAltOrientation = false;
-
-    /**
      * Orientation forced by some window. If there is no visible window that specifies orientation
      * it is set to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}.
      *
@@ -1085,10 +1077,6 @@
         return mLastOrientation;
     }
 
-    boolean getAltOrientation() {
-        return mAltOrientation;
-    }
-
     int getLastWindowForcedOrientation() {
         return mLastWindowForcedOrientation;
     }
@@ -1130,15 +1118,9 @@
     boolean rotationNeedsUpdate() {
         final int lastOrientation = getLastOrientation();
         final int oldRotation = getRotation();
-        final boolean oldAltOrientation = getAltOrientation();
 
         final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
-        final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
-                lastOrientation, rotation);
-        if (oldRotation == rotation && oldAltOrientation == altOrientation) {
-            return false;
-        }
-        return true;
+        return oldRotation != rotation;
     }
 
     /**
@@ -1160,12 +1142,14 @@
     @Override
     boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
             ConfigurationContainer requestingContainer) {
+        final int previousRotation = mRotation;
         final Configuration config = updateOrientationFromAppTokens(
                 getRequestedOverrideConfiguration(), freezeDisplayToken, false);
-        // If display rotation class tells us that it doesn't consider app requested orientation,
-        // this display won't rotate just because of an app changes its requested orientation. Thus
-        // it indicates that this display chooses not to handle this request.
-        final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+        // This event is considered handled iff a configuration propagation is triggered, because
+        // that's the only place lower level containers check if they need to do something to this
+        // request. The only guaranteed signal is that the display is rotated to a different
+        // orientation (i.e. rotating 180 degrees doesn't count).
+        final boolean handled = (mRotation - previousRotation) % 2 != 0;
         if (config == null) {
             return handled;
         }
@@ -1334,7 +1318,6 @@
 
         final int oldRotation = mRotation;
         final int lastOrientation = mLastOrientation;
-        final boolean oldAltOrientation = mAltOrientation;
         final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
                 + mDisplayId + " based on lastOrientation=" + lastOrientation
@@ -1366,35 +1349,26 @@
         }
         final boolean rotateSeamlessly = mayRotateSeamlessly;
 
-        // TODO: Implement forced rotation changes.
-        //       Set mAltOrientation to indicate that the application is receiving
-        //       an orientation that has different metrics than it expected.
-        //       eg. Portrait instead of Landscape.
-
-        final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
-                lastOrientation, rotation);
-
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
                 + " selected orientation " + lastOrientation
                 + ", got rotation " + rotation + " which has "
-                + (altOrientation ? "incompatible" : "compatible") + " metrics");
+                + " metrics");
 
-        if (oldRotation == rotation && oldAltOrientation == altOrientation) {
+        if (oldRotation == rotation) {
             // No change.
             return false;
         }
 
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
                 + " rotation changed to " + rotation
-                + (altOrientation ? " (alt)" : "") + " from " + oldRotation
-                + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
+                + " from " + oldRotation
+                + ", lastOrientation=" + lastOrientation);
 
         if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
             mWaitingForConfig = true;
         }
 
         mRotation = rotation;
-        mAltOrientation = altOrientation;
 
         mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
         mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
@@ -1536,26 +1510,8 @@
     private DisplayInfo updateDisplayAndOrientation(int uiMode) {
         // Use the effective "visual" dimensions based on current rotation
         final boolean rotated = (mRotation == ROTATION_90 || mRotation == ROTATION_270);
-        final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
-        final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
-        int dw = realdw;
-        int dh = realdh;
-
-        if (mAltOrientation) {
-            if (realdw > realdh) {
-                // Turn landscape into portrait.
-                int maxw = (int)(realdh/1.3f);
-                if (maxw < realdw) {
-                    dw = maxw;
-                }
-            } else {
-                // Turn portrait into landscape.
-                int maxh = (int)(realdw/1.3f);
-                if (maxh < realdh) {
-                    dh = maxh;
-                }
-            }
-        }
+        final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
+        final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
 
         // Update application display metrics.
         final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(mRotation);
@@ -2307,13 +2263,12 @@
         out.set(mDisplayFrames.mStable);
     }
 
-    TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
-        if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
-                + mDisplayId);
+    void setStackOnDisplay(int stackId, boolean onTop, TaskStack stack) {
+        if (DEBUG_STACK) {
+            Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId=" + mDisplayId);
+        }
 
-        final TaskStack stack = new TaskStack(mWmService, stackId, controller);
         mTaskStackContainers.addStackToDisplay(stack, onTop);
-        return stack;
     }
 
     void moveStackToDisplay(TaskStack stack, boolean onTop) {
@@ -3656,6 +3611,12 @@
         }
     }
 
+    /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+    int getNaturalOrientation() {
+        return mBaseDisplayWidth < mBaseDisplayHeight
+                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+    }
+
     void performLayout(boolean initial, boolean updateInputWindows) {
         if (!isLayoutNeeded()) {
             return;
@@ -4009,7 +3970,6 @@
 
         /**
          * Adds the stack to this container.
-         * @see DisplayContent#createStack(int, boolean, StackWindowController)
          */
         void addStackToDisplay(TaskStack stack, boolean onTop) {
             addStackReferenceIfNeeded(stack);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bcc7be4..bc165dc 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -329,15 +329,6 @@
         return mFixedToUserRotation;
     }
 
-    /**
-     * Returns {@code true} if this display rotation takes app requested orientation into
-     * consideration; {@code false} otherwise. For the time being the only case where this is {@code
-     * false} is when {@link #isFixedToUserRotation()} is {@code true}.
-     */
-    boolean respectAppRequestedOrientation() {
-        return !mFixedToUserRotation;
-    }
-
     public int getLandscapeRotation() {
         return mLandscapeRotation;
     }
@@ -685,36 +676,6 @@
         return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
     }
 
-    /**
-     * Given an orientation constant and a rotation, returns true if the rotation
-     * has compatible metrics to the requested orientation.  For example, if
-     * the application requested landscape and got seascape, then the rotation
-     * has compatible metrics; if the application requested portrait and got landscape,
-     * then the rotation has incompatible metrics; if the application did not specify
-     * a preference, then anything goes.
-     *
-     * @param orientation An orientation constant, such as
-     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
-     * @param rotation The rotation to check.
-     * @return True if the rotation is compatible with the requested orientation.
-     */
-    boolean rotationHasCompatibleMetrics(int orientation, int rotation) {
-        switch (orientation) {
-            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
-            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
-            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
-                return isAnyPortrait(rotation);
-
-            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
-                return isLandscapeOrSeascape(rotation);
-
-            default:
-                return true;
-        }
-    }
-
     private boolean isValidRotationChoice(final int preferredRotation) {
         // Determine if the given app orientation is compatible with the provided rotation choice.
         switch (mCurrentAppOrientation) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 607ee76..786a306 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -127,6 +127,7 @@
      * {@code true} when {@link #closeLocked()} is called.
      */
     private boolean mIsClosing;
+    IBinder mTransferTouchFromToken;
 
     DragState(WindowManagerService service, DragDropController controller, IBinder token,
             SurfaceControl surface, int flags, IBinder localWin) {
@@ -177,6 +178,8 @@
 
         mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
         t.setWindowCrop(mInputSurface, mTmpClipRect);
+        t.transferTouchFocus(mTransferTouchFromToken, h.token);
+        mTransferTouchFromToken = null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 639ed02..f9c9d33 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,5 +1,6 @@
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
@@ -9,7 +10,6 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Slog;
-import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
@@ -204,6 +204,37 @@
                 + WindowManagerService.TYPE_LAYER_OFFSET;
     }
 
+    /** Callback to get pointer display id. */
+    @Override
+    public int getPointerDisplayId() {
+        synchronized (mService.mGlobalLock) {
+            // If desktop mode is not enabled, show on the default display.
+            if (!mService.mForceDesktopModeOnExternalDisplays) {
+                return DEFAULT_DISPLAY;
+            }
+
+            // Look for the topmost freeform display.
+            int firstExternalDisplayId = DEFAULT_DISPLAY;
+            for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
+                final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
+                // Heuristic solution here. Currently when "Freeform windows" developer option is
+                // enabled we automatically put secondary displays in freeform mode and emulating
+                // "desktop mode". It also makes sense to show the pointer on the same display.
+                if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+                    return displayContent.getDisplayId();
+                }
+
+                if (firstExternalDisplayId == DEFAULT_DISPLAY
+                        && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
+                    firstExternalDisplayId = displayContent.getDisplayId();
+                }
+            }
+
+            // Look for the topmost non-default display
+            return firstExternalDisplayId;
+        }
+    }
+
     /** Waits until the built-in input devices have been configured. */
     public boolean waitForInputDevicesReady(long timeoutMillis) {
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 49a3186..d6f1616 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsState;
@@ -26,8 +25,10 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
+import android.view.ViewRootImpl;
 
 import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import java.io.PrintWriter;
@@ -106,8 +107,8 @@
             mTmpRect.inset(mWin.mGivenContentInsets);
         }
         mSource.setFrame(mTmpRect);
-        setServerVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
-
+        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility
+                && !mWin.mGivenInsetsPending);
     }
 
     void updateControlForTarget(@Nullable WindowState target) {
@@ -115,15 +116,15 @@
             return;
         }
         if (target == null) {
-            revokeControl();
+            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
+            mWin.cancelAnimation();
             return;
         }
         mAdapter = new ControlAdapter();
         mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
-                false /* TODO hidden */);
+                !mClientVisible /* hidden */);
         mControllingWin = target;
         mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash);
-        setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
     }
 
     boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
@@ -135,7 +136,12 @@
     }
 
     private void setClientVisible(boolean clientVisible) {
+        if (mClientVisible == clientVisible) {
+            return;
+        }
         mClientVisible = clientVisible;
+        mDisplayContent.mWmService.mH.sendMessage(PooledLambda.obtainMessage(
+                DisplayContent::layoutAndAssignWindowLayersIfNeeded, mDisplayContent));
         updateVisibility();
     }
 
@@ -152,13 +158,8 @@
         return mControl;
     }
 
-    void revokeControl() {
-        if (mControllingWin != null) {
-
-            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
-            mWin.cancelAnimation();
-        }
-        setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
+    boolean isClientVisible() {
+        return !ViewRootImpl.USE_NEW_INSETS || mClientVisible;
     }
 
     private class ControlAdapter implements AnimationAdapter {
@@ -186,6 +187,7 @@
         public void onAnimationCancelled(SurfaceControl animationLeash) {
             if (mAdapter == this) {
                 mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
+                setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
                 mControl = null;
                 mControllingWin = null;
                 mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 8e119bb..bc01f7c 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -202,6 +202,9 @@
     }
 
     private void notifyPendingInsetsControlChanged() {
+        if (mPendingControlChanged.isEmpty()) {
+            return;
+        }
         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
                 final WindowState controllingWin = mPendingControlChanged.valueAt(i);
@@ -218,5 +221,11 @@
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "WindowInsetsStateController");
         mState.dump(prefix + "  ", pw);
+        pw.println(prefix + "  " + "Control map:");
+        for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) {
+            pw.print(prefix + "  ");
+            pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> "
+                    + mTypeWinControlMap.valueAt(i));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 5f56fe5..177f244 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -462,22 +462,19 @@
             mOccluded = false;
             mDismissingKeyguardActivity = null;
 
-            // Only the top activity of the focused stack on each display may control it's
-            // occluded state.
-            final ActivityStack focusedStack = display.getFocusedStack();
-            if (focusedStack != null) {
-                final ActivityRecord topDismissing =
-                        focusedStack.getTopDismissingKeyguardActivity();
-                mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
-                                && focusedStack.topRunningActivityLocked() == topDismissing
-                                && controller.canShowWhileOccluded(
+            final ActivityStack stack = getStackForControllingOccluding(display);
+            if (stack != null) {
+                final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
+                mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
+                        && stack.topRunningActivityLocked() == topDismissing
+                        && controller.canShowWhileOccluded(
                                 true /* dismissKeyguard */,
                                 false /* showWhenLocked */));
-                if (focusedStack.getTopDismissingKeyguardActivity() != null) {
-                    mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
+                if (stack.getTopDismissingKeyguardActivity() != null) {
+                    mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
                 }
-                mOccluded |= controller.mWindowManager.isShowingDream();
             }
+            mOccluded |= controller.mWindowManager.isShowingDream();
 
             // TODO(b/113840485): Handle app transition for individual display, and apply occluded
             // state change to secondary displays.
@@ -492,6 +489,23 @@
             }
         }
 
+        /**
+         * Gets the stack used to check the occluded state.
+         * <p>
+         * Only the top non-pinned activity of the focusable stack on each display can control its
+         * occlusion state.
+         */
+        private ActivityStack getStackForControllingOccluding(ActivityDisplay display) {
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (stack != null && stack.isFocusableAndVisible()
+                        && !stack.inPinnedWindowingMode()) {
+                    return stack;
+                }
+            }
+            return null;
+        }
+
         void dumpStatus(PrintWriter pw, String prefix) {
             final StringBuilder sb = new StringBuilder();
             sb.append(prefix);
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 3062d34..86dc66d 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -221,7 +221,7 @@
     }
 
     private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) {
-        final ActivityStack<?> stack = task.getStack();
+        final ActivityStack stack = task.getStack();
         final int displayId = stack.mDisplayId;
         final ActivityDisplay display =
                 mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 1a2aa2f..33ff194 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -18,6 +18,7 @@
 
 import static android.view.SurfaceControl.HIDDEN;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 
@@ -30,6 +31,7 @@
 public class Letterbox {
 
     private static final Rect EMPTY_RECT = new Rect();
+    private static final Point ZERO_POINT = new Point(0, 0);
 
     private final Supplier<SurfaceControl.Builder> mFactory;
     private final Rect mOuter = new Rect();
@@ -53,14 +55,19 @@
      * frames will be covered by black color surfaces.
      *
      * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
-     *
      * @param outer the outer frame of the letterbox (this frame will be black, except the area
-     *              that intersects with the {code inner} frame).
-     * @param inner the inner frame of the letterbox (this frame will be clear)
+     *              that intersects with the {code inner} frame), in global coordinates
+     * @param inner the inner frame of the letterbox (this frame will be clear), in global
+     *              coordinates
+     * @param surfaceOrigin the origin of the surface factory in global coordinates
      */
-    public void layout(Rect outer, Rect inner) {
+    public void layout(Rect outer, Rect inner, Point surfaceOrigin) {
         mOuter.set(outer);
         mInner.set(inner);
+        mOuter.offset(-surfaceOrigin.x, -surfaceOrigin.y);
+        mInner.offset(-surfaceOrigin.x, -surfaceOrigin.y);
+        outer = mOuter;
+        inner = mInner;
 
         mTop.layout(outer.left, outer.top, inner.right, inner.top);
         mLeft.layout(outer.left, inner.top, inner.left, outer.bottom);
@@ -94,7 +101,7 @@
      * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
      */
     public void hide() {
-        layout(EMPTY_RECT, EMPTY_RECT);
+        layout(EMPTY_RECT, EMPTY_RECT, ZERO_POINT);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
deleted file mode 100644
index 2a05af4..0000000
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ /dev/null
@@ -1,111 +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.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
-import android.app.RemoteAction;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * State and management of the pinned stack of activities.
- */
-class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
-        implements PinnedStackWindowListener {
-
-    PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-            boolean onTop) {
-        super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop);
-    }
-
-    @Override
-    PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
-            Rect outBounds) {
-        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mRootActivityContainer.mWindowManager);
-    }
-
-    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
-        return getWindowContainerController().getPictureInPictureBounds(aspectRatio,
-                null /* currentStackBounds */);
-    }
-
-    void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
-            boolean fromFullscreen) {
-        if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
-            mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
-        } else {
-            getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds,
-                    animationDuration, fromFullscreen);
-        }
-    }
-
-    private boolean skipResizeAnimation(boolean toFullscreen) {
-        if (!toFullscreen) {
-            return false;
-        }
-        final Configuration parentConfig = getParent().getConfiguration();
-        final ActivityRecord top = topRunningNonOverlayTaskActivity();
-        return top != null && !top.isConfigurationCompatible(parentConfig);
-    }
-
-    void setPictureInPictureAspectRatio(float aspectRatio) {
-        getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio);
-    }
-
-    void setPictureInPictureActions(List<RemoteAction> actions) {
-        getWindowContainerController().setPictureInPictureActions(actions);
-    }
-
-    boolean isAnimatingBoundsToFullscreen() {
-        return getWindowContainerController().mContainer.isAnimatingBoundsToFullscreen();
-    }
-
-    /**
-     * Returns whether to defer the scheduling of the multi-window mode.
-     */
-    boolean deferScheduleMultiWindowModeChanged() {
-        // For the pinned stack, the deferring of the multi-window mode changed is tied to the
-        // transition animation into picture-in-picture, and is called once the animation completes,
-        // or is interrupted in a way that would leave the stack in a non-fullscreen state.
-        // @see BoundsAnimationController
-        // @see BoundsAnimationControllerTests
-        return mWindowContainerController.deferScheduleMultiWindowModeChanged();
-    }
-
-    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {
-        // It is guaranteed that the activities requiring the update will be in the pinned stack at
-        // this point (either reparented before the animation into PiP, or before reparenting after
-        // the animation out of PiP)
-        synchronized (mService.mGlobalLock) {
-            if (!isAttached()) {
-                return;
-            }
-            ArrayList<TaskRecord> tasks = getAllTasks();
-            for (int i = 0; i < tasks.size(); i++ ) {
-                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
-                        forceUpdate);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
deleted file mode 100644
index 518e39b..0000000
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ /dev/null
@@ -1,205 +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.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
-import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
-
-import android.app.RemoteAction;
-import android.graphics.Rect;
-
-import java.util.List;
-
-/**
- * Controller for the pinned stack container. See {@link StackWindowController}.
- */
-public class PinnedStackWindowController extends StackWindowController {
-
-    private Rect mTmpFromBounds = new Rect();
-    private Rect mTmpToBounds = new Rect();
-
-    public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
-        super(stackId, listener, displayId, onTop, outBounds, service);
-    }
-
-    /**
-     * @return the {@param currentStackBounds} transformed to the give {@param aspectRatio}.  If
-     *         {@param currentStackBounds} is null, then the {@param aspectRatio} is applied to the
-     *         default bounds.
-     */
-    public Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
-        synchronized (mGlobalLock) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return null;
-            }
-
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            if (displayContent == null) {
-                return null;
-            }
-
-            final PinnedStackController pinnedStackController =
-                    displayContent.getPinnedStackController();
-            if (stackBounds == null) {
-                // Calculate the aspect ratio bounds from the default bounds
-                stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
-            }
-
-            if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
-                return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
-                        true /* useCurrentMinEdgeSize */);
-            } else {
-                return stackBounds;
-            }
-        }
-    }
-
-    /**
-     * Animates the pinned stack.
-     */
-    public void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
-            int animationDuration, boolean fromFullscreen) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                throw new IllegalArgumentException("Pinned stack container not found :(");
-            }
-
-            // Get the from-bounds
-            final Rect fromBounds = new Rect();
-            mContainer.getBounds(fromBounds);
-
-            // Get non-null fullscreen to-bounds for animating if the bounds are null
-            @SchedulePipModeChangedState int schedulePipModeChangedState =
-                NO_PIP_MODE_CHANGED_CALLBACKS;
-            final boolean toFullscreen = toBounds == null;
-            if (toFullscreen) {
-                if (fromFullscreen) {
-                    throw new IllegalArgumentException("Should not defer scheduling PiP mode"
-                            + " change on animation to fullscreen.");
-                }
-                schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
-
-                mService.getStackBounds(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
-                if (!mTmpToBounds.isEmpty()) {
-                    // If there is a fullscreen bounds, use that
-                    toBounds = new Rect(mTmpToBounds);
-                } else {
-                    // Otherwise, use the display bounds
-                    toBounds = new Rect();
-                    mContainer.getDisplayContent().getBounds(toBounds);
-                }
-            } else if (fromFullscreen) {
-                schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-            }
-
-            mContainer.setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
-
-            final Rect finalToBounds = toBounds;
-            final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
-                schedulePipModeChangedState;
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            displayContent.mBoundsAnimationController.getHandler().post(() -> {
-                if (mContainer == null) {
-                    return;
-                }
-                displayContent.mBoundsAnimationController.animateBounds(mContainer, fromBounds,
-                        finalToBounds, animationDuration, finalSchedulePipModeChangedState,
-                        fromFullscreen, toFullscreen);
-            });
-        }
-    }
-
-    /**
-     * Sets the current picture-in-picture aspect ratio.
-     */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mGlobalLock) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            final PinnedStackController pinnedStackController =
-                    mContainer.getDisplayContent().getPinnedStackController();
-
-            if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) != 0) {
-                mContainer.getAnimationOrCurrentBounds(mTmpFromBounds);
-                mTmpToBounds.set(mTmpFromBounds);
-                getPictureInPictureBounds(aspectRatio, mTmpToBounds);
-                if (!mTmpToBounds.equals(mTmpFromBounds)) {
-                    animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
-                            -1 /* duration */, false /* fromFullscreen */);
-                }
-                pinnedStackController.setAspectRatio(
-                        pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
-                                ? aspectRatio : -1f);
-            }
-        }
-    }
-
-    /**
-     * Sets the current picture-in-picture actions.
-     */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        synchronized (mGlobalLock) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
-        }
-    }
-
-    /**
-     * @return whether the multi-window mode change should be deferred as a part of a transition
-     * from fullscreen to non-fullscreen bounds.
-     */
-    public boolean deferScheduleMultiWindowModeChanged() {
-        synchronized (mGlobalLock) {
-            return mContainer.deferScheduleMultiWindowModeChanged();
-        }
-    }
-
-    /**
-     * @return whether the stack can be resized from the bounds animation.
-     */
-    public boolean pinnedStackResizeDisallowed() {
-        synchronized (mGlobalLock) {
-            return mContainer.pinnedStackResizeDisallowed();
-        }
-    }
-
-    /**
-     * The following calls are made from WM to AM.
-     */
-
-    /** Calls directly into activity manager so window manager lock shouldn't held. */
-    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {
-        if (mListener != null) {
-            PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
-            listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds,
-                    forceUpdate);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
deleted file mode 100644
index 33e8a60..0000000
--- a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
+++ /dev/null
@@ -1,33 +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.wm;
-
-import android.graphics.Rect;
-
-/**
- * Interface used by the creator of {@link PinnedStackWindowController} to listen to changes with
- * the stack container.
- */
-public interface PinnedStackWindowListener extends StackWindowListener {
-
-    /**
-     * Called when the stack container pinned stack animation will change the picture-in-picture
-     * mode. This is a direct call into ActivityManager.
-     */
-    default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {}
-}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index f55c7c9..9b72141 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -234,7 +234,7 @@
         mWindowManager = wm;
         setWindowContainer(mWindowManager.mRoot);
         mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
-        mDisplayManager.registerDisplayListener(this, mService.mH);
+        mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
         final Display[] displays = mDisplayManager.getDisplays();
@@ -955,7 +955,7 @@
         mWindowManager.deferSurfaceLayout();
 
         final ActivityDisplay display = r.getActivityStack().getDisplay();
-        PinnedActivityStack stack = display.getPinnedStack();
+        ActivityStack stack = display.getPinnedStack();
 
         // This will clear the pinned stack by moving an existing task to the full screen stack,
         // ensuring only one task is present.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4e70bbc..8fb7947 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -280,6 +281,15 @@
     }
 
     /**
+     * Returns true if the callingUid has any non-toast window currently visible to the user.
+     */
+    boolean isAnyNonToastWindowVisibleForUid(int callingUid) {
+        return forAllWindows(w -> {
+            return w.getOwningUid() == callingUid && w.isVisible() && w.mAttrs.type != TYPE_TOAST;
+        }, true /* traverseTopToBottom */);
+    }
+
+    /**
      * Returns the app window token for the input binder if it exist in the system.
      * NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
      * AppWindowToken represents an activity which can only exist on one display.
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index d85fdb0..58cf73a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -47,10 +47,9 @@
 import android.view.IWindowSession;
 import android.view.IWindowSessionCallback;
 import android.view.InputChannel;
-import android.view.Surface;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
-import android.view.InsetsState;
 import android.view.WindowManager;
 
 import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -187,7 +186,7 @@
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
             Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface, InsetsState outInsetsState) {
+            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -195,7 +194,7 @@
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurface, outInsetsState);
+                mergedConfiguration, outSurfaceControl, outInsetsState);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
@@ -432,7 +431,7 @@
 
     @Override
     public void insetsModified(IWindow window, InsetsState state) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             final WindowState windowState = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (windowState != null) {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
deleted file mode 100644
index ada807b..0000000
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Controller for the stack container. This is created by activity manager to link activity stacks
- * to the stack container they use in window manager.
- *
- * Test class: {@link StackWindowControllerTests}
- */
-public class StackWindowController
-        extends WindowContainerController<TaskStack, StackWindowListener> {
-
-    private final int mStackId;
-
-    private final H mHandler;
-
-    final Rect mTmpBounds = new Rect();
-
-    public StackWindowController(int stackId, StackWindowListener listener, int displayId,
-            boolean onTop, Rect outBounds) {
-        this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
-    }
-
-    @VisibleForTesting
-    public StackWindowController(int stackId, StackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
-        super(listener, service);
-        mStackId = stackId;
-        mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
-
-        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-        if (dc == null) {
-            throw new IllegalArgumentException("Trying to add stackId=" + stackId
-                    + " to unknown displayId=" + displayId);
-        }
-
-        dc.createStack(stackId, onTop, this);
-        getRawBounds(outBounds);
-    }
-
-    @Override
-    public void removeContainer() {
-        if (mContainer != null) {
-            mContainer.removeIfPossible();
-            super.removeContainer();
-        }
-    }
-
-    void reparent(int displayId, Rect outStackBounds, boolean onTop) {
-        if (mContainer == null) {
-            throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
-                    + " to displayId=" + displayId);
-        }
-
-        final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
-        if (targetDc == null) {
-            throw new IllegalArgumentException("Trying to move stackId=" + mStackId
-                    + " to unknown displayId=" + displayId);
-        }
-
-        targetDc.moveStackToDisplay(mContainer, onTop);
-        getRawBounds(outStackBounds);
-    }
-
-    void positionChildAt(Task child, int position) {
-        if (DEBUG_STACK) {
-            Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
-        }
-        if (child == null) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
-            }
-            return;
-        }
-        if (mContainer == null) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_WM, "positionChildAt: could not find stack for task=" + mContainer);
-            }
-            return;
-        }
-        child.positionAt(position);
-        mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    void positionChildAtTop(Task child, boolean includingParents) {
-        if (child == null) {
-            // TODO: Fix the call-points that cause this to happen.
-            return;
-        }
-
-        mContainer.positionChildAt(POSITION_TOP, child, includingParents);
-
-        final DisplayContent displayContent = mContainer.getDisplayContent();
-        if (displayContent.mAppTransition.isTransitionSet()) {
-            child.setSendingToBottom(false);
-        }
-        displayContent.layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    void positionChildAtBottom(Task child, boolean includingParents) {
-        if (child == null) {
-            // TODO: Fix the call-points that cause this to happen.
-            return;
-        }
-
-        mContainer.positionChildAt(POSITION_BOTTOM, child, includingParents);
-
-        if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) {
-            child.setSendingToBottom(true);
-        }
-        mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    /**
-     * Re-sizes a stack and its containing tasks.
-     *
-     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
-     * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
-     */
-    public void resize(Rect bounds, SparseArray<Rect> taskBounds,
-            SparseArray<Rect> taskTempInsetBounds) {
-        if (mContainer == null) {
-            throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
-        }
-        // We might trigger a configuration change. Save the current task bounds for freezing.
-        mContainer.prepareFreezingTaskBounds();
-        if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
-                && mContainer.isVisible()) {
-            mContainer.getDisplayContent().setLayoutNeeded();
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-    }
-
-    public void onPipAnimationEndResize() {
-        mContainer.onPipAnimationEndResize();
-    }
-
-    /**
-     * @see TaskStack.getStackDockedModeBoundsLocked(ConfigurationContainer, Rect, Rect, Rect)
-     */
-    public void getStackDockedModeBounds(Configuration parentConfig, Rect dockedBounds,
-            Rect currentTempTaskBounds,
-            Rect outStackBounds, Rect outTempTaskBounds) {
-        if (mContainer != null) {
-            mContainer.getStackDockedModeBoundsLocked(parentConfig, dockedBounds,
-                    currentTempTaskBounds, outStackBounds, outTempTaskBounds);
-            return;
-        }
-        outStackBounds.setEmpty();
-        outTempTaskBounds.setEmpty();
-    }
-
-    public void prepareFreezingTaskBounds() {
-        if (mContainer == null) {
-            throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
-                    + " not found.");
-        }
-        mContainer.prepareFreezingTaskBounds();
-    }
-
-    public void getRawBounds(Rect outBounds) {
-        if (mContainer.matchParentBounds()) {
-            outBounds.setEmpty();
-        } else {
-            mContainer.getRawBounds(outBounds);
-        }
-    }
-
-    public void getBounds(Rect outBounds) {
-        if (mContainer != null) {
-            mContainer.getBounds(outBounds);
-            return;
-        }
-        outBounds.setEmpty();
-    }
-
-    void requestResize(Rect bounds) {
-        mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
-    }
-
-    @Override
-    public String toString() {
-        return "{StackWindowController stackId=" + mStackId + "}";
-    }
-
-    private static final class H extends Handler {
-
-        static final int REQUEST_RESIZE = 0;
-
-        private final WeakReference<StackWindowController> mController;
-
-        H(WeakReference<StackWindowController> controller, Looper looper) {
-            super(looper);
-            mController = controller;
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            final StackWindowController controller = mController.get();
-            final StackWindowListener listener = (controller != null)
-                    ? controller.mListener : null;
-            if (listener == null) {
-                return;
-            }
-            switch (msg.what) {
-                case REQUEST_RESIZE:
-                    listener.requestResize((Rect) msg.obj);
-                    break;
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/StackWindowListener.java b/services/core/java/com/android/server/wm/StackWindowListener.java
deleted file mode 100644
index c763c17..0000000
--- a/services/core/java/com/android/server/wm/StackWindowListener.java
+++ /dev/null
@@ -1,29 +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.wm;
-
-import android.graphics.Rect;
-
-/**
- * Interface used by the creator of {@link StackWindowController} to listen to changes with
- * the stack container.
- */
-public interface StackWindowListener extends WindowContainerListener {
-
-    /** Called when the stack container would like its controller to resize. */
-    void requestResize(Rect bounds);
-}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 11068ce..9d9b48a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -280,7 +280,7 @@
         }
         mService.mAnimationTransferMap.remove(mAnimation);
         if (mLeash != null && destroyLeash) {
-            t.destroy(mLeash);
+            t.reparent(mLeash, null);
             scheduleAnim = true;
         }
         mLeash = null;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d334bd2..a7dd55b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -209,26 +209,14 @@
         super.removeImmediately();
     }
 
-    void reparent(StackWindowController stackController, int position, boolean moveParents) {
-        if (DEBUG_STACK) {
-            Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
-                    + " to stack=" + stackController + " at " + position);
-        }
-        final TaskStack stack = stackController.mContainer;
-        if (stack == null) {
-            throw new IllegalArgumentException("reparent: could not find stack="
-                    + stackController);
-        }
-        reparent(stack, position, moveParents);
-        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-    }
-
-
     void reparent(TaskStack stack, int position, boolean moveParents) {
         if (stack == mStack) {
             throw new IllegalArgumentException(
                     "task=" + this + " already child of stack=" + mStack);
         }
+        if (stack == null) {
+            throw new IllegalArgumentException("reparent: could not find stack.");
+        }
         if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
                 + " from stack=" + mStack);
         EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask");
@@ -254,6 +242,7 @@
             onDisplayChanged(displayContent);
             prevDisplayContent.setLayoutNeeded();
         }
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
     }
 
     /** @see ActivityTaskManagerService#positionTaskInStack(int, int, int). */
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 6acd864..f3050a9 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -709,7 +709,7 @@
 
         final List<TaskRecord> tasksToCheck = new ArrayList<>();
         for (int i = 0; i < display.getChildCount(); ++i) {
-            ActivityStack<?> stack = display.getChildAt(i);
+            final ActivityStack stack = display.getChildAt(i);
             if (!stack.inFreeformWindowingMode()) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index e15bf5b..9163165 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -24,6 +24,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -50,6 +51,7 @@
     private @Nullable TaskPositioner mTaskPositioner;
 
     private final Rect mTmpClipRect = new Rect();
+    private IBinder mTransferTouchFromToken;
 
     boolean isPositioningLocked() {
         return mTaskPositioner != null;
@@ -102,6 +104,8 @@
 
         mTmpClipRect.set(0, 0, p.x, p.y);
         t.setWindowCrop(mInputSurface, mTmpClipRect);
+        t.transferTouchFocus(mTransferTouchFromToken, h.token);
+        mTransferTouchFromToken = null;
     }
 
     boolean startMovingTask(IWindow window, float startX, float startY) {
@@ -170,7 +174,6 @@
         mPositioningDisplay = displayContent;
 
         mTaskPositioner = TaskPositioner.create(mService);
-        mTaskPositioner.register(displayContent);
 
         // We need to grab the touch focus so that the touch events during the
         // resizing/scrolling are not sent to the app. 'win' is the main window
@@ -181,12 +184,8 @@
                 && displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
             transferFocusFromWin = displayContent.mCurrentFocus;
         }
-        if (!mInputManager.transferTouchFocus(
-                transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
-            Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
-            cleanUpTaskPositioner();
-            return false;
-        }
+        mTransferTouchFromToken = transferFocusFromWin.mInputChannel.getToken();
+        mTaskPositioner.register(displayContent);
 
         mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
         return true;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index e343322..69dcaf4 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -44,6 +44,9 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 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;
@@ -452,17 +455,10 @@
         }
 
         final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
-        final StackWindowController stackController = getStack().getWindowContainerController();
+        final TaskStack stack = getStack().getTaskStack();
 
-        if (DEBUG_STACK) {
-            Slog.i(TAG_WM, "TaskRecord: taskId=" + taskId
-                    + " stack=" + stackController + " bounds=" + bounds);
-        }
-
-        final TaskStack stack = stackController.mContainer;
         if (stack == null) {
-            throw new IllegalArgumentException("TaskRecord: invalid stack="
-                    + stackController);
+            throw new IllegalArgumentException("TaskRecord: invalid stack=" + mStack);
         }
         EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
         mTask = new Task(taskId, stack, userId, mService.mWindowManager, mResizeMode,
@@ -739,7 +735,7 @@
 
             // Must reparent first in window manager to avoid a situation where AM can delete the
             // we are coming from in WM before we reparent because it became empty.
-            mTask.reparent(toStack.getWindowContainerController(), position,
+            mTask.reparent(toStack.getTaskStack(), position,
                     moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
 
             final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
@@ -971,10 +967,11 @@
      */
     boolean isSameIntentFilter(ActivityRecord r) {
         final Intent intent = new Intent(r.intent);
-        // Correct the activity intent for aliasing. The task record intent will always be based on
-        // the real activity that will be launched not the alias, so we need to use an intent with
-        // the component name pointing to the real activity not the alias in the activity record.
-        intent.setComponent(r.mActivityComponent);
+        // Make sure the component are the same if the input activity has the same real activity
+        // as the one in the task because either one of them could be the alias activity.
+        if (Objects.equals(realActivity, r.mActivityComponent) && this.intent != null) {
+            intent.setComponent(this.intent.getComponent());
+        }
         return intent.filterEquals(this.intent);
     }
 
@@ -1259,10 +1256,6 @@
         setFrontOfTask();
     }
 
-    void addActivityAtBottom(ActivityRecord r) {
-        addActivityAtIndex(0, r);
-    }
-
     void addActivityToTop(ActivityRecord r) {
         addActivityAtIndex(mActivities.size(), r);
     }
@@ -1278,6 +1271,34 @@
     }
 
     /**
+     * Checks if the top activity requires a particular orientation (either by override or
+     * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED.
+     */
+    private int getTopActivityRequestedOrientation() {
+        ActivityRecord top = getTopActivity();
+        if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED
+                || top == null) {
+            return getRequestedOverrideConfiguration().orientation;
+        }
+        int screenOrientation = top.getOrientation();
+        if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+            // NOSENSOR means the display's "natural" orientation, so return that.
+            ActivityDisplay display = mStack != null ? mStack.getDisplay() : null;
+            if (display != null && display.mDisplayContent != null) {
+                return mStack.getDisplay().mDisplayContent.getNaturalOrientation();
+            }
+        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            // LOCKED means the activity's orientation remains unchanged, so return existing value.
+            return top.getConfiguration().orientation;
+        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
+            return ORIENTATION_LANDSCAPE;
+        } else if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
+            return ORIENTATION_PORTRAIT;
+        }
+        return ORIENTATION_UNDEFINED;
+    }
+
+    /**
      * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
      * be in the current task or unparented to any task.
      */
@@ -1741,7 +1762,7 @@
         updateTaskDescription();
     }
 
-    private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
+    void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
         if (bounds == null) {
             return;
         }
@@ -1853,11 +1874,27 @@
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
+        // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
+        // restore the last recorded non-fullscreen bounds.
+        final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds();
+        final boolean nextPersistTaskBounds =
+                getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds()
+                || newParentConfig.windowConfiguration.persistTaskBounds();
+        if (!prevPersistTaskBounds && nextPersistTaskBounds
+                && mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) {
+            // Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop.
+            getRequestedOverrideConfiguration().windowConfiguration
+                    .setBounds(mLastNonFullscreenBounds);
+        }
+
         final boolean wasInMultiWindowMode = inMultiWindowMode();
         super.onConfigurationChanged(newParentConfig);
         if (wasInMultiWindowMode != inMultiWindowMode()) {
             mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
         }
+
+        // If the configuration supports persistent bounds (eg. Freeform), keep track of the
+        // current (non-fullscreen) bounds for persistence.
         if (getWindowConfiguration().persistTaskBounds()) {
             final Rect currentBounds = getRequestedOverrideBounds();
             if (!currentBounds.isEmpty()) {
@@ -2047,7 +2084,7 @@
      * configuring an "inherit-bounds" window which means that all configuration settings would
      * just be inherited from the parent configuration.
      **/
-    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Rect bounds,
+    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
             @NonNull Configuration parentConfig) {
         int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2060,6 +2097,7 @@
         }
         density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
 
+        final Rect bounds = inOutConfig.windowConfiguration.getBounds();
         Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
         if (outAppBounds == null || outAppBounds.isEmpty()) {
             inOutConfig.windowConfiguration.setAppBounds(bounds);
@@ -2107,13 +2145,14 @@
                     // Iterating across all screen orientations, and return the minimum of the task
                     // width taking into account that the bounds might change because the snap
                     // algorithm snaps to a different value
-                    getSmallestScreenWidthDpForDockedBounds(bounds);
+                    inOutConfig.smallestScreenWidthDp =
+                            getSmallestScreenWidthDpForDockedBounds(bounds);
                 }
                 // otherwise, it will just inherit
             }
         }
 
-        if (inOutConfig.orientation == Configuration.ORIENTATION_UNDEFINED) {
+        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
             inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
                     ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
         }
@@ -2134,36 +2173,56 @@
         }
     }
 
-    // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
-    void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
-            Configuration overrideConfig) {
-        // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it
-        // changes left bound vs. right bound, or top bound vs. bottom bound.
-        mTmpBounds.set(inOutConfig.windowConfiguration.getBounds());
-
-        inOutConfig.setTo(overrideConfig);
-
-        Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
-        if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
-            adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
-
-            int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
-            if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-                windowingMode = parentConfig.windowConfiguration.getWindowingMode();
-            }
-            if (windowingMode == WINDOWING_MODE_FREEFORM) {
-                // by policy, make sure the window remains within parent
-                fitWithinBounds(outOverrideBounds, parentConfig.windowConfiguration.getBounds());
-            }
-
-            computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig);
-        }
-    }
-
     @Override
     void resolveOverrideConfiguration(Configuration newParentConfig) {
-        computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig,
-                getRequestedOverrideConfiguration());
+        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+        super.resolveOverrideConfiguration(newParentConfig);
+        int windowingMode =
+                getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+            windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+        }
+        Rect outOverrideBounds =
+                getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+
+        if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent"
+            outOverrideBounds.setEmpty();
+
+            // If the task or its top activity requires a different orientation, make it fit the
+            // available bounds by scaling down its bounds.
+            int forcedOrientation = getTopActivityRequestedOrientation();
+            if (forcedOrientation != ORIENTATION_UNDEFINED
+                    && forcedOrientation != newParentConfig.orientation) {
+                final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+                final int parentWidth = parentBounds.width();
+                final int parentHeight = parentBounds.height();
+                final float aspect = ((float) parentHeight) / parentWidth;
+                if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+                    final int height = (int) (parentWidth / aspect);
+                    final int top = parentBounds.centerY() - height / 2;
+                    outOverrideBounds.set(
+                            parentBounds.left, top, parentBounds.right, top + height);
+                } else {
+                    final int width = (int) (parentHeight * aspect);
+                    final int left = parentBounds.centerX() - width / 2;
+                    outOverrideBounds.set(
+                            left, parentBounds.top, left + width, parentBounds.bottom);
+                }
+            }
+        }
+
+        if (outOverrideBounds.isEmpty()) {
+            // If the task fills the parent, just inherit all the other configs from parent.
+            return;
+        }
+
+        adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+            // by policy, make sure the window remains within parent somewhere
+            fitWithinBounds(outOverrideBounds, newParentConfig.windowConfiguration.getBounds());
+        }
+        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
     }
 
     Rect updateOverrideConfigurationFromLaunchBounds() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 9a56606..2d3e3ae 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -111,6 +111,7 @@
     private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
     private final Window mWindow;
     private final Surface mSurface;
+    private SurfaceControl mSurfaceControl;
     private SurfaceControl mChildSurfaceControl;
     private final IWindowSession mSession;
     private final WindowManagerService mService;
@@ -136,7 +137,7 @@
         final Window window = new Window();
         final IWindowSession session = WindowManagerGlobal.getWindowSession();
         window.setSession(session);
-        final Surface surface = new Surface();
+        final SurfaceControl surfaceControl = new SurfaceControl();
         final Rect tmpRect = new Rect();
         final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
         final Rect tmpFrame = new Rect();
@@ -213,14 +214,14 @@
             // Local call.
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
-                surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
+                surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
                 navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
                 currentOrientation);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
+                    tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -230,15 +231,16 @@
     }
 
     @VisibleForTesting
-    TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
+    TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
             TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
             int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
             Rect taskBounds, int currentOrientation) {
         mService = service;
+        mSurface = new Surface();
         mHandler = new Handler(mService.mH.getLooper());
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = window;
-        mSurface = surface;
+        mSurfaceControl = surfaceControl;
         mSnapshot = snapshot;
         mTitle = title;
         mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
@@ -281,6 +283,8 @@
 
     private void drawSnapshot() {
         final GraphicBuffer buffer = mSnapshot.getSnapshot();
+        mSurface.copyFrom(mSurfaceControl);
+
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
                 + mSizeMismatch);
         if (mSizeMismatch) {
@@ -310,13 +314,14 @@
         if (!mSurface.isValid()) {
             throw new IllegalStateException("mSurface does not hold a valid surface.");
         }
-        final SurfaceSession session = new SurfaceSession(mSurface);
+        final SurfaceSession session = new SurfaceSession();
 
         // Keep a reference to it such that it doesn't get destroyed when finalized.
         mChildSurfaceControl = new SurfaceControl.Builder(session)
                 .setName(mTitle + " - task-snapshot-surface")
                 .setBufferSize(buffer.getWidth(), buffer.getHeight())
                 .setFormat(buffer.getFormat())
+                .setParent(mSurfaceControl)
                 .build();
         Surface surface = new Surface();
         surface.copyFrom(mChildSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ee74bdf..8ed7d04 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -33,6 +34,10 @@
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 
+import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
+import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
@@ -47,10 +52,12 @@
 import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
 import static com.android.server.wm.StackProto.TASKS;
 import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.CallSuper;
+import android.app.RemoteAction;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -71,9 +78,10 @@
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
+import java.util.List;
 
 public class TaskStack extends WindowContainer<Task> implements
-        BoundsAnimationTarget {
+        BoundsAnimationTarget, ConfigurationContainerListener {
     /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
      * restrict IME adjustment so that a min portion of top stack remains visible.*/
     private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
@@ -93,6 +101,10 @@
     private Rect mTmpRect2 = new Rect();
     private Rect mTmpRect3 = new Rect();
 
+    /** For Pinned stack controlling. */
+    private Rect mTmpFromBounds = new Rect();
+    private Rect mTmpToBounds = new Rect();
+
     /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
     private final Rect mAdjustedBounds = new Rect();
 
@@ -141,6 +153,9 @@
 
     private Dimmer mDimmer = new Dimmer(this);
 
+    // TODO: remove after unification.
+    ActivityStack mActivityStack;
+
     /**
      * For {@link #prepareSurfaces}.
      */
@@ -150,10 +165,11 @@
     private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry =
             new AnimatingAppWindowTokenRegistry();
 
-    TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
+    TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack) {
         super(service);
         mStackId = stackId;
-        setController(controller);
+        mActivityStack = activityStack;
+        activityStack.registerConfigurationChangeListener(this);
         mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_minimize_thickness);
         EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
@@ -572,6 +588,49 @@
         positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers);
     }
 
+    void positionChildAt(Task child, int position) {
+        if (DEBUG_STACK) {
+            Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
+        }
+        if (child == null) {
+            if (DEBUG_STACK) {
+                Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
+            }
+            return;
+        }
+        child.positionAt(position);
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+    }
+
+    void positionChildAtTop(Task child, boolean includingParents) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        positionChildAt(POSITION_TOP, child, includingParents);
+
+        final DisplayContent displayContent = getDisplayContent();
+        if (displayContent.mAppTransition.isTransitionSet()) {
+            child.setSendingToBottom(false);
+        }
+        displayContent.layoutAndAssignWindowLayersIfNeeded();
+    }
+
+    void positionChildAtBottom(Task child, boolean includingParents) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        positionChildAt(POSITION_BOTTOM, child, includingParents);
+
+        if (getDisplayContent().mAppTransition.isTransitionSet()) {
+            child.setSendingToBottom(true);
+        }
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+    }
+
     @Override
     void positionChildAt(int position, Task child, boolean includingParents) {
         positionChildAt(position, child, includingParents, child.showForAllUsers());
@@ -596,6 +655,21 @@
         EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition);
     }
 
+    void reparent(int displayId, Rect outStackBounds, boolean onTop) {
+        final DisplayContent targetDc = mWmService.mRoot.getDisplayContent(displayId);
+        if (targetDc == null) {
+            throw new IllegalArgumentException("Trying to move stackId=" + mStackId
+                    + " to unknown displayId=" + displayId);
+        }
+
+        targetDc.moveStackToDisplay(this, onTop);
+        if (matchParentBounds()) {
+            outStackBounds.setEmpty();
+        } else {
+            getRawBounds(outStackBounds);
+        }
+    }
+
     // TODO: We should really have users as a window container in the hierarchy so that we don't
     // have to do complicated things like we are doing in this method.
     private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
@@ -725,6 +799,23 @@
     }
 
     /**
+     * Re-sizes a stack and its containing tasks.
+     *
+     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
+     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
+     * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
+     */
+    void resize(Rect bounds, SparseArray<Rect> taskBounds,
+            SparseArray<Rect> taskTempInsetBounds) {
+        // We might trigger a configuration change. Save the current task bounds for freezing.
+        prepareFreezingTaskBounds();
+        if (setBounds(bounds, taskBounds, taskTempInsetBounds) && isVisible()) {
+            getDisplayContent().setLayoutNeeded();
+            mWmService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+    }
+
+    /**
      * Calculate an amount by which to expand the stack bounds in each direction.
      * Used to make room for shadows in the pinned windowing mode.
      */
@@ -929,12 +1020,7 @@
                 (dockedStack == null || dockedStack == this) ? null : dockedStack.getRawBounds();
         getStackDockedModeBoundsLocked(mDisplayContent.getConfiguration(), dockedBounds,
                 null /* currentTempTaskBounds */, bounds, tempBounds);
-        getController().requestResize(bounds);
-    }
-
-    @Override
-    StackWindowController getController() {
-        return (StackWindowController) super.getController();
+        mActivityStack.requestResize(bounds);
     }
 
     @Override
@@ -947,6 +1033,14 @@
     }
 
     @Override
+    void removeImmediately() {
+        if (mActivityStack != null) {
+            mActivityStack.unregisterConfigurationChangeListener(this);
+        }
+        super.removeImmediately();
+    }
+
+    @Override
     void onParentSet() {
         super.onParentSet();
 
@@ -1572,14 +1666,13 @@
                 // I don't believe you...
             }
 
-            final PinnedStackWindowController controller =
-                    (PinnedStackWindowController) getController();
-            if (schedulePipModeChangedCallback && controller != null) {
+            if (schedulePipModeChangedCallback && mActivityStack != null) {
                 // We need to schedule the PiP mode change before the animation up. It is possible
                 // in this case for the animation down to not have been completed, so always
                 // force-schedule and update to the client to ensure that it is notified that it
                 // is no longer in picture-in-picture mode
-                controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
+                mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(null,
+                        forceUpdate);
             }
         }
         return true;
@@ -1592,12 +1685,10 @@
             // Update to the final bounds if requested. This is done here instead of in the bounds
             // animator to allow us to coordinate this after we notify the PiP mode changed
 
-            final PinnedStackWindowController controller =
-                    (PinnedStackWindowController) getController();
-            if (schedulePipModeChangedCallback && controller != null) {
+            if (schedulePipModeChangedCallback) {
                 // We need to schedule the PiP mode change after the animation down, so use the
                 // final bounds
-                controller.updatePictureInPictureModeForPinnedStackAnimation(
+                mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(
                         mBoundsAnimationTarget, false /* forceUpdate */);
             }
 
@@ -1624,6 +1715,135 @@
         }
     }
 
+    /**
+     * @return the current stack bounds transformed to the given {@param aspectRatio}. If
+     *         the default bounds is {@code null}, then the {@param aspectRatio} is applied to the
+     *         default bounds.
+     */
+    Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
+        if (!mWmService.mSupportsPictureInPicture) {
+            return null;
+        }
+
+        final DisplayContent displayContent = getDisplayContent();
+        if (displayContent == null) {
+            return null;
+        }
+
+        if (!inPinnedWindowingMode()) {
+            return null;
+        }
+
+        final PinnedStackController pinnedStackController =
+                displayContent.getPinnedStackController();
+        if (stackBounds == null) {
+            // Calculate the aspect ratio bounds from the default bounds
+            stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
+        }
+
+        if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
+            return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
+                    true /* useCurrentMinEdgeSize */);
+        } else {
+            return stackBounds;
+        }
+    }
+
+    /**
+     * Animates the pinned stack.
+     */
+    void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
+            int animationDuration, boolean fromFullscreen) {
+        if (!inPinnedWindowingMode()) {
+            return;
+        }
+        // Get the from-bounds
+        final Rect fromBounds = new Rect();
+        getBounds(fromBounds);
+
+        // Get non-null fullscreen to-bounds for animating if the bounds are null
+        @SchedulePipModeChangedState int schedulePipModeChangedState =
+                NO_PIP_MODE_CHANGED_CALLBACKS;
+        final boolean toFullscreen = toBounds == null;
+        if (toFullscreen) {
+            if (fromFullscreen) {
+                throw new IllegalArgumentException("Should not defer scheduling PiP mode"
+                        + " change on animation to fullscreen.");
+            }
+            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
+
+            mWmService.getStackBounds(
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
+            if (!mTmpToBounds.isEmpty()) {
+                // If there is a fullscreen bounds, use that
+                toBounds = new Rect(mTmpToBounds);
+            } else {
+                // Otherwise, use the display bounds
+                toBounds = new Rect();
+                getDisplayContent().getBounds(toBounds);
+            }
+        } else if (fromFullscreen) {
+            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
+        }
+
+        setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
+
+        final Rect finalToBounds = toBounds;
+        final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
+                schedulePipModeChangedState;
+        final DisplayContent displayContent = getDisplayContent();
+        displayContent.mBoundsAnimationController.getHandler().post(() -> {
+            displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
+                    finalToBounds, animationDuration, finalSchedulePipModeChangedState,
+                    fromFullscreen, toFullscreen);
+        });
+    }
+
+    /**
+     * Sets the current picture-in-picture aspect ratio.
+     */
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        if (!mWmService.mSupportsPictureInPicture) {
+            return;
+        }
+
+        if (!inPinnedWindowingMode()) {
+            return;
+        }
+
+        final PinnedStackController pinnedStackController =
+                getDisplayContent().getPinnedStackController();
+
+        if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
+            return;
+        }
+        getAnimationOrCurrentBounds(mTmpFromBounds);
+        mTmpToBounds.set(mTmpFromBounds);
+        getPictureInPictureBounds(aspectRatio, mTmpToBounds);
+        if (!mTmpToBounds.equals(mTmpFromBounds)) {
+            animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
+                    -1 /* duration */, false /* fromFullscreen */);
+        }
+        pinnedStackController.setAspectRatio(
+                pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+                        ? aspectRatio : -1f);
+    }
+
+    /**
+     * Sets the current picture-in-picture actions.
+     */
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        if (!mWmService.mSupportsPictureInPicture) {
+            return;
+        }
+
+        if (!inPinnedWindowingMode()) {
+            return;
+        }
+
+        getDisplayContent().getPinnedStackController().setActions(actions);
+    }
+
     @Override
     public boolean isAttached() {
         synchronized (mWmService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index b219419..2e5df45 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -31,6 +31,10 @@
 
 import com.android.server.wm.WindowManagerService.H;
 
+/**
+ * 1. Adjust the top most focus display if touch down on some display.
+ * 2. Adjust the pointer icon when cursor moves to the task bounds.
+ */
 public class TaskTapPointerEventListener implements PointerEventListener {
 
     private final Region mTouchExcludeRegion = new Region();
@@ -67,7 +71,7 @@
                     return;
                 }
                 WindowContainer parent = mDisplayContent.getParent();
-                if (parent != null) {
+                if (parent != null && parent.getTopChild() != mDisplayContent) {
                     parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent,
                             true /* includingParents */);
                 }
@@ -80,8 +84,7 @@
         if (motionEvent.getDisplayId() != getDisplayId()) {
             return;
         }
-        final int action = motionEvent.getAction();
-        switch (action & MotionEvent.ACTION_MASK) {
+        switch (motionEvent.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 final int x = (int) motionEvent.getX();
                 final int y = (int) motionEvent.getY();
@@ -97,7 +100,7 @@
                 }
             }
             break;
-
+            case MotionEvent.ACTION_HOVER_ENTER:
             case MotionEvent.ACTION_HOVER_MOVE: {
                 final int x = (int) motionEvent.getX();
                 final int y = (int) motionEvent.getY();
@@ -125,6 +128,7 @@
                     mPointerIconType = iconType;
                     if (mPointerIconType == TYPE_NOT_SPECIFIED) {
                         // Find the underlying window and ask it restore the pointer icon.
+                        mService.mH.removeMessages(H.RESTORE_POINTER_ICON);
                         mService.mH.obtainMessage(H.RESTORE_POINTER_ICON,
                                 x, y, mDisplayContent).sendToTarget();
                     } else {
@@ -133,6 +137,18 @@
                 }
             }
             break;
+            case MotionEvent.ACTION_HOVER_EXIT: {
+                final int x = (int) motionEvent.getX();
+                final int y = (int) motionEvent.getY();
+                if (mPointerIconType != TYPE_NOT_SPECIFIED) {
+                    mPointerIconType = TYPE_NOT_SPECIFIED;
+                    // Find the underlying window and ask it to restore the pointer icon.
+                    mService.mH.removeMessages(H.RESTORE_POINTER_ICON);
+                    mService.mH.obtainMessage(H.RESTORE_POINTER_ICON,
+                            x, y, mDisplayContent).sendToTarget();
+                }
+            }
+            break;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 32c5a3b..1905877 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -326,7 +326,7 @@
         }
 
         if (mSurfaceControl != null) {
-            mPendingTransaction.destroy(mSurfaceControl);
+            mPendingTransaction.reparent(mSurfaceControl, null);
 
             // Merge to parent transaction to ensure the transactions on this WindowContainer are
             // applied in native even if WindowContainer is removed.
@@ -437,6 +437,7 @@
                 if (mChildren.peekLast() != child) {
                     mChildren.remove(child);
                     mChildren.add(child);
+                    onChildPositionChanged();
                 }
                 if (includingParents && getParent() != null) {
                     getParent().positionChildAt(POSITION_TOP, this /* child */,
@@ -447,6 +448,7 @@
                 if (mChildren.peekFirst() != child) {
                     mChildren.remove(child);
                     mChildren.addFirst(child);
+                    onChildPositionChanged();
                 }
                 if (includingParents && getParent() != null) {
                     getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
@@ -460,8 +462,8 @@
                 //       doing this adjustment here and remove any adjustments in the callers.
                 mChildren.remove(child);
                 mChildren.add(position, child);
+                onChildPositionChanged();
         }
-        onChildPositionChanged();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 646fdd9..1691dc0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -158,8 +158,9 @@
         default boolean registerInputChannel(
                 DragState state, Display display, InputManagerService service,
                 InputChannel source) {
+            state.mTransferTouchFromToken = source.getToken();
             state.register(display);
-            return service.transferTouchFocus(source, state.getInputChannel());
+            return true;
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f5f55e2..fda7a85 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -723,7 +723,7 @@
 
         void updateSystemUiSettings() {
             boolean changed;
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext)
                         || PolicyControl.reloadFromSetting(mContext);
             }
@@ -1874,7 +1874,7 @@
             long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface, InsetsState outInsetsState) {
+            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -2047,7 +2047,7 @@
                 result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);
 
                 try {
-                    result = createSurfaceControl(outSurface, result, win, winAnimator);
+                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                 } catch (Exception e) {
                     displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
 
@@ -2078,7 +2078,7 @@
                     // handled yet, or it might want to draw a last frame. If we already have a
                     // surface, let the client use that, but don't create new surface at this point.
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
-                    winAnimator.mSurfaceController.getSurface(outSurface);
+                    winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 } else {
                     if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2086,7 +2086,7 @@
                     try {
                         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
                                 + win.mAttrs.getTitle());
-                        outSurface.release();
+                        outSurfaceControl.release();
                     } finally {
                         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                     }
@@ -2182,7 +2182,7 @@
                 + ", requestedHeight=" + requestedHeight
                 + ", viewVisibility=" + viewVisibility
                 + "\nRelayout returning frame=" + outFrame
-                + ", surface=" + outSurface);
+                + ", surface=" + outSurfaceControl);
 
             if (localLOGV || DEBUG_FOCUS) Slog.v(
                 TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
@@ -2252,7 +2252,7 @@
         return focusMayChange;
     }
 
-    private int createSurfaceControl(Surface outSurface, int result, WindowState win,
+    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win,
             WindowStateAnimator winAnimator) {
         if (!win.mHasSurface) {
             result |= RELAYOUT_RES_SURFACE_CHANGED;
@@ -2266,13 +2266,13 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
         if (surfaceController != null) {
-            surfaceController.getSurface(outSurface);
-            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurface + ": copied");
+            surfaceController.getSurfaceControl(outSurfaceControl);
+            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurfaceControl + ": copied");
         } else {
             // For some reason there isn't a surface.  Clear the
             // caller's object so they see the same state.
             Slog.w(TAG_WM, "Failed to create surface control for " + win);
-            outSurface.release();
+            outSurfaceControl.release();
         }
 
         return result;
@@ -2629,12 +2629,23 @@
     @Override
     public void onUserSwitched() {
         mSettingsObserver.updateSystemUiSettings();
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             // force a re-application of focused window sysui visibility on each display.
             mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw);
         }
     }
 
+    @Override
+    public void moveDisplayToTop(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent != null && mRoot.getTopChild() != displayContent) {
+                mRoot.positionChildAt(WindowContainer.POSITION_TOP, displayContent,
+                        true /* includingParents */);
+            }
+        }
+    }
+
     /**
      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
      * performance, only one layout pass should be done. This can be called multiple times, and
@@ -2651,28 +2662,6 @@
     }
 
     /**
-     * @return true if the activity contains windows that have
-     *         {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set
-     */
-    public boolean containsShowWhenLockedWindow(IBinder token) {
-        synchronized (mGlobalLock) {
-            final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
-            return wtoken != null && wtoken.containsShowWhenLockedWindow();
-        }
-    }
-
-    /**
-     * @return true if the activity contains windows that have
-     *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
-     */
-    public boolean containsDismissKeyguardWindow(IBinder token) {
-        synchronized (mGlobalLock) {
-            final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
-            return wtoken != null && wtoken.containsDismissKeyguardWindow();
-        }
-    }
-
-    /**
      * Notifies activity manager that some Keyguard flags have changed and that it needs to
      * reevaluate the visibilities of the activities.
      * @param callback Runnable to be called when activity manager is done reevaluating visibilities
@@ -5738,17 +5727,6 @@
     }
 
     /**
-     * Returns true if the callingUid has any window currently visible to the user.
-     */
-    public boolean isAnyWindowVisibleForUid(int callingUid) {
-        synchronized (mGlobalLock) {
-            return mRoot.forAllWindows(w -> {
-                return w.getOwningUid() == callingUid && w.isVisible();
-            }, true /* traverseTopToBottom */);
-        }
-    }
-
-    /**
      * Called when a task has been removed from the recent tasks list.
      * <p>
      * Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
@@ -5965,8 +5943,6 @@
                     pw.print(" apps="); pw.print(mAppsFreezingScreen);
             final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
             pw.print("  mRotation="); pw.print(defaultDisplayContent.getRotation());
-                    pw.print(" mAltOrientation=");
-                            pw.println(defaultDisplayContent.getAltOrientation());
             pw.print("  mLastWindowForcedOrientation=");
                     pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
                     pw.print(" mLastOrientation=");
@@ -6166,7 +6142,7 @@
                     dumpWindowsLocked(pw, true, null);
                 }
                 return;
-            } else if ("all".equals(cmd) || "a".equals(cmd)) {
+            } else if ("all".equals(cmd)) {
                 synchronized (mGlobalLock) {
                     dumpWindowsLocked(pw, true, null);
                 }
@@ -6412,7 +6388,7 @@
     }
 
     void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e78c12c..8f86c00 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1373,12 +1373,15 @@
 
     @Override
     boolean isVisible() {
-        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility;
+        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility
+                // If we don't have a provider, this window isn't used as a window generating
+                // insets, so nobody can hide it over the inset APIs.
+                && (mInsetProvider == null || mInsetProvider.isClientVisible());
     }
 
     /**
-     * @return True if the window would be visible if we'd ignore policy visibility, false
-     *         otherwise.
+     * @return {@code true} if the window would be visible if we'd ignore policy visibility,
+     *         {@code false} otherwise.
      */
     boolean wouldBeVisibleIfPolicyIgnored() {
         return mHasSurface && !isParentWindowHidden()
@@ -2402,6 +2405,14 @@
                 && !cantReceiveTouchInput();
     }
 
+    @Override
+    public boolean canShowWhenLocked() {
+        final boolean showBecauseOfActivity =
+                mAppToken != null && mAppToken.mActivityRecord.canShowWhenLocked();
+        final boolean showBecauseOfWindow = (getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+        return showBecauseOfActivity || showBecauseOfWindow;
+    }
+
     /** @return false if this window desires touch events. */
     boolean cantReceiveTouchInput() {
         return mAppToken != null && mAppToken.getTask() != null
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index ce627e2..c2a8e7e 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -478,8 +478,8 @@
         return mSurfaceControl.getHandle();
     }
 
-    void getSurface(Surface outSurface) {
-        outSurface.copyFrom(mSurfaceControl);
+    void getSurfaceControl(SurfaceControl outSurfaceControl) {
+        outSurfaceControl.copyFrom(mSurfaceControl);
     }
 
     int getLayer() {
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
new file mode 100644
index 0000000..936ee85
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+
+import android.os.Trace;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Buffer used for window tracing.
+ */
+abstract class WindowTraceBuffer {
+    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+    final Object mBufferSizeLock = new Object();
+    final BlockingQueue<byte[]> mBuffer;
+    int mBufferSize;
+    private final int mBufferCapacity;
+    private final File mTraceFile;
+
+    WindowTraceBuffer(int size, File traceFile) throws IOException {
+        mBufferCapacity = size;
+        mTraceFile = traceFile;
+        mBuffer = new LinkedBlockingQueue<>();
+
+        initTraceFile();
+    }
+
+    int getAvailableSpace() {
+        return mBufferCapacity - mBufferSize;
+    }
+
+    /**
+     * Inserts the specified element into this buffer.
+     *
+     * This method is synchronized with {@code #take()} and {@code #clear()}
+     * for consistency.
+     *
+     * @param proto the element to add
+     * @return {@code true} if the inserted item was inserted into the buffer
+     * @throws IllegalStateException if the element cannot be added because it is larger
+     *                               than the buffer size.
+     */
+    boolean add(ProtoOutputStream proto) throws InterruptedException {
+        byte[] protoBytes = proto.getBytes();
+        int protoLength = protoBytes.length;
+        if (protoLength > mBufferCapacity) {
+            throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
+                    + mBufferCapacity + " Object size: " + protoLength);
+        }
+        synchronized (mBufferSizeLock) {
+            boolean canAdd = canAdd(protoBytes);
+            if (canAdd) {
+                mBuffer.offer(protoBytes);
+                mBufferSize += protoLength;
+            }
+            return canAdd;
+        }
+    }
+
+    void writeNextBufferElementToFile() throws IOException {
+        byte[] proto;
+        try {
+            proto = take();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return;
+        }
+
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
+            try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
+                os.write(proto);
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+
+    /**
+     * Retrieves and removes the head of this queue, waiting if necessary
+     * until an element becomes available.
+     *
+     * This method is synchronized with {@code #add(ProtoOutputStream)} and {@code #clear()}
+     * for consistency.
+     *
+     * @return the head of this buffer, or {@code null} if this buffer is empty
+     */
+    private byte[] take() throws InterruptedException {
+        byte[] item = mBuffer.take();
+        synchronized (mBufferSizeLock) {
+            mBufferSize -= item.length;
+            return item;
+        }
+    }
+
+    private void initTraceFile() throws IOException {
+        mTraceFile.delete();
+        try (OutputStream os = new FileOutputStream(mTraceFile)) {
+            mTraceFile.setReadable(true, false);
+            ProtoOutputStream proto = new ProtoOutputStream(os);
+            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+            proto.flush();
+        }
+    }
+
+    /**
+     * Checks if the element can be added to the buffer. The element is already certain to be
+     * smaller than the overall buffer size.
+     *
+     * @param protoBytes byte array representation of the Proto object to add
+     * @return <tt>true<</tt> if the element can be added to the buffer or not
+     */
+    abstract boolean canAdd(byte[] protoBytes) throws InterruptedException;
+
+    /**
+     * Flush all buffer content to the disk.
+     *
+     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
+     */
+    abstract void writeToDisk() throws IOException, InterruptedException;
+
+    /**
+     * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceQueueBuffer}
+     */
+    static class Builder {
+        private File mTraceFile;
+        private int mBufferCapacity;
+
+
+        Builder setTraceFile(File traceFile) {
+            mTraceFile = traceFile;
+            return this;
+        }
+
+        Builder setBufferCapacity(int size) {
+            mBufferCapacity = size;
+            return this;
+        }
+
+        File getFile() {
+            return mTraceFile;
+        }
+
+        WindowTraceBuffer build() throws IOException {
+            if (mBufferCapacity <= 0) {
+                throw new IllegalStateException("Buffer capacity must be greater than 0.");
+            }
+
+            if (mTraceFile == null) {
+                throw new IllegalArgumentException("A valid trace file must be specified.");
+            }
+
+            return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
new file mode 100644
index 0000000..b7fc7ac
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static android.os.Build.IS_USER;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first
+ * {@code #size size} bytes of window trace elements.
+ * Once the buffer is full it will no longer accepts new elements.
+ */
+class WindowTraceQueueBuffer extends WindowTraceBuffer {
+    private Thread mWriterThread;
+    private boolean mCancel;
+
+    @VisibleForTesting
+    WindowTraceQueueBuffer(int size, File traceFile, boolean startWriterThread) throws IOException {
+        super(size, traceFile);
+        if (startWriterThread) {
+            initializeWriterThread();
+        }
+    }
+
+    WindowTraceQueueBuffer(int size, File traceFile) throws IOException {
+        this(size, traceFile, !IS_USER);
+    }
+
+    private void initializeWriterThread() {
+        mCancel = false;
+        mWriterThread = new Thread(() -> {
+            try {
+                loop();
+            } catch (IOException e) {
+                throw new IllegalStateException("Failed to execute trace write loop thread", e);
+            }
+        }, "window_tracing");
+        mWriterThread.start();
+    }
+
+    private void loop() throws IOException {
+        while (!mCancel) {
+            writeNextBufferElementToFile();
+        }
+    }
+
+    private void restartWriterThread() throws InterruptedException {
+        if (mWriterThread != null) {
+            mCancel = true;
+            mWriterThread.interrupt();
+            mWriterThread.join();
+            initializeWriterThread();
+        }
+    }
+
+    @Override
+    boolean canAdd(byte[] protoBytes) {
+        long availableSpace = getAvailableSpace();
+        return availableSpace >= protoBytes.length;
+    }
+
+    @Override
+    void writeToDisk() throws InterruptedException {
+        while (!mBuffer.isEmpty()) {
+            mBufferSizeLock.wait();
+            mBufferSizeLock.notify();
+        }
+        restartWriterThread();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 8fa56bb..63539c4 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -17,31 +17,23 @@
 package com.android.server.wm;
 
 import static android.os.Build.IS_USER;
+
 import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
 import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
 import static com.android.server.wm.WindowManagerTraceProto.WHERE;
 import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.annotation.Nullable;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
 
 /**
  * A class that allows window manager to dump its state continuously to a trace file, such that a
@@ -49,35 +41,42 @@
  */
 class WindowTracing {
 
+    /**
+     * Maximum buffer size, currently defined as 512 KB
+     * Size was experimentally defined to fit between 100 to 150 elements.
+     */
+    private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
     private static final String TAG = "WindowTracing";
-    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
 
     private final Object mLock = new Object();
-    private final File mTraceFile;
-    private final BlockingQueue<ProtoOutputStream> mWriteQueue = new ArrayBlockingQueue<>(200);
+    private final WindowTraceBuffer.Builder mBufferBuilder;
+
+    private WindowTraceBuffer mTraceBuffer;
 
     private boolean mEnabled;
     private volatile boolean mEnabledLockFree;
 
     WindowTracing(File file) {
-        mTraceFile = file;
+        mBufferBuilder = new WindowTraceBuffer.Builder()
+                .setTraceFile(file)
+                .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
     }
 
     void startTrace(@Nullable PrintWriter pw) throws IOException {
-        if (IS_USER){
+        if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
         synchronized (mLock) {
-            logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
-            mWriteQueue.clear();
-            mTraceFile.delete();
-            try (OutputStream os = new FileOutputStream(mTraceFile)) {
-                mTraceFile.setReadable(true, false);
-                ProtoOutputStream proto = new ProtoOutputStream(os);
-                proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
-                proto.flush();
+            logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + ".");
+            if (mTraceBuffer != null) {
+                try {
+                    mTraceBuffer.writeToDisk();
+                } catch (InterruptedException e) {
+                    logAndPrintln(pw, "Error: Unable to flush the previous buffer.");
+                }
             }
+            mTraceBuffer = mBufferBuilder.build();
             mEnabled = mEnabledLockFree = true;
         }
     }
@@ -91,67 +90,42 @@
     }
 
     void stopTrace(@Nullable PrintWriter pw) {
-        if (IS_USER){
+        if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
         synchronized (mLock) {
-            logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
+            logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile()
+                    + ". Waiting for traces to flush.");
             mEnabled = mEnabledLockFree = false;
-            while (!mWriteQueue.isEmpty()) {
+
+            synchronized (mLock) {
                 if (mEnabled) {
                     logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
                     throw new IllegalStateException("tracing enabled while waiting for flush.");
                 }
                 try {
-                    mLock.wait();
-                    mLock.notify();
+                    mTraceBuffer.writeToDisk();
+                } catch (IOException e) {
+                    Log.e(TAG, "Unable to write buffer to file", e);
                 } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
+                    Log.e(TAG, "Unable to interrupt window tracing file write thread", e);
                 }
             }
-            logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+            logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + ".");
         }
     }
 
-    void appendTraceEntry(ProtoOutputStream proto) {
+    private void appendTraceEntry(ProtoOutputStream proto) {
         if (!mEnabledLockFree) {
             return;
         }
 
-        if (!mWriteQueue.offer(proto)) {
-            Log.e(TAG, "Dropping window trace entry, queue full");
-        }
-    }
-
-    void loop() {
-        for (;;) {
-            loopOnce();
-        }
-    }
-
-    @VisibleForTesting
-    void loopOnce() {
-        ProtoOutputStream proto;
         try {
-            proto = mWriteQueue.take();
+            mTraceBuffer.add(proto);
         } catch (InterruptedException e) {
+            Log.e(TAG, "Unable to add element to trace", e);
             Thread.currentThread().interrupt();
-            return;
-        }
-
-        synchronized (mLock) {
-            try {
-                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
-                try (OutputStream os = new FileOutputStream(mTraceFile, true /* append */)) {
-                    os.write(proto.getBytes());
-                }
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to write file " + mTraceFile, e);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-            }
-            mLock.notify();
         }
     }
 
@@ -161,11 +135,7 @@
 
     static WindowTracing createDefaultAndStartLooper(Context context) {
         File file = new File("/data/misc/wmtrace/wm_trace.pb");
-        WindowTracing windowTracing = new WindowTracing(file);
-        if (!IS_USER){
-            new Thread(windowTracing::loop, "window_tracing").start();
-        }
-        return windowTracing;
+        return new WindowTracing(file);
     }
 
     int onShellCommand(ShellCommand shell, String cmd) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b4fe837..fb00aeb 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -107,6 +107,8 @@
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
+        "android.hardware.gnss.visibility_control@1.0",
+        "android.hardware.input.classifier@1.0",
         "android.hardware.ir@1.0",
         "android.hardware.light@2.0",
         "android.hardware.power@1.0",
@@ -119,6 +121,7 @@
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
         "android.hardware.vr@1.0",
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index defcfd9..63dca62 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -22,6 +22,7 @@
 #include <android/hardware/vibrator/1.1/types.h>
 #include <android/hardware/vibrator/1.2/IVibrator.h>
 #include <android/hardware/vibrator/1.2/types.h>
+#include <android/hardware/vibrator/1.3/IVibrator.h>
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
@@ -42,6 +43,7 @@
 namespace V1_0 = android::hardware::vibrator::V1_0;
 namespace V1_1 = android::hardware::vibrator::V1_1;
 namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
 
 namespace android {
 
@@ -136,6 +138,19 @@
     }
 }
 
+static jboolean vibratorSupportsExternalControl(JNIEnv*, jobject) {
+    return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false);
+}
+
+static void vibratorSetExternalControl(JNIEnv*, jobject, jboolean enabled) {
+    Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled))
+        .withDefault(Status::UNKNOWN_ERROR);
+    if (status != Status::OK) {
+      ALOGE("Failed to set vibrator external control (%" PRIu32 ").",
+            static_cast<uint32_t>(status));
+    }
+}
+
 static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength) {
     Status status;
     uint32_t lengthMs;
@@ -187,7 +202,9 @@
     { "vibratorOff", "()V", (void*)vibratorOff },
     { "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
     { "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
-    { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect}
+    { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect},
+    { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
+    { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
 };
 
 int register_android_server_VibratorService(JNIEnv *env)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 43d2dcf..90c9cc2 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,9 +107,11 @@
     jmethodID getLongPressTimeout;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
+    jmethodID getPointerDisplayId;
     jmethodID getKeyboardLayoutOverlay;
     jmethodID getDeviceAlias;
     jmethodID getTouchCalibrationForInputDevice;
+    jmethodID getContextForDisplay;
 } gServiceClassInfo;
 
 static struct {
@@ -174,15 +176,6 @@
     loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
 }
 
-static void updatePointerControllerFromViewport(
-        sp<PointerController> controller, const DisplayViewport* const viewport) {
-    if (controller != nullptr && viewport != nullptr) {
-        const int32_t width = viewport->logicalRight - viewport->logicalLeft;
-        const int32_t height = viewport->logicalBottom - viewport->logicalTop;
-        controller->setDisplayViewport(width, height, viewport->orientation);
-    }
-}
-
 enum {
     WM_ACTION_PASS_TO_USER = 1,
 };
@@ -252,7 +245,7 @@
             const sp<IBinder>& token,
             const std::string& reason);
     virtual void notifyInputChannelBroken(const sp<IBinder>& token);
-    virtual void notifyFocusChanged(const sp<IBinder>& token);
+    virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken);
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
@@ -268,17 +261,16 @@
 
     /* --- PointerControllerPolicyInterface implementation --- */
 
-    virtual void loadPointerIcon(SpriteIcon* icon);
-    virtual void loadPointerResources(PointerResources* outResources);
+    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId);
+    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId);
     virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
-            std::map<int32_t, PointerAnimation>* outAnimationResources);
+            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId);
     virtual int32_t getDefaultPointerIconId();
     virtual int32_t getCustomPointerIconId();
 
 private:
     sp<InputManager> mInputManager;
 
-    jobject mContextObj;
     jobject mServiceObj;
     sp<Looper> mLooper;
 
@@ -310,14 +302,19 @@
 
         // Input devices to be disabled
         SortedVector<int32_t> disabledInputDevices;
+
+        // Associated Pointer controller display.
+        int32_t pointerDisplayId;
     } mLocked GUARDED_BY(mLock);
 
     std::atomic<bool> mInteractive;
 
-    void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
+    void updateInactivityTimeoutLocked();
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
     void ensureSpriteControllerLocked();
-
+    const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
+    int32_t getPointerDisplayId();
+    void updatePointerDisplayLocked();
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
     static inline JNIEnv* jniEnv() {
@@ -332,7 +329,6 @@
         mLooper(looper), mInteractive(true) {
     JNIEnv* env = jniEnv();
 
-    mContextObj = env->NewGlobalRef(contextObj);
     mServiceObj = env->NewGlobalRef(serviceObj);
 
     {
@@ -342,6 +338,7 @@
         mLocked.pointerGesturesEnabled = true;
         mLocked.showTouches = false;
         mLocked.pointerCapture = false;
+        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
     }
     mInteractive = true;
 
@@ -353,7 +350,6 @@
 NativeInputManager::~NativeInputManager() {
     JNIEnv* env = jniEnv();
 
-    env->DeleteGlobalRef(mContextObj);
     env->DeleteGlobalRef(mServiceObj);
 }
 
@@ -391,9 +387,10 @@
     return false;
 }
 
-static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
-    for (const DisplayViewport& v : viewports) {
-        if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
+        REQUIRES(mLock) {
+    for (const DisplayViewport& v : mLocked.viewports) {
+        if (v.displayId == displayId) {
             return &v;
         }
     }
@@ -420,20 +417,14 @@
         }
     }
 
-    const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
-    {
+    // Get the preferred pointer controller displayId.
+    int32_t pointerDisplayId = getPointerDisplayId();
+
+    { // acquire lock
         AutoMutex _l(mLock);
-        const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
-        // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
-        // if they are different.
-        const bool internalViewportChanged = (newInternalViewport != nullptr) &&
-                (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
-        if (internalViewportChanged) {
-            sp<PointerController> controller = mLocked.pointerController.promote();
-            updatePointerControllerFromViewport(controller, newInternalViewport);
-        }
         mLocked.viewports = viewports;
-    }
+        mLocked.pointerDisplayId = pointerDisplayId;
+    } // release lock
 
     mInputManager->getReader()->requestRefreshConfiguration(
             InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -556,15 +547,42 @@
 
         controller = new PointerController(this, mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
-
-        const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
-        updatePointerControllerFromViewport(controller, internalViewport);
-
-        updateInactivityTimeoutLocked(controller);
+        updateInactivityTimeoutLocked();
     }
+
+    updatePointerDisplayLocked();
+
     return controller;
 }
 
+int32_t NativeInputManager::getPointerDisplayId() {
+    JNIEnv* env = jniEnv();
+    jint pointerDisplayId = env->CallIntMethod(mServiceObj,
+            gServiceClassInfo.getPointerDisplayId);
+    if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
+        pointerDisplayId = ADISPLAY_ID_DEFAULT;
+    }
+
+    return pointerDisplayId;
+}
+
+void NativeInputManager::updatePointerDisplayLocked() REQUIRES(mLock) {
+    ATRACE_CALL();
+
+    sp<PointerController> controller = mLocked.pointerController.promote();
+    if (controller != nullptr) {
+        const DisplayViewport* viewport = findDisplayViewportLocked(mLocked.pointerDisplayId);
+        if (viewport == nullptr) {
+            ALOGW("Can't find pointer display viewport, fallback to default display.");
+            viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
+        }
+
+        if (viewport != nullptr) {
+            controller->setDisplayViewport(*viewport);
+        }
+    }
+}
+
 void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
     if (mLocked.spriteController == nullptr) {
         JNIEnv* env = jniEnv();
@@ -718,7 +736,8 @@
     }
 }
 
-void NativeInputManager::notifyFocusChanged(const sp<IBinder>& token) {
+void NativeInputManager::notifyFocusChanged(const sp<IBinder>& oldToken,
+        const sp<IBinder>& newToken) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     ALOGD("notifyFocusChanged");
 #endif
@@ -726,12 +745,11 @@
 
     JNIEnv* env = jniEnv();
 
-    jobject tokenObj = javaObjectForIBinder(env, token);
-    if (tokenObj) {
-        env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
-                tokenObj);
-        checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
-    }
+    jobject oldTokenObj = javaObjectForIBinder(env, oldToken);
+    jobject newTokenObj = javaObjectForIBinder(env, newToken);
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
+            oldTokenObj, newTokenObj);
+    checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
 }
 
 void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
@@ -821,16 +839,16 @@
 
     if (mLocked.systemUiVisibility != visibility) {
         mLocked.systemUiVisibility = visibility;
-
-        sp<PointerController> controller = mLocked.pointerController.promote();
-        if (controller != nullptr) {
-            updateInactivityTimeoutLocked(controller);
-        }
+        updateInactivityTimeoutLocked();
     }
 }
 
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
-        REQUIRES(mLock) {
+void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
+    sp<PointerController> controller = mLocked.pointerController.promote();
+    if (controller == nullptr) {
+        return;
+    }
+
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
     controller->setInactivityTimeout(lightsOut
             ? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1182,19 +1200,22 @@
     return result;
 }
 
-void NativeInputManager::loadPointerIcon(SpriteIcon* icon) {
+void NativeInputManager::loadPointerIcon(SpriteIcon* icon, int32_t displayId) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
 
     ScopedLocalRef<jobject> pointerIconObj(env, env->CallObjectMethod(
-            mServiceObj, gServiceClassInfo.getPointerIcon));
+            mServiceObj, gServiceClassInfo.getPointerIcon, displayId));
     if (checkAndClearExceptionFromCallback(env, "getPointerIcon")) {
         return;
     }
 
+    ScopedLocalRef<jobject> displayContext(env, env->CallObjectMethod(
+            mServiceObj, gServiceClassInfo.getContextForDisplay, displayId));
+
     PointerIcon pointerIcon;
     status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
-                                                    mContextObj, &pointerIcon);
+            displayContext.get(), &pointerIcon);
     if (!status && !pointerIcon.isNullIcon()) {
         *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
     } else {
@@ -1202,28 +1223,34 @@
     }
 }
 
-void NativeInputManager::loadPointerResources(PointerResources* outResources) {
+void NativeInputManager::loadPointerResources(PointerResources* outResources, int32_t displayId) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
 
-    loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_HOVER,
+    ScopedLocalRef<jobject> displayContext(env, env->CallObjectMethod(
+            mServiceObj, gServiceClassInfo.getContextForDisplay, displayId));
+
+    loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_HOVER,
             &outResources->spotHover);
-    loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_TOUCH,
+    loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_TOUCH,
             &outResources->spotTouch);
-    loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_ANCHOR,
+    loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_ANCHOR,
             &outResources->spotAnchor);
 }
 
 void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
-        std::map<int32_t, PointerAnimation>* outAnimationResources) {
+        std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
 
+    ScopedLocalRef<jobject> displayContext(env, env->CallObjectMethod(
+            mServiceObj, gServiceClassInfo.getContextForDisplay, displayId));
+
     for (int iconId = POINTER_ICON_STYLE_CONTEXT_MENU; iconId <= POINTER_ICON_STYLE_GRABBING;
              ++iconId) {
         PointerIcon pointerIcon;
         loadSystemIconAsSpriteWithPointerIcon(
-                env, mContextObj, iconId, &pointerIcon, &((*outResources)[iconId]));
+                env, displayContext.get(), iconId, &pointerIcon, &((*outResources)[iconId]));
         if (!pointerIcon.bitmapFrames.empty()) {
             PointerAnimation& animationData = (*outAnimationResources)[iconId];
             size_t numFrames = pointerIcon.bitmapFrames.size() + 1;
@@ -1238,7 +1265,7 @@
             }
         }
     }
-    loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_NULL,
+    loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_NULL,
             &((*outResources)[POINTER_ICON_STYLE_NULL]));
 }
 
@@ -1480,27 +1507,6 @@
     im->setSystemUiVisibility(visibility);
 }
 
-static jboolean nativeTransferTouchFocus(JNIEnv* env,
-        jclass /* clazz */, jlong ptr, jobject fromChannelObj, jobject toChannelObj) {
-    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
-    sp<InputChannel> fromChannel =
-            android_view_InputChannel_getInputChannel(env, fromChannelObj);
-    sp<InputChannel> toChannel =
-            android_view_InputChannel_getInputChannel(env, toChannelObj);
-
-    if (fromChannel == nullptr || toChannel == nullptr) {
-        return JNI_FALSE;
-    }
-
-    if (im->getInputManager()->getDispatcher()->
-            transferTouchFocus(fromChannel, toChannel)) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
-}
-
 static void nativeSetPointerSpeed(JNIEnv* /* env */,
         jclass /* clazz */, jlong ptr, jint speed) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1688,8 +1694,6 @@
             (void*) nativeSetInputDispatchMode },
     { "nativeSetSystemUiVisibility", "(JI)V",
             (void*) nativeSetSystemUiVisibility },
-    { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z",
-            (void*) nativeTransferTouchFocus },
     { "nativeSetPointerSpeed", "(JI)V",
             (void*) nativeSetPointerSpeed },
     { "nativeSetShowTouches", "(JZ)V",
@@ -1765,7 +1769,7 @@
             "notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
     
     GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
-            "notifyFocusChanged", "(Landroid/os/IBinder;)V");
+            "notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V");
 
     GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
             "notifyANR",
@@ -1822,7 +1826,10 @@
             "getPointerLayer", "()I");
 
     GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
-            "getPointerIcon", "()Landroid/view/PointerIcon;");
+            "getPointerIcon", "(I)Landroid/view/PointerIcon;");
+
+    GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
+            "getPointerDisplayId", "()I");
 
     GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
             "getKeyboardLayoutOverlay",
@@ -1835,6 +1842,10 @@
             "getTouchCalibrationForInputDevice",
             "(Ljava/lang/String;I)Landroid/hardware/input/TouchCalibration;");
 
+    GET_METHOD_ID(gServiceClassInfo.getContextForDisplay, clazz,
+            "getContextForDisplay",
+            "(I)Landroid/content/Context;")
+
     // InputDevice
 
     FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 6d8fc1c..f3c19d0 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -26,6 +26,7 @@
 #include <android/hardware/gnss/1.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/2.0/IGnssMeasurement.h>
 #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
+#include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 #include "hardware_legacy/power.h"
@@ -80,7 +81,7 @@
 static jmethodID method_correctionSatConstType;
 static jmethodID method_correctionSatId;
 static jmethodID method_correctionSatCarrierFreq;
-static jmethodID method_correctionSatIsLos;
+static jmethodID method_correctionSatIsLosProb;
 static jmethodID method_correctionSatEpl;
 static jmethodID method_correctionSatEplUnc;
 static jmethodID method_correctionSatRefPlane;
@@ -88,6 +89,8 @@
 static jmethodID method_correctionPlaneLngDeg;
 static jmethodID method_correctionPlaneAltDeg;
 static jmethodID method_correctionPlaneAzimDeg;
+static jmethodID method_reportNfwNotification;
+static jmethodID method_isInEmergencySession;
 
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -135,6 +138,7 @@
 using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
 using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
 using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
+using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
 using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
 using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
 using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
@@ -151,6 +155,9 @@
 using IMeasurementCorrections =
     android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
 
+using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
+using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
+
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
     // hidl_death_recipient interface
@@ -179,6 +186,7 @@
 sp<IGnssDebug> gnssDebugIface = nullptr;
 sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
 sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
+sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
 sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
 sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
@@ -188,7 +196,7 @@
 // This boolean is needed to ensure that Gnsss Measurement Corrections related method are only
 // initalized when needed which will be few devices initially
 bool firstGnssMeasurementCorrectionInjected = false;
-
+sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr;
 
 #define WAKE_LOCK_NAME  "GPS"
 
@@ -313,6 +321,15 @@
     }
 }
 
+static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
+    jclass versionClass =
+            env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
+    jmethodID versionCtor = env->GetMethodID(versionClass, "<init>", "(II)V");
+    jobject version = env->NewObject(versionClass, versionCtor, major, minor);
+    env->DeleteLocalRef(versionClass);
+    return version;
+}
+
 struct ScopedJniString {
     ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) {
         mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr);
@@ -845,7 +862,7 @@
 
 Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0(
         const IGnssMeasurementCallback_V2_0::GnssData& data) {
-    // TODO(b/119571122): implement gnssMeasurementCb_2_0
+    translateAndSetGnssData(data);
     return Void();
 }
 
@@ -883,9 +900,8 @@
     return data.measurementCount;
 }
 
-template<>
-size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_1::GnssData>
-        (const IGnssMeasurementCallback_V1_1::GnssData& data) {
+template<class T>
+size_t GnssMeasurementCallback::getMeasurementCount(const T& data) {
     return data.measurements.size();
 }
 
@@ -947,6 +963,17 @@
             ADR_STATE_HALF_CYCLE_REPORTED));
 }
 
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+        <IGnssMeasurementCallback_V2_0::GnssMeasurement>(
+        const IGnssMeasurementCallback_V2_0::GnssMeasurement* measurement_V2_0,
+        JavaObject& object) {
+    translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object);
+
+    SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType)));
+}
+
 jobject GnssMeasurementCallback::translateGnssClock(
        JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock) {
     JavaObject object(env, "android/location/GnssClock");
@@ -1069,6 +1096,54 @@
 }
 
 /*
+ * GnssVisibilityControlCallback implements callback methods of IGnssVisibilityControlCallback.hal.
+ */
+struct GnssVisibilityControlCallback : public IGnssVisibilityControlCallback {
+    Return<void> nfwNotifyCb(const IGnssVisibilityControlCallback::NfwNotification& notification)
+            override;
+    Return<bool> isInEmergencySession() override;
+};
+
+Return<void> GnssVisibilityControlCallback::nfwNotifyCb(
+        const IGnssVisibilityControlCallback::NfwNotification& notification) {
+    JNIEnv* env = getJniEnv();
+    jstring proxyAppPackageName = env->NewStringUTF(notification.proxyAppPackageName.c_str());
+    jstring otherProtocolStackName = env->NewStringUTF(notification.otherProtocolStackName.c_str());
+    jstring requestorId = env->NewStringUTF(notification.requestorId.c_str());
+
+    if (proxyAppPackageName && otherProtocolStackName && requestorId) {
+        env->CallVoidMethod(mCallbacksObj, method_reportNfwNotification, proxyAppPackageName,
+                            notification.protocolStack, otherProtocolStackName,
+                            notification.requestor, requestorId,
+                            notification.inEmergencyMode, notification.isCachedLocation);
+    } else {
+        ALOGE("%s: OOM Error\n", __func__);
+    }
+
+    if (requestorId) {
+        env->DeleteLocalRef(requestorId);
+    }
+
+    if (otherProtocolStackName) {
+        env->DeleteLocalRef(otherProtocolStackName);
+    }
+
+    if (proxyAppPackageName) {
+        env->DeleteLocalRef(proxyAppPackageName);
+    }
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+Return<bool> GnssVisibilityControlCallback::isInEmergencySession() {
+    JNIEnv* env = getJniEnv();
+    auto result = env->CallBooleanMethod(mCallbacksObj, method_isInEmergencySession);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+/*
  * AGnssCallback_V1_0 implements callback methods required by the IAGnssCallback 1.0 interface.
  */
 struct AGnssCallback_V1_0 : public IAGnssCallback_V1_0 {
@@ -1302,6 +1377,9 @@
             "reportLocationBatch",
             "([Landroid/location/Location;)V");
     method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+    method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
+            "(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V");
+    method_isInEmergencySession = env->GetMethodID(clazz, "isInEmergencySession", "()Z");
 
     /*
      * Save a pointer to JVM.
@@ -1377,12 +1455,6 @@
     if (gnssHal_V2_0 != nullptr) {
         // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0
         auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
-        auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
-        if (!gnssCorrections.isOk()) {
-            ALOGD("Unable to get a handle to GnssMeasurementCorrections interface");
-        } else {
-            gnssCorrectionsIface = gnssCorrections;
-        }
         if (!gnssMeasurement.isOk()) {
             ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
         } else {
@@ -1390,6 +1462,12 @@
             gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0;
             gnssMeasurementIface = gnssMeasurementIface_V2_0;
         }
+        auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
+        if (!gnssCorrections.isOk()) {
+            ALOGD("Unable to get a handle to GnssMeasurementCorrections interface");
+        } else {
+            gnssCorrectionsIface = gnssCorrections;
+        }
     } else if (gnssHal_V1_1 != nullptr) {
          auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
          if (!gnssMeasurement.isOk()) {
@@ -1421,10 +1499,19 @@
         gnssNiIface = gnssNi;
     }
 
-    if (gnssHal_V1_1 != nullptr) {
+    if (gnssHal_V2_0 != nullptr) {
+        auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
+        if (!gnssConfiguration.isOk()) {
+            ALOGD("Unable to get a handle to GnssConfiguration_V2_0");
+        } else {
+            gnssConfigurationIface_V2_0 = gnssConfiguration;
+            gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_0;
+            gnssConfigurationIface = gnssConfigurationIface_V2_0;
+        }
+    } else if (gnssHal_V1_1 != nullptr) {
         auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
         if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration");
+            ALOGD("Unable to get a handle to GnssConfiguration_V1_1");
         } else {
             gnssConfigurationIface_V1_1 = gnssConfiguration;
             gnssConfigurationIface = gnssConfigurationIface_V1_1;
@@ -1451,6 +1538,15 @@
     } else {
         gnssBatchingIface = gnssBatching;
     }
+
+    if (gnssHal_V2_0 != nullptr) {
+        auto gnssVisibilityControl = gnssHal_V2_0->getExtensionVisibilityControl();
+        if (!gnssVisibilityControl.isOk()) {
+            ALOGD("Unable to get a handle to GnssVisibilityControl interface");
+        } else {
+            gnssVisibilityControlIface = gnssVisibilityControl;
+        }
+    }
 }
 
 static jboolean android_location_GnssLocationProvider_is_supported(
@@ -1463,9 +1559,23 @@
     return (agnssRilIface != nullptr) ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean android_location_gpsLocationProvider_is_gnss_configuration_supported(
-        JNIEnv* /* env */, jclass /* jclazz */) {
-    return (gnssConfigurationIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+static jobject android_location_GnssConfiguration_get_gnss_configuration_version(
+        JNIEnv* env, jclass /* jclazz */) {
+    jint major, minor;
+    if (gnssConfigurationIface_V2_0 != nullptr) {
+        major = 2;
+        minor = 0;
+    } else if (gnssConfigurationIface_V1_1 != nullptr) {
+        major = 1;
+        minor = 1;
+    } else if (gnssConfigurationIface != nullptr) {
+        major = 1;
+        minor = 0;
+    } else {
+        return nullptr;
+    }
+
+    return createHalInterfaceVersionJavaObject(env, major, minor);
 }
 
 static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject obj) {
@@ -1538,7 +1648,13 @@
     if (agnssRilIface != nullptr) {
         agnssRilIface->setCallback(aGnssRilCbIface);
     } else {
-        ALOGI("Unable to Initialize AGnss Ril interface\n");
+        ALOGI("Unable to initialize AGnss Ril interface\n");
+    }
+
+    if (gnssVisibilityControlIface != nullptr) {
+        sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface =
+                new GnssVisibilityControlCallback();
+        gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
     }
 
     return JNI_TRUE;
@@ -1940,6 +2056,11 @@
     return result;
 }
 
+static jboolean android_location_GnssLocationProvider_is_gnss_visibility_control_supported(
+        JNIEnv* /* env */, jclass /* clazz */) {
+    return (gnssVisibilityControlIface != nullptr) ?  JNI_TRUE : JNI_FALSE;
+}
+
 static void android_location_GnssNetworkConnectivityHandler_update_network_state(JNIEnv* env,
                                                                        jobject /* obj */,
                                                                        jboolean connected,
@@ -2156,8 +2277,8 @@
                 singleSatCorrClass, "getSatId", "()I");
             method_correctionSatCarrierFreq = env->GetMethodID(
                 singleSatCorrClass, "getCarrierFrequencyHz", "()F");
-            method_correctionSatIsLos = env->GetMethodID(
-                singleSatCorrClass,"getSatIsLos", "()Z");
+            method_correctionSatIsLosProb = env->GetMethodID(
+                singleSatCorrClass,"getProbSatIsLos", "()F");
             method_correctionSatEpl = env->GetMethodID(
                 singleSatCorrClass, "getExcessPathLengthMeters", "()F");
             method_correctionSatEplUnc = env->GetMethodID(
@@ -2175,8 +2296,8 @@
             env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
         jfloat carrierFreqHz = env->CallFloatMethod(
             singleSatCorrectionObj, method_correctionSatCarrierFreq);
-        jboolean satIsLos = env->CallBooleanMethod(singleSatCorrectionObj,
-            method_correctionSatIsLos);
+        jfloat probSatIsLos = env->CallFloatMethod(singleSatCorrectionObj,
+            method_correctionSatIsLosProb);
         jfloat eplMeters =
             env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
         jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj,
@@ -2216,7 +2337,7 @@
             .constellation = static_cast<GnssConstellationType>(constType),
             .svid = static_cast<uint16_t>(satId),
             .carrierFrequencyHz = carrierFreqHz,
-            .satIsLos = static_cast<bool>(satIsLos),
+            .probSatIsLos = probSatIsLos,
             .excessPathLengthMeters = eplMeters,
             .excessPathLengthUncertaintyMeters = eplUncMeters,
             .reflectingPlane = reflectingPlane,
@@ -2278,9 +2399,9 @@
     return boolToJbool(result.isOk());
 }
 
-static jboolean android_location_GnssLocationProvider_set_emergency_supl_pdn(JNIEnv*,
-                                                                    jobject,
-                                                                    jint emergencySuplPdn) {
+static jboolean android_location_GnssConfiguration_set_emergency_supl_pdn(JNIEnv*,
+                                                                          jobject,
+                                                                          jint emergencySuplPdn) {
     if (gnssConfigurationIface == nullptr) {
         ALOGE("no GNSS configuration interface available");
         return JNI_FALSE;
@@ -2294,7 +2415,7 @@
     }
 }
 
-static jboolean android_location_GnssLocationProvider_set_supl_version(JNIEnv*,
+static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
                                                                     jobject,
                                                                     jint version) {
     if (gnssConfigurationIface == nullptr) {
@@ -2309,9 +2430,14 @@
     }
 }
 
-static jboolean android_location_GnssLocationProvider_set_supl_es(JNIEnv*,
-                                                                    jobject,
-                                                                    jint suplEs) {
+static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
+                                                               jobject,
+                                                               jint suplEs) {
+    if (gnssConfigurationIface_V2_0 != nullptr) {
+        ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0.");
+        return JNI_FALSE;
+    }
+
     if (gnssConfigurationIface == nullptr) {
         ALOGE("no GNSS configuration interface available");
         return JNI_FALSE;
@@ -2325,9 +2451,9 @@
     }
 }
 
-static jboolean android_location_GnssLocationProvider_set_supl_mode(JNIEnv*,
-                                                                    jobject,
-                                                                    jint mode) {
+static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
+                                                                 jobject,
+                                                                 jint mode) {
     if (gnssConfigurationIface == nullptr) {
         ALOGE("no GNSS configuration interface available");
         return JNI_FALSE;
@@ -2341,9 +2467,14 @@
     }
 }
 
-static jboolean android_location_GnssLocationProvider_set_gps_lock(JNIEnv*,
-                                                                   jobject,
-                                                                   jint gpsLock) {
+static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
+                                                                jobject,
+                                                                jint gpsLock) {
+    if (gnssConfigurationIface_V2_0 != nullptr) {
+        ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0.");
+        return JNI_FALSE;
+    }
+
     if (gnssConfigurationIface == nullptr) {
         ALOGE("no GNSS configuration interface available");
         return JNI_FALSE;
@@ -2357,7 +2488,7 @@
     }
 }
 
-static jboolean android_location_GnssLocationProvider_set_lpp_profile(JNIEnv*,
+static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*,
                                                                    jobject,
                                                                    jint lppProfile) {
     if (gnssConfigurationIface == nullptr) {
@@ -2374,9 +2505,9 @@
     }
 }
 
-static jboolean android_location_GnssLocationProvider_set_gnss_pos_protocol_select(JNIEnv*,
-                                                                   jobject,
-                                                                   jint gnssPosProtocol) {
+static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*,
+                                                                            jobject,
+                                                                            jint gnssPosProtocol) {
     if (gnssConfigurationIface == nullptr) {
         ALOGE("no GNSS configuration interface available");
         return JNI_FALSE;
@@ -2390,7 +2521,7 @@
     }
 }
 
-static jboolean android_location_GnssLocationProvider_set_satellite_blacklist(
+static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
         JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
     if (gnssConfigurationIface_V1_1 == nullptr) {
         ALOGI("No GNSS Satellite Blacklist interface available");
@@ -2431,6 +2562,27 @@
     }
 }
 
+static jboolean android_location_GnssConfiguration_set_es_extension_sec(
+        JNIEnv*, jobject, jint emergencyExtensionSeconds) {
+    if (gnssConfigurationIface == nullptr) {
+        ALOGE("no GNSS configuration interface available");
+        return JNI_FALSE;
+    }
+
+    if (gnssConfigurationIface_V2_0 == nullptr) {
+        ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
+                " versions earlier than 2.0.");
+        return JNI_FALSE;
+    }
+
+    auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
+    if (result.isOk()) {
+        return result;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
 static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return 0; // batching not supported, size = 0
@@ -2492,23 +2644,43 @@
     return gnssBatchingIface->stop();
 }
 
+static jboolean android_location_GnssVisibilityControl_enable_nfw_location_access(
+        JNIEnv* env, jobject, jobjectArray proxyApps) {
+    if (gnssVisibilityControlIface == nullptr) {
+        ALOGI("No GNSS Visibility Control interface available");
+        return JNI_FALSE;
+    }
+
+    const jsize length = env->GetArrayLength(proxyApps);
+    hidl_vec<hidl_string> hidlProxyApps(length);
+    for (int i = 0; i < length; ++i) {
+        jstring proxyApp = (jstring) (env->GetObjectArrayElement(proxyApps, i));
+        ScopedJniString jniProxyApp(env, proxyApp);
+        hidlProxyApps[i] = jniProxyApp;
+    }
+
+    auto result = gnssVisibilityControlIface->enableNfwLocationAccess(hidlProxyApps);
+    if (result.isOk()) {
+        return result;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
 static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"class_init_native", "()V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_class_init_native)},
     {"native_is_supported", "()Z", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_is_supported)},
-    {"native_is_gnss_configuration_supported", "()Z",
-            reinterpret_cast<void *>(
-                    android_location_gpsLocationProvider_is_gnss_configuration_supported)},
     {"native_init_once", "()V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_init_once)},
     {"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)},
     {"native_cleanup", "()V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_cleanup)},
     {"native_set_position_mode",
-                "(IIIIIZ)Z",
-                reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
+            "(IIIIIZ)Z",
+            reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
     {"native_start", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_start)},
     {"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)},
     {"native_delete_aiding_data",
@@ -2545,31 +2717,8 @@
     {"native_get_internal_state",
             "()Ljava/lang/String;",
             reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)},
-    {"native_set_supl_es",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_es)},
-    {"native_set_supl_version",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_version)},
-    {"native_set_supl_mode",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_mode)},
-    {"native_set_lpp_profile",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_lpp_profile)},
-    {"native_set_gnss_pos_protocol_select",
-            "(I)Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssLocationProvider_set_gnss_pos_protocol_select)},
-    {"native_set_gps_lock",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_gps_lock)},
-    {"native_set_emergency_supl_pdn",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_emergency_supl_pdn)},
-    {"native_set_satellite_blacklist",
-            "([I[I)Z",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_satellite_blacklist)},
+    {"native_is_gnss_visibility_control_supported", "()Z", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_is_gnss_visibility_control_supported)},
 };
 
 static const JNINativeMethod sMethodsBatching[] = {
@@ -2663,6 +2812,50 @@
             reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed)},
 };
 
+static const JNINativeMethod sConfigurationMethods[] = {
+     /* name, signature, funcPtr */
+    {"native_get_gnss_configuration_version",
+            "()Lcom/android/server/location/GnssConfiguration$HalInterfaceVersion;",
+            reinterpret_cast<void *>(
+                    android_location_GnssConfiguration_get_gnss_configuration_version)},
+    {"native_set_supl_es",
+            "(I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_es)},
+    {"native_set_supl_version",
+            "(I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_version)},
+    {"native_set_supl_mode",
+            "(I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_mode)},
+    {"native_set_lpp_profile",
+            "(I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_lpp_profile)},
+    {"native_set_gnss_pos_protocol_select",
+            "(I)Z",
+            reinterpret_cast<void *>(
+                    android_location_GnssConfiguration_set_gnss_pos_protocol_select)},
+    {"native_set_gps_lock",
+            "(I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_gps_lock)},
+    {"native_set_emergency_supl_pdn",
+            "(I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_emergency_supl_pdn)},
+    {"native_set_satellite_blacklist",
+            "([I[I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_satellite_blacklist)},
+    {"native_set_es_extension_sec",
+            "(I)Z",
+            reinterpret_cast<void *>(android_location_GnssConfiguration_set_es_extension_sec)},
+};
+
+static const JNINativeMethod sVisibilityControlMethods[] = {
+     /* name, signature, funcPtr */
+    {"native_enable_nfw_location_access",
+            "([Ljava/lang/String;)Z",
+            reinterpret_cast<void *>(
+                    android_location_GnssVisibilityControl_enable_nfw_location_access)},
+};
+
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
     jniRegisterNativeMethods(
             env,
@@ -2689,6 +2882,16 @@
             "com/android/server/location/GnssNetworkConnectivityHandler",
             sNetworkConnectivityMethods,
             NELEM(sNetworkConnectivityMethods));
+    jniRegisterNativeMethods(
+            env,
+            "com/android/server/location/GnssConfiguration",
+            sConfigurationMethods,
+            NELEM(sConfigurationMethods));
+    jniRegisterNativeMethods(
+            env,
+            "com/android/server/location/GnssVisibilityControl",
+            sVisibilityControlMethods,
+            NELEM(sVisibilityControlMethods));
     return jniRegisterNativeMethods(
             env,
             "com/android/server/location/GnssLocationProvider",
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 3c87e42..7dd30bd 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -75,6 +75,23 @@
     jbyte* mElements;
 };
 
+int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
+#if HAS_FSVERITY
+    const char* path = env->GetStringUTFChars(filePath, nullptr);
+    ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
+    if (rfd.get() < 0) {
+      return errno;
+    }
+    if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) {
+      return errno;
+    }
+    return 0;
+#else
+    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
+    return ENOSYS;
+#endif  // HAS_FSVERITY
+}
+
 int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
 #if HAS_FSVERITY
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest) + kSha256Bytes);
@@ -82,14 +99,17 @@
     data->digest_size = kSha256Bytes;  // the only input/output parameter
 
     const char* path = env->GetStringUTFChars(filePath, nullptr);
-    ::android::base::unique_fd rfd(open(path, O_RDONLY));
+    ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
+    if (rfd.get() < 0) {
+      return errno;
+    }
     if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
       return errno;
     }
     return 0;
 #else
     LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
-    return -1;
+    return ENOSYS;
 #endif  // HAS_FSVERITY
 }
 
@@ -172,6 +192,7 @@
 }
 
 const JNINativeMethod sMethods[] = {
+    { "enableFsverityNative", "(Ljava/lang/String;)I", (void *)enableFsverity },
     { "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
     { "constructFsveritySignedDataNative", "([B)[B", (void *)constructFsveritySignedData },
     { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor },
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
index 05912a5..de5dd17 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -66,6 +66,9 @@
         map.put(
                 DOWNLOAD_STATE_INITIALIZATION_ERROR,
                 InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
 
         // Error constants corresponding to errors related to bad update file.
         map.put(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 781e1c4..76ae5cc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -221,7 +221,7 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
 import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -245,6 +245,7 @@
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.UserRestrictionsUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -4990,26 +4991,22 @@
     private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
             int flags, int callingUid, int userHandle) {
         int quality;
-        final int realQuality;
         synchronized (getLockObject()) {
             quality = getPasswordQuality(null, userHandle, /* parent */ false);
             if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
                 quality = PASSWORD_QUALITY_UNSPECIFIED;
             }
             final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
-            realQuality = metrics.quality;
-            if (quality != PASSWORD_QUALITY_UNSPECIFIED) {
-
-                if (realQuality < quality
-                        && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
-                    Slog.w(LOG_TAG, "resetPassword: password quality 0x"
-                            + Integer.toHexString(realQuality)
-                            + " does not meet required quality 0x"
-                            + Integer.toHexString(quality));
-                    return false;
-                }
-                quality = Math.max(realQuality, quality);
+            final int realQuality = metrics.quality;
+            if (realQuality < quality
+                    && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+                Slog.w(LOG_TAG, "resetPassword: password quality 0x"
+                        + Integer.toHexString(realQuality)
+                        + " does not meet required quality 0x"
+                        + Integer.toHexString(quality));
+                return false;
             }
+            quality = Math.max(realQuality, quality);
             int length = getPasswordMinimumLength(null, userHandle, /* parent */ false);
             if (password.length() < length) {
                 Slog.w(LOG_TAG, "resetPassword: password length " + password.length()
@@ -5086,7 +5083,7 @@
         try {
             if (token == null) {
                 if (!TextUtils.isEmpty(password)) {
-                    mLockPatternUtils.saveLockPassword(password, null, realQuality, userHandle);
+                    mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
                 } else {
                     mLockPatternUtils.clearLock(null, userHandle);
                 }
@@ -5095,7 +5092,7 @@
                 result = mLockPatternUtils.setLockCredentialWithToken(password,
                         TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE
                                 : LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                        realQuality, tokenHandle, token, userHandle);
+                        quality, tokenHandle, token, userHandle);
             }
             boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
             if (requireEntry) {
@@ -5326,6 +5323,7 @@
         }
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
+        ComponentName adminComponent = null;
         synchronized (getLockObject()) {
             // Make sure the caller has any active admin with the right policy or
             // the required permission.
@@ -5336,8 +5334,7 @@
                     android.Manifest.permission.LOCK_DEVICE);
             final long ident = mInjector.binderClearCallingIdentity();
             try {
-                final ComponentName adminComponent = admin == null ?
-                        null : admin.info.getComponent();
+                adminComponent = admin == null ? null : admin.info.getComponent();
                 if (adminComponent != null) {
                     // For Profile Owners only, callers with only permission not allowed.
                     if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
@@ -5389,6 +5386,7 @@
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.LOCK_NOW)
+                .setAdmin(adminComponent)
                 .setInt(flags)
                 .write();
     }
@@ -6105,7 +6103,9 @@
         synchronized (getLockObject()) {
             delegates = getDelegatePackagesInternalLocked(scope, userId);
         }
-        if (delegates.size() != 1) {
+        if (delegates.size() == 0) {
+            return null;
+        } else if (delegates.size() > 1) {
             Slog.wtf(LOG_TAG, "More than one delegate holds " + scope);
             return null;
         }
@@ -9192,21 +9192,15 @@
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
 
-        // TODO When InputMethodManager supports per user calls remove
-        //      this restriction.
-        if (!checkCallerIsCurrentUserOrProfile()) {
+        // TODO When InputMethodManager supports per user calls remove this restriction.
+        if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
+                && !checkCallerIsCurrentUserOrProfile()) {
             return false;
         }
-
         final int callingUserId = mInjector.userHandleGetCallingUserId();
         if (packageList != null) {
-            // InputMethodManager fetches input methods for current user.
-            // So this can only be set when calling user is the current user
-            // or parent is current user in case of managed profiles.
-            InputMethodManager inputMethodManager =
-                    mContext.getSystemService(InputMethodManager.class);
-            List<InputMethodInfo> enabledImes = inputMethodManager.getEnabledInputMethodList();
-
+            List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
+                    .getEnabledInputMethodListAsUser(callingUserId);
             if (enabledImes != null) {
                 List<String> enabledPackages = new ArrayList<String>();
                 for (InputMethodInfo ime : enabledImes) {
@@ -9254,22 +9248,16 @@
     @Override
     public List getPermittedInputMethodsForCurrentUser() {
         enforceManageUsers();
-        UserInfo currentUser;
-        try {
-            currentUser = mInjector.getIActivityManager().getCurrentUser();
-        } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e);
-            // Activity managed is dead, just allow all IMEs
-            return null;
-        }
 
-        int userId = currentUser.id;
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             List<String> result = null;
             // If we have multiple profiles we return the intersection of the
             // permitted lists. This can happen in cases where we have a device
             // and profile owner.
-            int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId);
+            int[] profileIds = InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
+                    ? new int[]{callingUserId}
+                    : mUserManager.getProfileIdsWithDisabled(callingUserId);
             for (int profileId : profileIds) {
                 // Just loop though all admins, only device or profiles
                 // owners can have permitted lists set.
@@ -9290,9 +9278,8 @@
 
             // If we have a permitted list add all system input methods.
             if (result != null) {
-                InputMethodManager inputMethodManager =
-                        mContext.getSystemService(InputMethodManager.class);
-                List<InputMethodInfo> imes = inputMethodManager.getInputMethodList();
+                List<InputMethodInfo> imes =
+                        InputMethodManagerInternal.get().getInputMethodListAsUser(callingUserId);
                 if (imes != null) {
                     for (InputMethodInfo ime : imes) {
                         ServiceInfo serviceInfo = ime.getServiceInfo();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
index d0ec0ee..699bec2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
@@ -23,6 +23,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.admin.DeviceAdminReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,15 +31,13 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.ArraySet;
 import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.view.IInputMethodManager;
+import com.android.server.inputmethod.InputMethodManagerInternal;
 
 import java.util.Arrays;
 import java.util.List;
@@ -53,18 +52,38 @@
     protected static final String TAG = "OverlayPackagesProvider";
 
     private final PackageManager mPm;
-    private final IInputMethodManager mIInputMethodManager;
     private final Context mContext;
+    private final Injector mInjector;
 
     public OverlayPackagesProvider(Context context) {
-        this(context, getIInputMethodManager());
+        this(context, new DefaultInjector());
     }
 
     @VisibleForTesting
-    OverlayPackagesProvider(Context context, IInputMethodManager iInputMethodManager) {
+    interface Injector {
+        boolean isPerProfileImeEnabled();
+        @NonNull
+        List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
+    }
+
+    private static final class DefaultInjector implements Injector {
+        @Override
+        public boolean isPerProfileImeEnabled() {
+            return InputMethodSystemProperty.PER_PROFILE_IME_ENABLED;
+        }
+
+        @NonNull
+        @Override
+        public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
+            return InputMethodManagerInternal.get().getInputMethodListAsUser(userId);
+        }
+    }
+
+    @VisibleForTesting
+    OverlayPackagesProvider(Context context, Injector injector) {
         mContext = context;
         mPm = checkNotNull(context.getPackageManager());
-        mIInputMethodManager = checkNotNull(iInputMethodManager);
+        mInjector = checkNotNull(injector);
     }
 
     /**
@@ -89,10 +108,12 @@
         // Newly installed system apps are uninstalled when they are not required and are either
         // disallowed or have a launcher icon.
         nonRequiredApps.removeAll(getRequiredApps(provisioningAction, admin.getPackageName()));
-        // Don't delete the system input method packages in case of Device owner provisioning.
-        if (ACTION_PROVISION_MANAGED_DEVICE.equals(provisioningAction)
+        if (mInjector.isPerProfileImeEnabled()) {
+            nonRequiredApps.removeAll(getSystemInputMethods(userId));
+        } else if (ACTION_PROVISION_MANAGED_DEVICE.equals(provisioningAction)
                 || ACTION_PROVISION_MANAGED_USER.equals(provisioningAction)) {
-            nonRequiredApps.removeAll(getSystemInputMethods());
+            // Don't delete the system input method packages in case of Device owner provisioning.
+            nonRequiredApps.removeAll(getSystemInputMethods(userId));
         }
         nonRequiredApps.addAll(getDisallowedApps(provisioningAction));
         return nonRequiredApps;
@@ -114,16 +135,8 @@
         return apps;
     }
 
-    private Set<String> getSystemInputMethods() {
-        // InputMethodManager is final so it cannot be mocked.
-        // So, we're using IInputMethodManager directly because it can be mocked.
-        final List<InputMethodInfo> inputMethods;
-        try {
-            inputMethods = mIInputMethodManager.getInputMethodList();
-        } catch (RemoteException e) {
-            // Should not happen
-            return null;
-        }
+    private Set<String> getSystemInputMethods(int userId) {
+        final List<InputMethodInfo> inputMethods = mInjector.getInputMethodListAsUser(userId);
         final Set<String> systemInputMethods = new ArraySet<>();
         for (InputMethodInfo inputMethodInfo : inputMethods) {
             ApplicationInfo applicationInfo = inputMethodInfo.getServiceInfo().applicationInfo;
@@ -149,11 +162,6 @@
         return disallowedApps;
     }
 
-    private static IInputMethodManager getIInputMethodManager() {
-        final IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
-        return IInputMethodManager.Stub.asInterface(b);
-    }
-
     private Set<String> getRequiredAppsSet(String provisioningAction) {
         final int resId;
         switch (provisioningAction) {
diff --git a/services/ipmemorystore/Android.bp b/services/ipmemorystore/Android.bp
new file mode 100644
index 0000000..013cf56
--- /dev/null
+++ b/services/ipmemorystore/Android.bp
@@ -0,0 +1,4 @@
+java_library_static {
+    name: "services.ipmemorystore",
+    srcs: ["java/**/*.java"],
+}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
new file mode 100644
index 0000000..eaab650
--- /dev/null
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
@@ -0,0 +1,143 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+/**
+ * Encapsulating class for using the SQLite database backing the memory store.
+ *
+ * This class groups together the contracts and the SQLite helper used to
+ * use the database.
+ *
+ * @hide
+ */
+public class IpMemoryStoreDatabase {
+    /**
+     * Contract class for the Network Attributes table.
+     */
+    public static class NetworkAttributesContract {
+        public static final String TABLENAME = "NetworkAttributes";
+
+        public static final String COLNAME_L2KEY = "l2Key";
+        public static final String COLTYPE_L2KEY = "TEXT NOT NULL";
+
+        public static final String COLNAME_EXPIRYDATE = "expiryDate";
+        // Milliseconds since the Epoch, in true Java style
+        public static final String COLTYPE_EXPIRYDATE = "BIGINT";
+
+        public static final String COLNAME_ASSIGNEDV4ADDRESS = "assignedV4Address";
+        public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER";
+
+        // Please note that the group hint is only a *hint*, hence its name. The client can offer
+        // this information to nudge the grouping in the decision it thinks is right, but it can't
+        // decide for the memory store what is the same L3 network.
+        public static final String COLNAME_GROUPHINT = "groupHint";
+        public static final String COLTYPE_GROUPHINT = "TEXT";
+
+        public static final String COLNAME_DNSADDRESSES = "dnsAddresses";
+        // Stored in marshalled form as is
+        public static final String COLTYPE_DNSADDRESSES = "BLOB";
+
+        public static final String COLNAME_MTU = "mtu";
+        public static final String COLTYPE_MTU = "INTEGER";
+
+        public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
+                + TABLENAME                 + " ("
+                + COLNAME_L2KEY             + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, "
+                + COLNAME_EXPIRYDATE        + " " + COLTYPE_EXPIRYDATE        + ", "
+                + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", "
+                + COLNAME_GROUPHINT         + " " + COLTYPE_GROUPHINT         + ", "
+                + COLNAME_DNSADDRESSES      + " " + COLTYPE_DNSADDRESSES      + ", "
+                + COLNAME_MTU               + " " + COLTYPE_MTU               + ")";
+        public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME;
+    }
+
+    /**
+     * Contract class for the Private Data table.
+     */
+    public static class PrivateDataContract {
+        public static final String TABLENAME = "PrivateData";
+
+        public static final String COLNAME_L2KEY = "l2Key";
+        public static final String COLTYPE_L2KEY = "TEXT NOT NULL";
+
+        public static final String COLNAME_CLIENT = "client";
+        public static final String COLTYPE_CLIENT = "TEXT NOT NULL";
+
+        public static final String COLNAME_DATANAME = "dataName";
+        public static final String COLTYPE_DATANAME = "TEXT NOT NULL";
+
+        public static final String COLNAME_DATA = "data";
+        public static final String COLTYPE_DATA = "BLOB NOT NULL";
+
+        public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
+                + TABLENAME        + " ("
+                + COLNAME_L2KEY    + " " + COLTYPE_L2KEY    + ", "
+                + COLNAME_CLIENT   + " " + COLTYPE_CLIENT   + ", "
+                + COLNAME_DATANAME + " " + COLTYPE_DATANAME + ", "
+                + COLNAME_DATA     + " " + COLTYPE_DATA     + ", "
+                + "PRIMARY KEY ("
+                + COLNAME_L2KEY    + ", "
+                + COLNAME_CLIENT   + ", "
+                + COLNAME_DATANAME + "))";
+        public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME;
+    }
+
+    // To save memory when the DB is not used, close it after 30s of inactivity. This is
+    // determined manually based on what feels right.
+    private static final long IDLE_CONNECTION_TIMEOUT_MS = 30_000;
+
+    /** The SQLite DB helper */
+    public static class DbHelper extends SQLiteOpenHelper {
+        // Update this whenever changing the schema.
+        private static final int SCHEMA_VERSION = 1;
+        private static final String DATABASE_FILENAME = "IpMemoryStore.db";
+
+        public DbHelper(@NonNull final Context context) {
+            super(context, DATABASE_FILENAME, null, SCHEMA_VERSION);
+            setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+        }
+
+        /** Called when the database is created */
+        public void onCreate(@NonNull final SQLiteDatabase db) {
+            db.execSQL(NetworkAttributesContract.CREATE_TABLE);
+            db.execSQL(PrivateDataContract.CREATE_TABLE);
+        }
+
+        /** Called when the database is upgraded */
+        public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion,
+                final int newVersion) {
+            // No upgrade supported yet.
+            db.execSQL(NetworkAttributesContract.DROP_TABLE);
+            db.execSQL(PrivateDataContract.DROP_TABLE);
+            onCreate(db);
+        }
+
+        /** Called when the database is downgraded */
+        public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion,
+                final int newVersion) {
+            // Downgrades always nuke all data and recreate an empty table.
+            db.execSQL(NetworkAttributesContract.DROP_TABLE);
+            db.execSQL(PrivateDataContract.DROP_TABLE);
+            onCreate(db);
+        }
+    }
+}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
new file mode 100644
index 0000000..55a72190
--- /dev/null
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -0,0 +1,222 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.IIpMemoryStore;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.util.Log;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Implementation for the IP memory store.
+ * This component offers specialized services for network components to store and retrieve
+ * knowledge about networks, and provides intelligence that groups level 2 networks together
+ * into level 3 networks.
+ *
+ * @hide
+ */
+public class IpMemoryStoreService extends IIpMemoryStore.Stub {
+    private static final String TAG = IpMemoryStoreService.class.getSimpleName();
+    private static final int MAX_CONCURRENT_THREADS = 4;
+
+    @NonNull
+    final Context mContext;
+    @Nullable
+    final SQLiteDatabase mDb;
+    @NonNull
+    final ExecutorService mExecutor;
+
+    /**
+     * Construct an IpMemoryStoreService object.
+     * This constructor will block on disk access to open the database.
+     * @param context the context to access storage with.
+     */
+    public IpMemoryStoreService(@NonNull final Context context) {
+        // Note that constructing the service will access the disk and block
+        // for some time, but it should make no difference to the clients. Because
+        // the interface is one-way, clients fire and forget requests, and the callback
+        // will get called eventually in any case, and the framework will wait for the
+        // service to be created to deliver subsequent requests.
+        // Avoiding this would mean the mDb member can't be final, which means the service would
+        // have to test for nullity, care for failure, and allow for a wait at every single access,
+        // which would make the code a lot more complex and require all methods to possibly block.
+        mContext = context;
+        SQLiteDatabase db;
+        final IpMemoryStoreDatabase.DbHelper helper = new IpMemoryStoreDatabase.DbHelper(context);
+        try {
+            db = helper.getWritableDatabase();
+            if (null == db) Log.e(TAG, "Unexpected null return of getWriteableDatabase");
+        } catch (final SQLException e) {
+            Log.e(TAG, "Can't open the Ip Memory Store database", e);
+            db = null;
+        } catch (final Exception e) {
+            Log.wtf(TAG, "Impossible exception Ip Memory Store database", e);
+            db = null;
+        }
+        mDb = db;
+        // The work-stealing thread pool executor will spawn threads as needed up to
+        // the max only when there is no free thread available. This generally behaves
+        // exactly like one would expect it intuitively :
+        // - When work arrives, it will spawn a new thread iff there are no available threads
+        // - When there is no work to do it will shutdown threads after a while (the while
+        //   being equal to 2 seconds (not configurable) when max threads are spun up and
+        //   twice as much for every one less thread)
+        // - When all threads are busy the work is enqueued and waits for any worker
+        //   to become available.
+        // Because the stealing pool is made for very heavily parallel execution of
+        // small tasks that spawn others, it creates a queue per thread that in this
+        // case is overhead. However, the three behaviors above make it a superior
+        // choice to cached or fixedThreadPoolExecutor, neither of which can actually
+        // enqueue a task waiting for a thread to be free. This can probably be solved
+        // with judicious subclassing of ThreadPoolExecutor, but that's a lot of dangerous
+        // complexity for little benefit in this case.
+        mExecutor = Executors.newWorkStealingPool(MAX_CONCURRENT_THREADS);
+    }
+
+    /**
+     * Shutdown the memory store service, cancelling running tasks and dropping queued tasks.
+     *
+     * This is provided to give a way to clean up, and is meant to be available in case of an
+     * emergency shutdown.
+     */
+    public void shutdown() {
+        // By contrast with ExecutorService#shutdown, ExecutorService#shutdownNow tries
+        // to cancel the existing tasks, and does not wait for completion. It does not
+        // guarantee the threads can be terminated in any given amount of time.
+        mExecutor.shutdownNow();
+        if (mDb != null) mDb.close();
+    }
+
+    /**
+     * Store network attributes for a given L2 key.
+     *
+     * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+     *              key and only care about grouping can pass a unique ID here like the ones
+     *              generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+     *              relevance of such a network will lead to it being evicted soon if it's not
+     *              refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+     * @param attributes The attributes for this network.
+     * @param listener A listener to inform of the completion of this call, or null if the client
+     *        is not interested in learning about success/failure.
+     * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
+     * If the call failed, the L2 key will be null.
+     */
+    @Override
+    public void storeNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final NetworkAttributesParcelable attributes,
+            @Nullable final IOnStatusListener listener) {
+        // TODO : implement this.
+    }
+
+    /**
+     * Store a binary blob associated with an L2 key and a name.
+     *
+     * @param l2Key The L2 key for this network.
+     * @param clientId The ID of the client.
+     * @param name The name of this data.
+     * @param data The data to store.
+     * @param listener The listener that will be invoked to return the answer, or null if the
+     *        is not interested in learning about success/failure.
+     * Through the listener, returns a status to indicate success or failure.
+     */
+    @Override
+    public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final Blob data,
+            @Nullable final IOnStatusListener listener) {
+        // TODO : implement this.
+    }
+
+    /**
+     * Returns the best L2 key associated with the attributes.
+     *
+     * This will find a record that would be in the same group as the passed attributes. This is
+     * useful to choose the key for storing a sample or private data when the L2 key is not known.
+     * If multiple records are group-close to these attributes, the closest match is returned.
+     * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+     * order) L2 key is returned.
+     * If no record matches these attributes, null is returned.
+     *
+     * @param attributes The attributes of the network to find.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the L2 key if one matched, or null.
+     */
+    @Override
+    public void findL2Key(@NonNull final NetworkAttributesParcelable attributes,
+            @NonNull final IOnL2KeyResponseListener listener) {
+        // TODO : implement this.
+    }
+
+    /**
+     * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+     * to the same L3 network. Group-closeness is used to determine this.
+     *
+     * @param l2Key1 The key for the first network.
+     * @param l2Key2 The key for the second network.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
+     */
+    @Override
+    public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
+            @NonNull final IOnSameNetworkResponseListener listener) {
+        // TODO : implement this.
+    }
+
+    /**
+     * Retrieve the network attributes for a key.
+     * If no record is present for this key, this will return null attributes.
+     *
+     * @param l2Key The key of the network to query.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the network attributes and the L2 key associated with
+     *         the query.
+     */
+    @Override
+    public void retrieveNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final IOnNetworkAttributesRetrieved listener) {
+        // TODO : implement this.
+    }
+
+    /**
+     * Retrieve previously stored private data.
+     * If no data was stored for this L2 key and name this will return null.
+     *
+     * @param l2Key The L2 key.
+     * @param clientId The id of the client that stored this data.
+     * @param name The name of the data.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the private data if any or null if none, with the L2 key
+     *         and the name of the data associated with the query.
+     */
+    @Override
+    public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+        // TODO : implement this.
+    }
+}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java
new file mode 100644
index 0000000..aa45400
--- /dev/null
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.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 com.android.server.net.ipmemorystore;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A class containing the logic around the relevance value for
+ * IP Memory Store.
+ *
+ * @hide
+ */
+public class RelevanceUtils {
+    /**
+     * The relevance is a decaying value that gets lower and lower until it
+     * reaches 0 after some time passes. It follows an exponential decay law,
+     * dropping slowly at first then faster and faster, because a network is
+     * likely to be visited again if it was visited not long ago, and the longer
+     * it hasn't been visited the more likely it is that it won't be visited
+     * again. For example, a network visited on holiday should stay fresh for
+     * the duration of the holiday and persist for a while, but after the venue
+     * hasn't been visited for a while it should quickly be discarded. What
+     * should accelerate forgetting the network is extended periods without
+     * visits, so that occasional venues get discarded but regular visits keep
+     * the network relevant, even if the visits are infrequent.
+     *
+     * This function must be stable by iteration, meaning that adjusting the same value
+     * for different dates iteratively multiple times should give the same result.
+     * Formally, if f is the decay function that associates a relevance x at a date d1
+     * to the value at ulterior date d3, then for any date d2 between d1 and d3 :
+     * f(x, d3 - d1) = f(f(x, d3 - d2), d2 - d1). Intuitively, this property simply
+     * means it should be the same to compute and store back the value after two months,
+     * or to do it once after one month, store it back, and do it again after another
+     * months has passed.
+     * The pair of the relevance and date define the entire curve, so any pair
+     * of values on the curve will define the same curve. Setting one of them to a
+     * constant, so as not to have to store it, means the other one will always suffice
+     * to describe the curve. For example, only storing the date for a known, constant
+     * value of the relevance is an efficient way of remembering this information (and
+     * to compare relevances together, as f is monotonically decreasing).
+     *
+     *** Choosing the function :
+     * Functions of the kind described above are standard exponential decay functions
+     * like the ones that govern atomic decay where the value at any given date can be
+     * computed uniformly from the value at a previous date and the time elapsed since
+     * that date. It is simple to picture this kind of function as one where after a
+     * given period of time called the half-life, the relevance value will have been
+     * halved. Decay of this kind is expressed in function of the previous value by
+     * functions like
+     * f(x, t) = x * F ^ (t / L)
+     * ...where x is the value, t is the elapsed time, L is the half-life (or more
+     * generally the F-th-life) and F the decay factor (typically 0.5, hence why L is
+     * usually called the half-life). The ^ symbol here is used for exponentiation.
+     * Or, starting at a given M for t = 0 :
+     * f(t) = M * F ^ (t / L)
+     *
+     * Because a line in the store needs to become irrelevant at some point but
+     * this class of functions never go to 0, a minimum cutoff has to be chosen to
+     * represent irrelevance. The simpler way of doing this is to simply add this
+     * minimum cutoff to the computation before and removing it after.
+     * Thus the function becomes :
+     * f(x, t) = ((x + K) * F ^ (t / L)) - K
+     * ...where K is the minimum cutoff, L the half-life, and F the factor between
+     * the original x and x after its half-life. Strictly speaking using the word
+     * "half-life" implies that F = 0.5, but the relation works for any value of F.
+     *
+     * It is easy enough to check that this function satisfies the stability
+     * relation that was given above for any value of F, L and K, which become
+     * parameters that can be defined at will.
+     *
+     * relevance
+     *  1.0 |
+     *      |\
+     *      | \
+     *      |  \            (this graph rendered with L = 75 days and K = 1/40)
+     *  0.75|   ',
+     *      |     \
+     *      |      '.
+     *      |        \.
+     *      |          \
+     *  0.5 |           '\
+     *      |             ''.
+     *      |                ''.
+     *      |                   ''.
+     *  0.25|                      '''..
+     *      |                           '''..
+     *      |                                ''''....
+     *      |                                        '''''..........
+     *    0 +-------------------------------------------------------''''''''''----
+     *      0       50       100      150     200      250     300      350     400 days
+     *
+     *** Choosing the parameters
+     * The maximum M is an arbitrary parameter that simply scales the curve.
+     * The tradeoff for M is pretty simple : if the relevance is going to be an
+     * integer, the bigger M is the more precision there is in the relevance.
+     * However, values of M that are easy for humans to read are preferable to
+     * help debugging, and a suitably low value may be enough to ensure there
+     * won't be integer overflows in intermediate computations.
+     * A value of 1_000_000 probably is plenty for precision, while still in the
+     * low range of what ints can represent.
+     *
+     * F and L are parameters to be chosen arbitrarily and have an impact on how
+     * fast the relevance will be decaying at first, keeping in mind that
+     * the 400 days value and the cap stay the same. In simpler words, F and L
+     * define the steepness of the curve.
+     * To keep things simple (and familiar) F is arbitrarily chosen to be 0.5, and
+     * L is set to 200 days visually to achieve the desired effect. Refer to the
+     * illustration above to get a feel of how that feels.
+     *
+     * Moreover, the memory store works on an assumption that the relevance should
+     * be capped, and that an entry with capped relevance should decay in 400 days.
+     * This is on premises that the networks a device will need to remember the
+     * longest should be networks visited about once a year.
+     * For this reason, the relevance is at the maximum M 400 days before expiry :
+     * f(M, 400 days) = 0
+     * From replacing this with the value of the function, K can then be derived
+     * from the values of M, F and L :
+     * (M + K) * F ^ (t / L) - K = 0
+     * K = M * F ^ (400 days / L) / (1 - F ^ (400 days / L))
+     * Replacing with actual values this gives :
+     * K = 1_000_000 * 0.5 ^ (400 / 200) / (1 - 0.5 ^ (400 / 200))
+     *   = 1_000_000 / 3 ≈ 333_333.3
+     * This ensures the function has the desired profile, the desired value at
+     * cap, and the desired value at expiry.
+     *
+     *** Useful relations
+     * Let's define the expiry time for any given relevance x as the interval of
+     * time such as :
+     * f(x, expiry) = 0
+     * which can be rewritten
+     * ((x + K) * F ^ (expiry / L)) = K
+     * ...giving an expression of the expiry in function of the relevance x as
+     * expiry = L * logF(K / (x + K))
+     * Conversely the relevance x can be expressed in function of the expiry as
+     * x = K / F ^ (expiry / L) - K
+     * These relations are useful in utility functions.
+     *
+     *** Bumping things up
+     * The last issue therefore is to decide how to bump up the relevance. The
+     * simple approach is to simply lift up the curve a little bit by a constant
+     * normalized amount, delaying the time of expiry. For example increasing
+     * the relevance by an amount I gives :
+     * x2 = x1 + I
+     * x2 and x1 correspond to two different expiry times expiry2 and expiry1,
+     * and replacing x1 and x2 in the relation above with their expression in
+     * function of the expiry comes :
+     * K / F ^ (expiry2 / L) - K = K / F ^ (expiry1 / L) - K + I
+     * which resolves to :
+     * expiry2 = L * logF(K / (I + K / F ^ (expiry1 / L)))
+     *
+     * In this implementation, the bump is defined as 1/25th of the cap for
+     * the relevance. This means a network will be remembered for the maximum
+     * period of 400 days if connected 25 times in succession not accounting
+     * for decay. Of course decay actually happens so it will take more than 25
+     * connections for any given network to actually reach the cap, but because
+     * decay is slow at first, it is a good estimate of how fast cap happens.
+     *
+     * Specifically, it gives the following four results :
+     * - A network that a device connects to once hits irrelevance about 32.7 days after
+     *   it was first registered if never connected again.
+     * - A network that a device connects to once a day at a fixed hour will hit the cap
+     *   on the 27th connection.
+     * - A network that a device connects to once a week at a fixed hour will hit the cap
+     *   on the 57th connection.
+     * - A network that a device connects to every day for 7 straight days then never again
+     *   expires 144 days after the last connection.
+     * These metrics tend to match pretty well the requirements.
+     */
+
+    // TODO : make these constants configurable at runtime. Don't forget to build it so that
+    // changes will wipe the database, migrate the values, or otherwise make sure the relevance
+    // values are still meaningful.
+
+    // How long, in milliseconds, is a capped relevance valid for, or in other
+    // words how many milliseconds after its relevance was set to RELEVANCE_CAP does
+    // any given line expire. 400 days.
+    @VisibleForTesting
+    public static final long CAPPED_RELEVANCE_LIFETIME_MS = 400L * 24 * 60 * 60 * 1000;
+
+    // The constant that represents a normalized 1.0 value for the relevance. In other words,
+    // the cap for the relevance. This is referred to as M in the explanation above.
+    @VisibleForTesting
+    public static final int CAPPED_RELEVANCE = 1_000_000;
+
+    // The decay factor. After a half-life, the relevance will have decayed by this value.
+    // This is referred to as F in the explanation above.
+    private static final double DECAY_FACTOR = 0.5;
+
+    // The half-life. After this time, the relevance will have decayed by a factor DECAY_FACTOR.
+    // This is referred to as L in the explanation above.
+    private static final long HALF_LIFE_MS = 200L * 24 * 60 * 60 * 1000;
+
+    // The value of the frame change. This is referred to as K in the explanation above.
+    private static final double IRRELEVANCE_FLOOR =
+            CAPPED_RELEVANCE * powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS)
+            / (1 - powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS));
+
+    // How much to bump the relevance by every time a line is written to.
+    @VisibleForTesting
+    public static final int RELEVANCE_BUMP = CAPPED_RELEVANCE / 25;
+
+    // Java doesn't include a function for the logarithm in an arbitrary base, so implement it
+    private static final double LOG_DECAY_FACTOR = Math.log(DECAY_FACTOR);
+    private static double logF(final double value) {
+        return Math.log(value) / LOG_DECAY_FACTOR;
+    }
+
+    // Utility function to get a power of the decay factor, to simplify the code.
+    private static double powF(final double value) {
+        return Math.pow(DECAY_FACTOR, value);
+    }
+
+    /**
+     * Compute the value of the relevance now given an expiry date.
+     *
+     * @param expiry the date at which the column in the database expires.
+     * @return the adjusted value of the relevance for this moment in time.
+     */
+    public static int computeRelevanceForNow(final long expiry) {
+        return computeRelevanceForTargetDate(expiry, System.currentTimeMillis());
+    }
+
+    /**
+     * Compute the value of the relevance at a given date from an expiry date.
+     *
+     * Because relevance decays with time, a relevance in the past corresponds to
+     * a different relevance later.
+     *
+     * Relevance is always a positive value. 0 means not relevant at all.
+     *
+     * See the explanation at the top of this file to get the justification for this
+     * computation.
+     *
+     * @param expiry the date at which the column in the database expires.
+     * @param target the target date to adjust the relevance to.
+     * @return the adjusted value of the relevance for the target moment.
+     */
+    public static int computeRelevanceForTargetDate(final long expiry, final long target) {
+        final long delay = expiry - target;
+        if (delay >= CAPPED_RELEVANCE_LIFETIME_MS) return CAPPED_RELEVANCE;
+        if (delay <= 0) return 0;
+        return (int) (IRRELEVANCE_FLOOR / powF((float) delay / HALF_LIFE_MS) - IRRELEVANCE_FLOOR);
+    }
+
+    /**
+     * Compute the expiry duration adjusted up for a new fresh write.
+     *
+     * Every time data is written to the memory store for a given line, the
+     * relevance is bumped up by a certain amount, which will boost the priority
+     * of this line for computation of group attributes, and delay (possibly
+     * indefinitely, if the line is accessed regularly) forgetting the data stored
+     * in that line.
+     * As opposed to bumpExpiryDate, this function uses a duration from now to expiry.
+     *
+     * See the explanation at the top of this file for a justification of this computation.
+     *
+     * @param oldExpiryDuration the old expiry duration in milliseconds from now.
+     * @return the expiry duration representing a bumped up relevance value.
+     */
+    public static long bumpExpiryDuration(final long oldExpiryDuration) {
+        // L * logF(K / (I + K / F ^ (expiry1 / L))), as documented above
+        final double divisionFactor = powF(((double) oldExpiryDuration) / HALF_LIFE_MS);
+        final double oldRelevance = IRRELEVANCE_FLOOR / divisionFactor;
+        final long newDuration =
+                (long) (HALF_LIFE_MS * logF(IRRELEVANCE_FLOOR / (RELEVANCE_BUMP + oldRelevance)));
+        return Math.min(newDuration, CAPPED_RELEVANCE_LIFETIME_MS);
+    }
+
+    /**
+     * Compute the new expiry date adjusted up for a new fresh write.
+     *
+     * Every time data is written to the memory store for a given line, the
+     * relevance is bumped up by a certain amount, which will boost the priority
+     * of this line for computation of group attributes, and delay (possibly
+     * indefinitely, if the line is accessed regularly) forgetting the data stored
+     * in that line.
+     * As opposed to bumpExpiryDuration, this function takes the old timestamp and returns the
+     * new timestamp.
+     *
+     * {@see bumpExpiryDuration}, and keep in mind that the bump depends on when this is called,
+     * because the relevance decays exponentially, therefore bumping up a high relevance (for a
+     * date far in the future) is less potent than bumping up a low relevance (for a date in
+     * a close future).
+     *
+     * @param oldExpiryDate the old date of expiration.
+     * @return the new expiration date after the relevance bump.
+     */
+    public static long bumpExpiryDate(final long oldExpiryDate) {
+        final long now = System.currentTimeMillis();
+        final long newDuration = bumpExpiryDuration(oldExpiryDate - now);
+        return now + newDuration;
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index be09aea..cef47ca 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -74,6 +74,7 @@
 import com.android.internal.widget.ILockSettings;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.appbinding.AppBindingService;
+import com.android.server.attention.AttentionManagerService;
 import com.android.server.audio.AudioService;
 import com.android.server.biometrics.BiometricService;
 import com.android.server.biometrics.face.FaceService;
@@ -102,10 +103,12 @@
 import com.android.server.media.projection.MediaProjectionManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
+import com.android.server.net.ipmemorystore.IpMemoryStoreService;
 import com.android.server.net.watchlist.NetworkWatchlistService;
 import com.android.server.notification.NotificationManagerService;
 import com.android.server.oemlock.OemLockService;
 import com.android.server.om.OverlayManagerService;
+import com.android.server.os.BugreportManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
 import com.android.server.os.SchedulingPolicyService;
 import com.android.server.pm.BackgroundDexOptService;
@@ -118,6 +121,7 @@
 import com.android.server.pm.ShortcutService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.policy.PhoneWindowManager;
+import com.android.server.policy.role.LegacyRoleResolutionPolicy;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
 import com.android.server.power.ThermalManagerService;
@@ -148,6 +152,8 @@
 
 import dalvik.system.VMRuntime;
 
+import com.google.android.startop.iorap.IorapForwardingService;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.Locale;
@@ -259,6 +265,10 @@
             "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
     private static final String ADB_SERVICE_CLASS =
             "com.android.server.adb.AdbService$Lifecycle";
+    private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
+            "com.android.server.appprediction.AppPredictionManagerService";
+    private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
+            "com.android.server.contentsuggestions.ContentSuggestionsManagerService";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -785,6 +795,11 @@
         traceBeginAndSlog("StartRollbackManagerService");
         mSystemServiceManager.startService(RollbackManagerService.class);
         traceEnd();
+
+        // Service to capture bugreports.
+        traceBeginAndSlog("StartBugreportManagerService");
+        mSystemServiceManager.startService(BugreportManagerService.class);
+        traceEnd();
     }
 
     /**
@@ -1007,15 +1022,30 @@
             mSystemServiceManager.startService(PinnerService.class);
             traceEnd();
 
+            traceBeginAndSlog("IorapForwardingService");
+            mSystemServiceManager.startService(IorapForwardingService.class);
+            traceEnd();
+
             traceBeginAndSlog("SignedConfigService");
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             traceEnd();
-
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
         }
 
+        // Before things start rolling, be sure we have decided whether
+        // we are in safe mode.
+        final boolean safeMode = wm.detectSafeMode();
+        if (safeMode) {
+            // If yes, immediately turn on the global setting for airplane mode.
+            // Note that this does not send broadcasts at this stage because
+            // subsystems are not yet up. We will send broadcasts later to ensure
+            // all listeners have the chance to react with special handling.
+            Settings.Global.putInt(context.getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, 1);
+        }
+
         StatusBarManagerService statusBar = null;
         INotificationManager notification = null;
         LocationManagerService location = null;
@@ -1149,6 +1179,16 @@
 
             startContentCaptureService(context);
 
+            // App prediction manager service
+            traceBeginAndSlog("StartAppPredictionService");
+            mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS);
+            traceEnd();
+
+            // Content suggestions manager service
+            traceBeginAndSlog("StartContentSuggestionsService");
+            mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS);
+            traceEnd();
+
             // NOTE: ClipboardService indirectly depends on IntelligenceService
             traceBeginAndSlog("StartClipboardService");
             mSystemServiceManager.startService(ClipboardService.class);
@@ -1163,6 +1203,15 @@
             }
             traceEnd();
 
+            traceBeginAndSlog("StartIpMemoryStoreService");
+            try {
+                ServiceManager.addService(Context.IP_MEMORY_STORE_SERVICE,
+                        new IpMemoryStoreService(context));
+            } catch (Throwable e) {
+                reportWtf("starting IP Memory Store Service", e);
+            }
+            traceEnd();
+
             traceBeginAndSlog("StartIpSecService");
             try {
                 ipSecService = IpSecService.create(context);
@@ -1183,6 +1232,10 @@
                 traceEnd();
             }
 
+            traceBeginAndSlog("StartAttentionManagerService");
+            mSystemServiceManager.startService(AttentionManagerService.class);
+            traceEnd();
+
             traceBeginAndSlog("StartNetworkScoreService");
             mSystemServiceManager.startService(NetworkScoreService.Lifecycle.class);
             traceEnd();
@@ -1782,9 +1835,6 @@
         mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
         traceEnd();
 
-        // Before things start rolling, be sure we have decided whether
-        // we are in safe mode.
-        final boolean safeMode = wm.detectSafeMode();
         if (safeMode) {
             traceBeginAndSlog("EnterSafeModeAndDisableJitCompilation");
             mActivityManagerService.enterSafeMode();
@@ -1948,7 +1998,8 @@
 
             // Grants default permissions and defines roles
             traceBeginAndSlog("StartRoleManagerService");
-            mSystemServiceManager.startService(RoleManagerService.class);
+            mSystemServiceManager.startService(new RoleManagerService(
+                    mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext)));
             traceEnd();
 
             // No dependency on Webview preparation in system server. But this should
@@ -1981,6 +2032,20 @@
                 reportWtf("starting System UI", e);
             }
             traceEnd();
+            // Enable airplane mode in safe mode. setAirplaneMode() cannot be called
+            // earlier as it sends broadcasts to other services.
+            // TODO: This may actually be too late if radio firmware already started leaking
+            // RF before the respective services start. However, fixing this requires changes
+            // to radio firmware and interfaces.
+            if (safeMode) {
+                traceBeginAndSlog("EnableAirplaneModeInSafeMode");
+                try {
+                    connectivityF.setAirplaneMode(true);
+                } catch (Throwable e) {
+                    reportWtf("enabling Airplane Mode during Safe Mode bootup", e);
+                }
+                traceEnd();
+            }
             traceBeginAndSlog("MakeNetworkManagementServiceReady");
             try {
                 if (networkManagementF != null) {
@@ -2175,7 +2240,7 @@
         windowManager.onSystemUiStarted();
     }
 
-    private static void traceBeginAndSlog(String name) {
+    private static void traceBeginAndSlog(@NonNull String name) {
         Slog.i(TAG, name);
         BOOT_TIMINGS_TRACE_LOG.traceBegin(name);
     }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index e0ae68f..3b4d6a7 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -2,3 +2,19 @@
     name: "services.net",
     srcs: ["java/**/*.java"],
 }
+
+// TODO: move to networking module with DhcpClient and remove lib
+java_library {
+    name: "dhcp-packet-lib",
+    srcs: [
+        "java/android/net/dhcp/*Packet.java",
+    ]
+}
+
+filegroup {
+    name: "services-networkstack-shared-srcs",
+    srcs: [
+        "java/android/net/util/FdEventsReader.java", // TODO: move to NetworkStack with IpClient
+        "java/android/net/shared/*.java",
+    ]
+}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 6ba7d94..ce8b7e7 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,8 +1,5 @@
 package android.net.dhcp;
 
-import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
-import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
-
 import android.annotation.Nullable;
 import android.net.DhcpResults;
 import android.net.LinkAddress;
@@ -37,6 +34,9 @@
 public abstract class DhcpPacket {
     protected static final String TAG = "DhcpPacket";
 
+    // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
+    private static final int IPV4_MIN_MTU = 68;
+
     // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
     // CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
     // DHCP client timeout.
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
new file mode 100644
index 0000000..f068c3a
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -0,0 +1,172 @@
+/*
+ * 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.net.dhcp;
+
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
+
+import android.annotation.NonNull;
+import android.net.LinkAddress;
+
+import com.google.android.collect.Sets;
+
+import java.net.Inet4Address;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building.
+ *
+ * <p>This utility class does not check for validity of the parameters: invalid parameters are
+ * reported by the receiving module when unparceling the parcel.
+ *
+ * @see DhcpServingParams
+ * @hide
+ */
+public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel {
+    public static final int MTU_UNSET = 0;
+
+    /**
+     * Set the server address and served prefix for the DHCP server.
+     *
+     * <p>This parameter is required.
+     */
+    public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) {
+        this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
+        this.serverAddrPrefixLength = serverAddr.getPrefixLength();
+        return this;
+    }
+
+    /**
+     * Set the default routers to be advertised to DHCP clients.
+     *
+     * <p>Each router must be inside the served prefix. This may be an empty set, but it must
+     * always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
+        this.defaultRouters = toIntArray(defaultRouters);
+        return this;
+    }
+
+    /**
+     * Set the default routers to be advertised to DHCP clients.
+     *
+     * <p>Each router must be inside the served prefix. This may be an empty list of routers,
+     * but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
+        return setDefaultRouters(Sets.newArraySet(defaultRouters));
+    }
+
+    /**
+     * Convenience method to build the parameters with no default router.
+     *
+     * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
+     */
+    public DhcpServingParamsParcelExt setNoDefaultRouter() {
+        return setDefaultRouters();
+    }
+
+    /**
+     * Set the DNS servers to be advertised to DHCP clients.
+     *
+     * <p>This may be an empty set, but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
+        this.dnsServers = toIntArray(dnsServers);
+        return this;
+    }
+
+    /**
+     * Set the DNS servers to be advertised to DHCP clients.
+     *
+     * <p>This may be an empty list of servers, but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
+        return setDnsServers(Sets.newArraySet(dnsServers));
+    }
+
+    /**
+     * Convenience method to build the parameters with no DNS server.
+     *
+     * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
+     */
+    public DhcpServingParamsParcelExt setNoDnsServer() {
+        return setDnsServers();
+    }
+
+    /**
+     * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+     *
+     * <p>This parameter is optional. DNS servers and default routers are always excluded
+     * and do not need to be set here.
+     */
+    public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
+        this.excludedAddrs = toIntArray(excludedAddrs);
+        return this;
+    }
+
+    /**
+     * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+     *
+     * <p>This parameter is optional. DNS servers and default routers are always excluded
+     * and do not need to be set here.
+     */
+    public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
+        return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+    }
+
+    /**
+     * Set the lease time for leases assigned by the DHCP server.
+     *
+     * <p>This parameter is required.
+     */
+    public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
+        this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+        return this;
+    }
+
+    /**
+     * Set the link MTU to be advertised to DHCP clients.
+     *
+     * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
+     * is optional and defaults to {@link #MTU_UNSET}.
+     */
+    public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) {
+        this.linkMtu = linkMtu;
+        return this;
+    }
+
+    /**
+     * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option.
+     *
+     * <p>If not set, the default value is false.
+     */
+    public DhcpServingParamsParcelExt setMetered(boolean metered) {
+        this.metered = metered;
+        return this;
+    }
+
+    private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
+        int[] res = new int[addrs.size()];
+        int i = 0;
+        for (Inet4Address addr : addrs) {
+            res[i] = inet4AddressToIntHTH(addr);
+            i++;
+        }
+        return res;
+    }
+}
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 493350d..8b22f68 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -17,20 +17,26 @@
 package android.net.ip;
 
 import static android.net.NetworkUtils.numericToInetAddress;
-import static android.net.util.NetworkConstants.asByte;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.util.NetworkConstants.FF;
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+import static android.net.util.NetworkConstants.asByte;
 
+import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetd;
+import android.net.INetworkStackStatusCallback;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.NetworkStack;
 import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.DhcpServingParamsParcelExt;
+import android.net.dhcp.IDhcpServer;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
@@ -126,6 +132,10 @@
     }
 
     public static class Dependencies {
+        private final Context mContext;
+        public Dependencies(Context context) {
+            mContext = context;
+        }
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
         }
@@ -138,9 +148,12 @@
             return NetdService.getInstance();
         }
 
-        public DhcpServer makeDhcpServer(Looper looper, String ifName,
-                DhcpServingParams params, SharedLog log) {
-            return new DhcpServer(looper, ifName, params, log);
+        /**
+         * Create a DhcpServer instance to be used by IpServer.
+         */
+        public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+                DhcpServerCallbacks cb) {
+            mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
         }
     }
 
@@ -197,7 +210,10 @@
     // Advertisements (otherwise, we do not add them to mLinkProperties at all).
     private LinkProperties mLastIPv6LinkProperties;
     private RouterAdvertisementDaemon mRaDaemon;
-    private DhcpServer mDhcpServer;
+
+    // To be accessed only on the handler thread
+    private int mDhcpServerStartIndex = 0;
+    private IDhcpServer mDhcpServer;
     private RaParams mLastRaParams;
 
     public IpServer(
@@ -252,35 +268,109 @@
 
     private boolean startIPv4() { return configureIPv4(true); }
 
+    /**
+     * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
+     * handler.
+     *
+     * <p>Different instances of this class can be created for each call to IDhcpServer methods,
+     * with different implementations of the callback, to differentiate handling of success/error in
+     * each call.
+     */
+    private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub {
+        @Override
+        public void onStatusAvailable(int statusCode) {
+            getHandler().post(() -> callback(statusCode));
+        }
+
+        public abstract void callback(int statusCode);
+    }
+
+    private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
+        private final int mStartIndex;
+
+        private DhcpServerCallbacksImpl(int startIndex) {
+            mStartIndex = startIndex;
+        }
+
+        @Override
+        public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException {
+            getHandler().post(() -> {
+                // We are on the handler thread: mDhcpServerStartIndex can be read safely.
+                if (mStartIndex != mDhcpServerStartIndex) {
+                    // This start request is obsolete. When the |server| binder token goes out of
+                    // scope, the garbage collector will finalize it, which causes the network stack
+                    // process garbage collector to collect the server itself.
+                    return;
+                }
+
+                if (statusCode != STATUS_SUCCESS) {
+                    mLog.e("Error obtaining DHCP server: " + statusCode);
+                    handleError();
+                    return;
+                }
+
+                mDhcpServer = server;
+                try {
+                    mDhcpServer.start(new OnHandlerStatusCallback() {
+                        @Override
+                        public void callback(int startStatusCode) {
+                            if (startStatusCode != STATUS_SUCCESS) {
+                                mLog.e("Error starting DHCP server: " + startStatusCode);
+                                handleError();
+                            }
+                        }
+                    });
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            });
+        }
+
+        private void handleError() {
+            mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+            transitionTo(mInitialState);
+        }
+    }
+
     private boolean startDhcp(Inet4Address addr, int prefixLen) {
         if (mUsingLegacyDhcp) {
             return true;
         }
-        final DhcpServingParams params;
-        try {
-            params = new DhcpServingParams.Builder()
-                    .setDefaultRouters(addr)
-                    .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
-                    .setDnsServers(addr)
-                    .setServerAddr(new LinkAddress(addr, prefixLen))
-                    .setMetered(true)
-                    .build();
-            // TODO: also advertise link MTU
-        } catch (DhcpServingParams.InvalidParameterException e) {
-            Log.e(TAG, "Invalid DHCP parameters", e);
-            return false;
-        }
+        final DhcpServingParamsParcel params;
+        params = new DhcpServingParamsParcelExt()
+                .setDefaultRouters(addr)
+                .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
+                .setDnsServers(addr)
+                .setServerAddr(new LinkAddress(addr, prefixLen))
+                .setMetered(true);
+        // TODO: also advertise link MTU
 
-        mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
-                mLog.forSubComponent("DHCP"));
-        mDhcpServer.start();
+        mDhcpServerStartIndex++;
+        mDeps.makeDhcpServer(
+                mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
         return true;
     }
 
     private void stopDhcp() {
+        // Make all previous start requests obsolete so servers are not started later
+        mDhcpServerStartIndex++;
+
         if (mDhcpServer != null) {
-            mDhcpServer.stop();
-            mDhcpServer = null;
+            try {
+                mDhcpServer.stop(new OnHandlerStatusCallback() {
+                    @Override
+                    public void callback(int statusCode) {
+                        if (statusCode != STATUS_SUCCESS) {
+                            mLog.e("Error stopping DHCP server: " + statusCode);
+                            mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+                            // Not much more we can do here
+                        }
+                    }
+                });
+                mDhcpServer = null;
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
         }
     }
 
diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
new file mode 100644
index 0000000..5b77f54
--- /dev/null
+++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.shared;
+
+import static android.net.shared.ParcelableUtil.fromParcelableArray;
+import static android.net.shared.ParcelableUtil.toParcelableArray;
+
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpPrefixParcelable;
+import android.net.LinkAddress;
+import android.net.LinkAddressParcelable;
+import android.net.LinkProperties;
+import android.net.LinkPropertiesParcelable;
+import android.net.ProxyInfo;
+import android.net.ProxyInfoParcelable;
+import android.net.RouteInfo;
+import android.net.RouteInfoParcelable;
+import android.net.Uri;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+
+/**
+ * Collection of utility methods to convert to and from stable AIDL parcelables for LinkProperties
+ * and its attributes.
+ * @hide
+ */
+public final class LinkPropertiesParcelableUtil {
+
+    /**
+     * Convert a ProxyInfo to a ProxyInfoParcelable
+     */
+    public static ProxyInfoParcelable toStableParcelable(@Nullable ProxyInfo proxyInfo) {
+        if (proxyInfo == null) {
+            return null;
+        }
+        final ProxyInfoParcelable parcel = new ProxyInfoParcelable();
+        parcel.host = proxyInfo.getHost();
+        parcel.port = proxyInfo.getPort();
+        parcel.exclusionList = proxyInfo.getExclusionList();
+        parcel.pacFileUrl = proxyInfo.getPacFileUrl().toString();
+        return parcel;
+    }
+
+    /**
+     * Convert a ProxyInfoParcelable to a ProxyInfo
+     */
+    public static ProxyInfo fromStableParcelable(@Nullable ProxyInfoParcelable parcel) {
+        if (parcel == null) {
+            return null;
+        }
+        if (Uri.EMPTY.toString().equals(parcel.pacFileUrl)) {
+            return ProxyInfo.buildDirectProxy(
+                    parcel.host, parcel.port, Arrays.asList(parcel.exclusionList));
+        } else {
+            return ProxyInfo.buildPacProxy(Uri.parse(parcel.pacFileUrl));
+        }
+    }
+
+    /**
+     * Convert an IpPrefixParcelable to an IpPrefix
+     */
+    public static IpPrefixParcelable toStableParcelable(@Nullable IpPrefix ipPrefix) {
+        if (ipPrefix == null) {
+            return null;
+        }
+        final IpPrefixParcelable parcel = new IpPrefixParcelable();
+        parcel.address = ipPrefix.getAddress().getHostAddress();
+        parcel.prefixLength = ipPrefix.getPrefixLength();
+        return parcel;
+    }
+
+    /**
+     * Convert an IpPrefix to an IpPrefixParcelable
+     */
+    public static IpPrefix fromStableParcelable(@Nullable IpPrefixParcelable parcel) {
+        if (parcel == null) {
+            return null;
+        }
+        return new IpPrefix(InetAddresses.parseNumericAddress(parcel.address), parcel.prefixLength);
+    }
+
+    /**
+     * Convert a RouteInfoParcelable to a RouteInfo
+     */
+    public static RouteInfoParcelable toStableParcelable(@Nullable RouteInfo routeInfo) {
+        if (routeInfo == null) {
+            return null;
+        }
+        final RouteInfoParcelable parcel = new RouteInfoParcelable();
+        parcel.destination = toStableParcelable(routeInfo.getDestination());
+        parcel.gatewayAddr = routeInfo.getGateway().getHostAddress();
+        parcel.ifaceName = routeInfo.getInterface();
+        parcel.type = routeInfo.getType();
+        return parcel;
+    }
+
+    /**
+     * Convert a RouteInfo to a RouteInfoParcelable
+     */
+    public static RouteInfo fromStableParcelable(@Nullable RouteInfoParcelable parcel) {
+        if (parcel == null) {
+            return null;
+        }
+        final IpPrefix destination = fromStableParcelable(parcel.destination);
+        return new RouteInfo(
+                destination, InetAddresses.parseNumericAddress(parcel.gatewayAddr),
+                parcel.ifaceName, parcel.type);
+    }
+
+    /**
+     * Convert a LinkAddressParcelable to a LinkAddress
+     */
+    public static LinkAddressParcelable toStableParcelable(@Nullable LinkAddress la) {
+        if (la == null) {
+            return null;
+        }
+        final LinkAddressParcelable parcel = new LinkAddressParcelable();
+        parcel.address = la.getAddress().getHostAddress();
+        parcel.prefixLength = la.getPrefixLength();
+        parcel.flags = la.getFlags();
+        parcel.scope = la.getScope();
+        return parcel;
+    }
+
+    /**
+     * Convert a LinkAddress to a LinkAddressParcelable
+     */
+    public static LinkAddress fromStableParcelable(@Nullable LinkAddressParcelable parcel) {
+        if (parcel == null) {
+            return null;
+        }
+        return new LinkAddress(
+                InetAddresses.parseNumericAddress(parcel.address),
+                parcel.prefixLength,
+                parcel.flags,
+                parcel.scope);
+    }
+
+    /**
+     * Convert a LinkProperties to a LinkPropertiesParcelable
+     */
+    public static LinkPropertiesParcelable toStableParcelable(@Nullable LinkProperties lp) {
+        if (lp == null) {
+            return null;
+        }
+        final LinkPropertiesParcelable parcel = new LinkPropertiesParcelable();
+        parcel.ifaceName = lp.getInterfaceName();
+        parcel.linkAddresses = toParcelableArray(
+                lp.getLinkAddresses(),
+                LinkPropertiesParcelableUtil::toStableParcelable,
+                LinkAddressParcelable.class);
+        parcel.dnses = toParcelableArray(
+                lp.getDnsServers(), InetAddress::getHostAddress, String.class);
+        parcel.pcscfs = toParcelableArray(
+                lp.getPcscfServers(), InetAddress::getHostAddress, String.class);
+        parcel.validatedPrivateDnses = toParcelableArray(
+                lp.getValidatedPrivateDnsServers(), InetAddress::getHostAddress, String.class);
+        parcel.usePrivateDns = lp.isPrivateDnsActive();
+        parcel.privateDnsServerName = lp.getPrivateDnsServerName();
+        parcel.domains = lp.getDomains();
+        parcel.routes = toParcelableArray(
+                lp.getRoutes(), LinkPropertiesParcelableUtil::toStableParcelable,
+                RouteInfoParcelable.class);
+        parcel.httpProxy = toStableParcelable(lp.getHttpProxy());
+        parcel.mtu = lp.getMtu();
+        parcel.tcpBufferSizes = lp.getTcpBufferSizes();
+        parcel.nat64Prefix = toStableParcelable(lp.getNat64Prefix());
+        parcel.stackedLinks = toParcelableArray(
+                lp.getStackedLinks(), LinkPropertiesParcelableUtil::toStableParcelable,
+                LinkPropertiesParcelable.class);
+        return parcel;
+    }
+
+    /**
+     * Convert a LinkPropertiesParcelable to a LinkProperties
+     */
+    public static LinkProperties fromStableParcelable(@Nullable LinkPropertiesParcelable parcel) {
+        if (parcel == null) {
+            return null;
+        }
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(parcel.ifaceName);
+        lp.setLinkAddresses(fromParcelableArray(parcel.linkAddresses,
+                LinkPropertiesParcelableUtil::fromStableParcelable));
+        lp.setDnsServers(fromParcelableArray(parcel.dnses, InetAddresses::parseNumericAddress));
+        lp.setPcscfServers(fromParcelableArray(parcel.pcscfs, InetAddresses::parseNumericAddress));
+        lp.setValidatedPrivateDnsServers(
+                fromParcelableArray(parcel.validatedPrivateDnses,
+                InetAddresses::parseNumericAddress));
+        lp.setUsePrivateDns(parcel.usePrivateDns);
+        lp.setPrivateDnsServerName(parcel.privateDnsServerName);
+        lp.setDomains(parcel.domains);
+        for (RouteInfoParcelable route : parcel.routes) {
+            lp.addRoute(fromStableParcelable(route));
+        }
+        lp.setHttpProxy(fromStableParcelable(parcel.httpProxy));
+        lp.setMtu(parcel.mtu);
+        lp.setTcpBufferSizes(parcel.tcpBufferSizes);
+        lp.setNat64Prefix(fromStableParcelable(parcel.nat64Prefix));
+        for (LinkPropertiesParcelable stackedLink : parcel.stackedLinks) {
+            lp.addStackedLink(fromStableParcelable(stackedLink));
+        }
+        return lp;
+    }
+}
diff --git a/services/net/java/android/net/shared/NetworkMonitorUtils.java b/services/net/java/android/net/shared/NetworkMonitorUtils.java
new file mode 100644
index 0000000..463cf2a
--- /dev/null
+++ b/services/net/java/android/net/shared/NetworkMonitorUtils.java
@@ -0,0 +1,66 @@
+/*
+ * 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.net.shared;
+
+import android.content.Context;
+import android.net.NetworkCapabilities;
+import android.provider.Settings;
+
+/** @hide */
+public class NetworkMonitorUtils {
+
+    // Network conditions broadcast constants
+    public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
+            "android.net.conn.NETWORK_CONDITIONS_MEASURED";
+    public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
+    public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
+    public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
+    public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
+    public static final String EXTRA_CELL_ID = "extra_cellid";
+    public static final String EXTRA_SSID = "extra_ssid";
+    public static final String EXTRA_BSSID = "extra_bssid";
+    /** real time since boot */
+    public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
+    public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
+    public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
+            "android.permission.ACCESS_NETWORK_CONDITIONS";
+
+    // TODO: once the URL is a resource overlay, remove and have the resource define the default
+    private static final String DEFAULT_HTTP_URL =
+            "http://connectivitycheck.gstatic.com/generate_204";
+
+    /**
+     * Get the captive portal server HTTP URL that is configured on the device.
+     */
+    public static String getCaptivePortalServerHttpUrl(Context context) {
+        final String settingUrl = Settings.Global.getString(
+                context.getContentResolver(),
+                Settings.Global.CAPTIVE_PORTAL_HTTP_URL);
+        return settingUrl != null ? settingUrl : DEFAULT_HTTP_URL;
+    }
+
+    /**
+     * Return whether validation is required for a network.
+     * @param dfltNetCap Default requested network capabilities.
+     * @param nc Network capabilities of the network to test.
+     */
+    public static boolean isValidationRequired(
+            NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
+        // TODO: Consider requiring validation for DUN networks.
+        return dfltNetCap.satisfiedByNetworkCapabilities(nc);
+    }
+}
diff --git a/services/net/java/android/net/shared/ParcelableUtil.java b/services/net/java/android/net/shared/ParcelableUtil.java
new file mode 100644
index 0000000..a18976c
--- /dev/null
+++ b/services/net/java/android/net/shared/ParcelableUtil.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.shared;
+
+import android.annotation.NonNull;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Utility methods to help convert to/from stable parcelables.
+ * @hide
+ */
+public final class ParcelableUtil {
+    // Below methods could be implemented easily with streams, but streams are frowned upon in
+    // frameworks code.
+
+    /**
+     * Convert a list of BaseType items to an array of ParcelableType items using the specified
+     * converter function.
+     */
+    public static <ParcelableType, BaseType> ParcelableType[] toParcelableArray(
+            @NonNull List<BaseType> base,
+            @NonNull Function<BaseType, ParcelableType> conv,
+            @NonNull Class<ParcelableType> parcelClass) {
+        final ParcelableType[] out = (ParcelableType[]) Array.newInstance(parcelClass, base.size());
+        int i = 0;
+        for (BaseType b : base) {
+            out[i] = conv.apply(b);
+            i++;
+        }
+        return out;
+    }
+
+    /**
+     * Convert an array of ParcelableType items to a list of BaseType items using the specified
+     * converter function.
+     */
+    public static <ParcelableType, BaseType> ArrayList<BaseType> fromParcelableArray(
+            @NonNull ParcelableType[] parceled, @NonNull Function<ParcelableType, BaseType> conv) {
+        final ArrayList<BaseType> out = new ArrayList<>(parceled.length);
+        for (ParcelableType t : parceled) {
+            out.add(conv.apply(t));
+        }
+        return out;
+    }
+}
diff --git a/services/net/java/android/net/shared/PrivateDnsConfig.java b/services/net/java/android/net/shared/PrivateDnsConfig.java
new file mode 100644
index 0000000..41e0bad
--- /dev/null
+++ b/services/net/java/android/net/shared/PrivateDnsConfig.java
@@ -0,0 +1,94 @@
+/*
+ * 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.net.shared;
+
+import android.net.InetAddresses;
+import android.net.PrivateDnsConfigParcel;
+import android.text.TextUtils;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+
+/** @hide */
+public class PrivateDnsConfig {
+    public final boolean useTls;
+    public final String hostname;
+    public final InetAddress[] ips;
+
+    public PrivateDnsConfig() {
+        this(false);
+    }
+
+    public PrivateDnsConfig(boolean useTls) {
+        this.useTls = useTls;
+        this.hostname = "";
+        this.ips = new InetAddress[0];
+    }
+
+    public PrivateDnsConfig(String hostname, InetAddress[] ips) {
+        this.useTls = !TextUtils.isEmpty(hostname);
+        this.hostname = useTls ? hostname : "";
+        this.ips = (ips != null) ? ips : new InetAddress[0];
+    }
+
+    public PrivateDnsConfig(PrivateDnsConfig cfg) {
+        useTls = cfg.useTls;
+        hostname = cfg.hostname;
+        ips = cfg.ips;
+    }
+
+    /**
+     * Indicates whether this is a strict mode private DNS configuration.
+     */
+    public boolean inStrictMode() {
+        return useTls && !TextUtils.isEmpty(hostname);
+    }
+
+    @Override
+    public String toString() {
+        return PrivateDnsConfig.class.getSimpleName()
+                + "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
+    }
+
+    /**
+     * Create a stable AIDL-compatible parcel from the current instance.
+     */
+    public PrivateDnsConfigParcel toParcel() {
+        final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel();
+        parcel.hostname = hostname;
+
+        final String[] parceledIps = new String[ips.length];
+        for (int i = 0; i < ips.length; i++) {
+            parceledIps[i] = ips[i].getHostAddress();
+        }
+        parcel.ips = parceledIps;
+
+        return parcel;
+    }
+
+    /**
+     * Build a configuration from a stable AIDL-compatible parcel.
+     */
+    public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) {
+        final InetAddress[] ips = new InetAddress[parcel.ips.length];
+        for (int i = 0; i < ips.length; i++) {
+            ips[i] = InetAddresses.parseNumericAddress(parcel.ips[i]);
+        }
+
+        return new PrivateDnsConfig(parcel.hostname, ips);
+    }
+}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 3defe56..c183b81 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -16,9 +16,6 @@
 
 package android.net.util;
 
-import java.nio.ByteBuffer;
-
-
 /**
  * Networking protocol constants.
  *
@@ -81,8 +78,6 @@
      *     - https://tools.ietf.org/html/rfc791
      */
     public static final int IPV4_HEADER_MIN_LEN = 20;
-    public static final int IPV4_MIN_MTU = 68;
-    public static final int IPV4_MAX_MTU = 65_535;
     public static final int IPV4_IHL_MASK = 0xf;
     public static final int IPV4_FLAGS_OFFSET = 6;
     public static final int IPV4_FRAGMENT_MASK = 0x1fff;
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 5a73a4e..2cdb2b0 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -32,11 +32,12 @@
  *
  * All access to class methods other than dump() must be on the same thread.
  *
+ * TODO: this is a copy of SharedLog in the NetworkStack. Remove after Tethering is migrated.
  * @hide
  */
 public class SharedLog {
-    private final static int DEFAULT_MAX_RECORDS = 500;
-    private final static String COMPONENT_DELIMITER = ".";
+    private static final int DEFAULT_MAX_RECORDS = 500;
+    private static final String COMPONENT_DELIMITER = ".";
 
     private enum Category {
         NONE,
@@ -69,6 +70,13 @@
         mComponent = component;
     }
 
+    public String getTag() {
+        return mTag;
+    }
+
+    /**
+     * Create a SharedLog based on this log with an additional component prefix on each logged line.
+     */
     public SharedLog forSubComponent(String component) {
         if (!isRootLogInstance()) {
             component = mComponent + COMPONENT_DELIMITER + component;
@@ -76,6 +84,11 @@
         return new SharedLog(mLocalLog, mTag, component);
     }
 
+    /**
+     * Dump the contents of this log.
+     *
+     * <p>This method may be called on any thread.
+     */
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
     }
@@ -84,10 +97,21 @@
     // Methods that both log an entry and emit it to the system log.
     //////
 
+    /**
+     * Log an error due to an exception. This does not include the exception stacktrace.
+     *
+     * <p>The log entry will be also added to the system log.
+     * @see #e(String, Throwable)
+     */
     public void e(Exception e) {
         Log.e(mTag, record(Category.ERROR, e.toString()));
     }
 
+    /**
+     * Log an error message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void e(String msg) {
         Log.e(mTag, record(Category.ERROR, msg));
     }
@@ -96,7 +120,7 @@
      * Log an error due to an exception, with the exception stacktrace if provided.
      *
      * <p>The error and exception message appear in the shared log, but the stacktrace is only
-     * logged in general log output (logcat).
+     * logged in general log output (logcat). The log entry will be also added to the system log.
      */
     public void e(@NonNull String msg, @Nullable Throwable exception) {
         if (exception == null) {
@@ -106,10 +130,20 @@
         Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
     }
 
+    /**
+     * Log an informational message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void i(String msg) {
         Log.i(mTag, record(Category.NONE, msg));
     }
 
+    /**
+     * Log a warning message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void w(String msg) {
         Log.w(mTag, record(Category.WARN, msg));
     }
@@ -118,14 +152,30 @@
     // Methods that only log an entry (and do NOT emit to the system log).
     //////
 
+    /**
+     * Log a general message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
     public void log(String msg) {
         record(Category.NONE, msg);
     }
 
+    /**
+     * Log a general, formatted message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     * @see String#format(String, Object...)
+     */
     public void logf(String fmt, Object... args) {
         log(String.format(fmt, args));
     }
 
+    /**
+     * Log a message with MARK level.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
     public void mark(String msg) {
         record(Category.MARK, msg);
     }
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 9159f0d..0cf0d34 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,9 +26,6 @@
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    bmgrlib \
-    bu \
-    services.backup \
     services.core \
     services.net
 
@@ -41,8 +38,7 @@
 
 LOCAL_MODULE := FrameworksServicesRoboTests
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-    $(call all-java-files-under, backup/src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res
@@ -82,8 +78,7 @@
 
 LOCAL_TEST_PACKAGE := FrameworksServicesLib
 
-LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) \
-    $(call find-files-in-subdirs,$(LOCAL_PATH)/backup/src,*Test.java,.)
+LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.)
 
 include external/robolectric-shadows/run_robotests.mk
 
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index b8723c5..b8db3f3 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.backup;
 
+import static android.Manifest.permission.BACKUP;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
@@ -45,6 +46,7 @@
 import android.util.SparseArray;
 
 import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowBinder;
 
 import org.junit.After;
@@ -64,7 +66,7 @@
 
 /** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBinder.class})
+@Config(shadows = {ShadowApplicationPackageManager.class, ShadowBinder.class})
 @Presubmit
 public class BackupManagerServiceTest {
     private static final String TEST_PACKAGE = "package";
@@ -192,6 +194,19 @@
 
     /** Test that the service unregisters users when stopped. */
     @Test
+    public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+
+        backupManagerService.stopServiceForUser(mUserOneId);
+
+        verify(mUserOneService).tearDownService();
+        verify(mUserTwoService, never()).tearDownService();
+    }
+
+    /** Test that the service unregisters users when stopped. */
+    @Test
     public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
         BackupManagerService backupManagerService = createService();
 
@@ -1061,7 +1076,7 @@
                 createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
         FullBackupJob job = new FullBackupJob();
 
-        backupManagerService.beginFullBackup(job);
+        backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
 
         verify(mUserOneService).beginFullBackup(job);
     }
@@ -1072,7 +1087,7 @@
         BackupManagerService backupManagerService = createService();
         FullBackupJob job = new FullBackupJob();
 
-        backupManagerService.beginFullBackup(job);
+        backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
 
         verify(mUserOneService, never()).beginFullBackup(job);
     }
@@ -1083,7 +1098,7 @@
         BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
 
-        backupManagerService.endFullBackup();
+        backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
         verify(mUserOneService).endFullBackup();
     }
@@ -1093,7 +1108,7 @@
     public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
         BackupManagerService backupManagerService = createService();
 
-        backupManagerService.endFullBackup();
+        backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
         verify(mUserOneService, never()).endFullBackup();
     }
@@ -1528,6 +1543,7 @@
     }
 
     private BackupManagerService createService() {
+        mShadowContext.grantPermissions(BACKUP);
         return new BackupManagerService(
                 mContext, new Trampoline(mContext), startBackupThread(null));
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
new file mode 100644
index 0000000..9a78d0b3
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 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 static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobScheduler;
+import android.content.Context;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowJobScheduler;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowJobScheduler.class})
+@Presubmit
+public class FullBackupJobTest {
+    private Context mContext;
+    private BackupManagerConstants mConstants;
+    private ShadowJobScheduler mShadowJobScheduler;
+
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = RuntimeEnvironment.application;
+        mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
+        mConstants.start();
+
+        mShadowJobScheduler = Shadows.shadowOf(mContext.getSystemService(JobScheduler.class));
+
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mConstants.stop();
+        FullBackupJob.cancel(mUserOneId, mContext);
+        FullBackupJob.cancel(mUserTwoId, mContext);
+    }
+
+    @Test
+    public void testSchedule_afterScheduling_jobExists() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+    }
+
+    @Test
+    public void testCancel_afterCancelling_jobDoesntExist() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+        FullBackupJob.cancel(mUserOneId, mContext);
+        FullBackupJob.cancel(mUserTwoId, mContext);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+    }
+//
+    @Test
+    public void testSchedule_onlySchedulesForRequestedUser() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+    }
+//
+    @Test
+    public void testCancel_onlyCancelsForRequestedUser() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+        FullBackupJob.cancel(mUserOneId, mContext);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+    }
+
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(FullBackupJob.MIN_JOB_ID, FullBackupJob.MAX_JOB_ID,
+                userId);
+    }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
new file mode 100644
index 0000000..f8bb1ee
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 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 static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import static org.testng.Assert.expectThrows;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class JobIdManagerTest {
+    private static final int MIN_JOB_ID = 10;
+    private static final int MAX_JOB_ID = 20;
+
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
+    @Before
+    public void setUp() {
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
+    }
+
+    @Test
+    public void testGetJobIdForUserId_returnsDifferentJobIdsForDifferentUsers() {
+        int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+        int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserTwoId);
+
+        assertThat(jobIdOne).isNotEqualTo(jobIdTwo);
+    }
+
+    @Test
+    public void testGetJobIdForUserId_returnsSameJobIdForSameUser() {
+        int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+        int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+
+        assertThat(jobIdOne).isEqualTo(jobIdTwo);
+    }
+
+    @Test
+    public void testGetJobIdForUserId_throwsExceptionIfRangeIsExceeded() {
+        expectThrows(
+                RuntimeException.class,
+                () -> JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID,
+                        MAX_JOB_ID + 1));
+    }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
index 8e17209..8d9e44f 100644
--- a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
@@ -18,8 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 import org.junit.After;
@@ -35,32 +37,67 @@
     private Context mContext;
     private BackupManagerConstants mConstants;
 
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
     @Before
     public void setUp() throws Exception {
         mContext = RuntimeEnvironment.application;
         mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
         mConstants.start();
+
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
     }
 
     @After
     public void tearDown() throws Exception {
         mConstants.stop();
-        KeyValueBackupJob.cancel(mContext);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+        KeyValueBackupJob.cancel(mUserTwoId, mContext);
     }
 
     @Test
     public void testIsScheduled_beforeScheduling_returnsFalse() {
-        boolean isScheduled = KeyValueBackupJob.isScheduled();
-
-        assertThat(isScheduled).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
     }
 
     @Test
     public void testIsScheduled_afterScheduling_returnsTrue() {
-        KeyValueBackupJob.schedule(mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
 
-        boolean isScheduled = KeyValueBackupJob.isScheduled();
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
+    }
 
-        assertThat(isScheduled).isTrue();
+    @Test
+    public void testIsScheduled_afterCancelling_returnsFalse() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+        KeyValueBackupJob.cancel(mUserTwoId, mContext);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+    }
+
+    @Test
+    public void testIsScheduled_afterScheduling_returnsTrueOnlyForScheduledUser() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+    }
+
+    @Test
+    public void testIsScheduled_afterCancelling_returnsFalseOnlyForCancelledUser() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
     }
 }
diff --git a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
index 693092d..9a6e003 100644
--- a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
@@ -41,6 +41,7 @@
 import static java.util.stream.Stream.concat;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupTransport;
 import android.content.ComponentName;
@@ -48,6 +49,7 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 import com.android.server.backup.testing.TransportData;
@@ -56,7 +58,9 @@
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.transport.TransportClientManager;
 import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -64,6 +68,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowPackageManager;
 
 import java.util.ArrayList;
@@ -73,6 +78,7 @@
 import java.util.stream.Stream;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowApplicationPackageManager.class})
 @Presubmit
 public class TransportManagerTest {
     private static final String PACKAGE_A = "some.package.a";
@@ -84,12 +90,14 @@
     private TransportData mTransportA2;
     private TransportData mTransportB1;
     private ShadowPackageManager mShadowPackageManager;
+    private @UserIdInt int mUserId;
     private Context mContext;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mUserId = UserHandle.USER_SYSTEM;
         mContext = RuntimeEnvironment.application;
         mShadowPackageManager = shadowOf(mContext.getPackageManager());
 
@@ -98,6 +106,12 @@
         mTransportB1 = genericTransport(PACKAGE_B, "TransportBaz");
     }
 
+    /** Reset shadow state. */
+    @After
+    public void tearDown() throws Exception {
+        ShadowApplicationPackageManager.reset();
+    }
+
     @Test
     public void testRegisterTransports() throws Exception {
         setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
@@ -662,7 +676,8 @@
         packageInfo.packageName = packageName;
         packageInfo.applicationInfo = new ApplicationInfo();
         packageInfo.applicationInfo.privateFlags = flags;
-        mShadowPackageManager.addPackage(packageInfo);
+        mShadowPackageManager.installPackage(packageInfo);
+        ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
     }
 
     private TransportManager createTransportManagerWithRegisteredTransports(
@@ -684,6 +699,7 @@
             @Nullable TransportData selectedTransport, TransportData... transports) {
         TransportManager transportManager =
                 new TransportManager(
+                        mUserId,
                         mContext,
                         merge(selectedTransport, transports)
                                 .stream()
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index bb60c27..427aed7 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -42,6 +42,8 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.PowerManager;
@@ -54,6 +56,7 @@
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowBinder;
 import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
 import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
@@ -80,7 +83,7 @@
  * UserBackupManagerService} that performs operations for its target user.
  */
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAppBackupUtils.class})
+@Config(shadows = {ShadowAppBackupUtils.class, ShadowApplicationPackageManager.class})
 @Presubmit
 public class UserBackupManagerServiceTest {
     private static final String TAG = "BMSTest";
@@ -137,6 +140,7 @@
     public void tearDown() throws Exception {
         mBackupThread.quit();
         ShadowAppBackupUtils.reset();
+        ShadowApplicationPackageManager.reset();
     }
 
     /**
@@ -195,6 +199,7 @@
     public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
@@ -210,6 +215,7 @@
     public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+        registerPackages(PACKAGE_1);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
@@ -228,6 +234,7 @@
     public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
@@ -245,6 +252,7 @@
     public void testFilterAppsEligibleForBackup() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1, PACKAGE_2);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
@@ -264,6 +272,7 @@
     @Test
     public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        registerPackages(PACKAGE_1, PACKAGE_2);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         String[] filtered =
@@ -281,6 +290,7 @@
     public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1, PACKAGE_2);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
@@ -504,10 +514,14 @@
     private void setUpForUpdateTransportAttributes() throws Exception {
         mTransportComponent = mTransport.getTransportComponent();
         String transportPackage = mTransportComponent.getPackageName();
+        PackageInfo packageInfo = getPackageInfo(transportPackage);
 
         ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
-        shadowPackageManager.addPackage(transportPackage);
+        shadowPackageManager.installPackage(packageInfo);
         shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+        // Set up for user invocations on ApplicationPackageManager.
+        ShadowApplicationPackageManager.addInstalledPackage(transportPackage, packageInfo);
+        ShadowApplicationPackageManager.setPackageUid(transportPackage, PACKAGE_UID);
 
         mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
     }
@@ -749,7 +763,7 @@
     private void setUpForRequestBackup(String... packages) throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         for (String packageName : packages) {
-            mShadowPackageManager.addPackage(packageName);
+            registerPackages(packageName);
             ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
         }
         setUpCurrentTransport(mTransportManager, mTransport);
@@ -829,7 +843,7 @@
     public void testRequestBackup_whenNotProvisioned() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
-        backupManagerService.setProvisioned(false);
+        backupManagerService.setSetupComplete(false);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
 
@@ -848,7 +862,7 @@
         setUpCurrentTransport(mTransportManager, mTransport.unregistered());
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
 
@@ -864,11 +878,11 @@
     @Test
     public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        mShadowPackageManager.addPackage(PACKAGE_1);
+        registerPackages(PACKAGE_1);
         setUpCurrentTransport(mTransportManager, mTransport);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
         // Haven't set PACKAGE_1 as eligible
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -965,14 +979,13 @@
     private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
         return backupManagerService;
     }
 
     /**
-     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
-     * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
-     * to the backup thread.
+     * Test verifying that creating a new instance posts a transport registration task to the backup
+     * thread.
      */
     @Test
     public void testCreateAndInitializeService_postRegisterTransports() {
@@ -992,9 +1005,8 @@
     }
 
     /**
-     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
-     * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
-     * transports on the main thread.
+     * Test verifying that creating a new instance does not directly register transports on the main
+     * thread.
      */
     @Test
     public void testCreateAndInitializeService_doesNotRegisterTransportsSynchronously() {
@@ -1013,11 +1025,7 @@
         verify(mTransportManager, never()).registerTransports();
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullContext_throws() {
         expectThrows(
@@ -1033,11 +1041,7 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullTrampoline_throws() {
         expectThrows(
@@ -1053,11 +1057,7 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullBackupThread_throws() {
         expectThrows(
@@ -1073,11 +1073,7 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullStateDir_throws() {
         expectThrows(
@@ -1145,6 +1141,22 @@
         backupManagerService.setPowerManager(powerManagerMock);
     }
 
+    private PackageInfo getPackageInfo(String packageName) {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = packageName;
+        packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.packageName = packageName;
+        return packageInfo;
+    }
+
+    private void registerPackages(String... packages) {
+        for (String packageName : packages) {
+            PackageInfo packageInfo = getPackageInfo(packageName);
+            mShadowPackageManager.installPackage(packageInfo);
+            ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
+        }
+    }
+
     /**
      * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
      * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
@@ -1155,8 +1167,9 @@
          * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
          * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
          */
-        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
-            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+        public static void schedule(int userId, Context ctx, long delay,
+                BackupManagerConstants constants) {
+            ShadowKeyValueBackupJob.schedule(userId, ctx, delay, constants);
             throw new IllegalArgumentException();
         }
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index 423512c..aca48b6 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -12,6 +12,7 @@
 import static org.testng.Assert.expectThrows;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.Application;
 import android.app.backup.BackupDataInput;
 import android.app.backup.FullBackupDataOutput;
@@ -62,6 +63,7 @@
     private static final String TEST_PACKAGE_INSTALLER = "com.test.package.installer";
     private static final Long TEST_PACKAGE_VERSION_CODE = 100L;
 
+    private @UserIdInt int mUserId;
     private PackageManager mPackageManager;
     private ShadowApplicationPackageManager mShadowPackageManager;
     private File mFilesDir;
@@ -72,6 +74,7 @@
     public void setUp() throws Exception {
         Application application = RuntimeEnvironment.application;
 
+        mUserId = UserHandle.USER_SYSTEM;
         mPackageManager = application.getPackageManager();
         mShadowPackageManager = (ShadowApplicationPackageManager) shadowOf(mPackageManager);
 
@@ -352,7 +355,7 @@
         byte[] obbBytes = "obb".getBytes();
         File obbFile = createObbFileAndWrite(obbDir, obbBytes);
 
-        mBackupWriter.backupObb(packageInfo);
+        mBackupWriter.backupObb(mUserId, packageInfo);
 
         byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
         assertThat(writtenBytes).isEqualTo(obbBytes);
@@ -366,7 +369,7 @@
         File obbDir = createObbDirForPackage(packageInfo.packageName);
         // No obb file created.
 
-        mBackupWriter.backupObb(packageInfo);
+        mBackupWriter.backupObb(mUserId, packageInfo);
 
         assertThat(mBackupDataOutputFile.length()).isEqualTo(0);
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
new file mode 100644
index 0000000..bfb2b14
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019 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.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
+import com.android.server.backup.testing.TestUtils;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.io.File;
+
+/**
+ * Tests verifying the interaction between {@link SetupObserver} and {@link
+ * UserBackupManagerService}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowApplicationPackageManager.class})
+@Presubmit
+public class SetupObserverTest {
+    private static final String TAG = "SetupObserverTest";
+    private static final int USER_ID = 10;
+
+    @Mock private TransportManager mTransportManager;
+
+    private Context mContext;
+    private UserBackupManagerService mUserBackupManagerService;
+    private HandlerThread mHandlerThread;
+
+    /** Setup state. */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mHandlerThread = BackupManagerServiceTestUtils.startSilentBackupThread(TAG);
+        mUserBackupManagerService =
+                BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
+                        USER_ID,
+                        mContext,
+                        mHandlerThread,
+                        new File(mContext.getDataDir(), "test1"),
+                        new File(mContext.getDataDir(), "test2"),
+                        mTransportManager);
+    }
+
+    /** Test observer handles changes from not setup -> setup correctly. */
+    @Test
+    public void testOnChange_whenNewlySetup_updatesState() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(true, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
+    }
+
+    /** Test observer handles changes from setup -> not setup correctly. */
+    @Test
+    public void testOnChange_whenPreviouslySetup_doesNotUpdateState() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(true);
+        changeSetupCompleteSettingForUser(false, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
+    }
+
+    /** Test observer handles changes from not setup -> not setup correctly. */
+    @Test
+    public void testOnChange_whenNotPreviouslySetup_doesNotUpdateStateIfNoChange()
+            throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(false, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isFalse();
+    }
+
+    /** Test observer handles changes from not setup -> setup correctly. */
+    @Test
+    public void testOnChange_whenNewlySetup_schedulesBackup() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(true, USER_ID);
+        // Setup conditions for a full backup job to be scheduled.
+        mUserBackupManagerService.setEnabled(true);
+        mUserBackupManagerService.enqueueFullBackup("testPackage", /* lastBackedUp */ 0);
+        // Clear the handler of all pending tasks. This is to prevent the below assertion on the
+        // handler from encountering false positives due to other tasks being scheduled as part of
+        // setup work.
+        TestUtils.runToEndOfTasks(mHandlerThread.getLooper());
+
+        setupObserver.onChange(true);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserBackupManagerService.getUserId())).isTrue();
+        // Verifies that the full backup job is scheduled. The job is scheduled via a posted message
+        // on the backup handler so we verify that a message exists.
+        assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
+    }
+
+    private void changeSetupCompleteSettingForUser(boolean value, int userId) {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                value ? 1 : 0,
+                userId);
+    }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 5e84ab0..4811523 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -115,6 +115,7 @@
 import com.android.server.backup.testing.TransportTestUtils;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
 import com.android.server.testing.shadows.FrameworkShadowLooper;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowBackupDataInput;
 import com.android.server.testing.shadows.ShadowBackupDataOutput;
 import com.android.server.testing.shadows.ShadowEventLog;
@@ -157,6 +158,7 @@
 @Config(
         shadows = {
             FrameworkShadowLooper.class,
+            ShadowApplicationPackageManager.class,
             ShadowBackupDataInput.class,
             ShadowBackupDataOutput.class,
             ShadowEventLog.class,
@@ -244,6 +246,7 @@
     @After
     public void tearDown() throws Exception {
         ShadowBackupDataInput.reset();
+        ShadowApplicationPackageManager.reset();
     }
 
     @Test
@@ -2432,10 +2435,12 @@
 
     private AgentMock setUpAgent(PackageData packageData) {
         try {
+            String packageName = packageData.packageName;
             mPackageManager.setApplicationEnabledSetting(
-                    packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+                    packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
             PackageInfo packageInfo = getPackageInfo(packageData);
-            mShadowPackageManager.addPackage(packageInfo);
+            mShadowPackageManager.installPackage(packageInfo);
+            ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
             mContext.sendBroadcast(getPackageAddedIntent(packageData));
             // Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
             mShadowBackupLooper.runToEndOfTasks();
@@ -2531,7 +2536,7 @@
 
     private PackageManagerBackupAgent createPmAgent() {
         PackageManagerBackupAgent pmAgent =
-                new PackageManagerBackupAgent(mApplication.getPackageManager());
+                new PackageManagerBackupAgent(mApplication.getPackageManager(), USER_ID);
         pmAgent.attach(mApplication);
         pmAgent.onCreate();
         return pmAgent;
@@ -2721,7 +2726,7 @@
             throws RemoteException, IOException {
         verify(transportMock.transport).requestBackupTime();
         assertBackupPendingFor(packages);
-        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mBackupManagerService.getUserId())).isTrue();
     }
 
     private void assertBackupPendingFor(PackageData... packages) throws IOException {
@@ -2836,7 +2841,7 @@
 
         ThrowingPackageManagerBackupAgent(
                 PackageManager packageManager, RuntimeException exception) {
-            super(packageManager);
+            super(packageManager, USER_ID);
             mException = exception;
         }
 
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 4009876..f17a9fe 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -57,6 +57,7 @@
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowEventLog;
 import com.android.server.testing.shadows.ShadowPerformUnifiedRestoreTask;
 
@@ -77,7 +78,13 @@
 import java.util.ArrayDeque;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class})
+@Config(
+        shadows = {
+            ShadowApplicationPackageManager.class,
+            ShadowBinder.class,
+            ShadowEventLog.class,
+            ShadowPerformUnifiedRestoreTask.class
+        })
 @Presubmit
 public class ActiveRestoreSessionTest {
     private static final String PACKAGE_1 = "com.example.package1";
@@ -140,6 +147,7 @@
 
     @After
     public void tearDown() throws Exception {
+        ShadowApplicationPackageManager.reset();
         ShadowPerformUnifiedRestoreTask.reset();
     }
 
@@ -561,7 +569,8 @@
         packageInfo.packageName = packageName;
         packageInfo.applicationInfo = new ApplicationInfo();
         packageInfo.applicationInfo.uid = uid;
-        mShadowPackageManager.addPackage(packageInfo);
+        mShadowPackageManager.installPackage(packageInfo);
+        ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
     }
 
     private IRestoreSession createActiveRestoreSession(
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
index 7dd0d92..f033af8 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -49,6 +50,7 @@
 
     @Mock private Context mContext;
     @Mock private TransportConnectionListener mTransportConnectionListener;
+    private @UserIdInt int mUserId;
     private TransportClientManager mTransportClientManager;
     private ComponentName mTransportComponent;
     private Intent mBindIntent;
@@ -57,7 +59,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mTransportClientManager = new TransportClientManager(mContext, new TransportStats());
+        mUserId = UserHandle.USER_SYSTEM;
+        mTransportClientManager =
+                new TransportClientManager(mUserId, mContext, new TransportStats());
         mTransportComponent = new ComponentName(PACKAGE_NAME, CLASS_NAME);
         mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
 
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
index 7281a3c..392f2ca 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
@@ -36,6 +36,7 @@
 import static org.testng.Assert.expectThrows;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -83,6 +84,7 @@
     @Mock private TransportConnectionListener mTransportConnectionListener;
     @Mock private TransportConnectionListener mTransportConnectionListener2;
     @Mock private IBackupTransport.Stub mTransportBinder;
+    @UserIdInt private int mUserId;
     private TransportStats mTransportStats;
     private TransportClient mTransportClient;
     private ComponentName mTransportComponent;
@@ -96,6 +98,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mUserId = UserHandle.USER_SYSTEM;
         Looper mainLooper = Looper.getMainLooper();
         mShadowMainLooper = extract(mainLooper);
         mTransportComponent =
@@ -105,6 +108,7 @@
         mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
         mTransportClient =
                 new TransportClient(
+                        mUserId,
                         mContext,
                         mTransportStats,
                         mBindIntent,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
index 5fffb14..aefc871 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
@@ -54,7 +54,10 @@
 
     @Implementation
     protected static boolean appIsRunningAndEligibleForBackupWithTransport(
-            @Nullable TransportClient transportClient, String packageName, PackageManager pm) {
+            @Nullable TransportClient transportClient,
+            String packageName,
+            PackageManager pm,
+            int userId) {
         return sAppsRunningAndEligibleForBackupWithTransport.contains(packageName);
     }
 
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
new file mode 100644
index 0000000..ab121ed
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 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.testing.shadows;
+
+import static android.content.pm.PackageManager.NameNotFoundException;
+
+import android.app.ApplicationPackageManager;
+import android.content.pm.PackageInfo;
+import android.util.ArrayMap;
+
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
+ * package in user-specific invocations.
+ */
+@Implements(value = ApplicationPackageManager.class)
+public class ShadowApplicationPackageManager
+        extends org.robolectric.shadows.ShadowApplicationPackageManager {
+    private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>();
+    private static final List<PackageInfo> sInstalledPackages = new ArrayList<>();
+    private static final Map<String, Integer> sPackageUids = new ArrayMap<>();
+
+    /**
+     * Registers the package {@code packageName} to be returned when invoking {@link
+     * ApplicationPackageManager#getPackageInfoAsUser(String, int, int)} and {@link
+     * ApplicationPackageManager#getInstalledPackagesAsUser(int, int)}.
+     */
+    public static void addInstalledPackage(String packageName, PackageInfo packageInfo) {
+        sPackageInfos.put(packageName, packageInfo);
+        sInstalledPackages.add(packageInfo);
+    }
+
+    /**
+     * Sets the package uid {@code packageUid} for the package {@code packageName} to be returned
+     * when invoking {@link ApplicationPackageManager#getPackageUidAsUser(String, int, int)}.
+     */
+    public static void setPackageUid(String packageName, int packageUid) {
+        sPackageUids.put(packageName, packageUid);
+    }
+
+    @Override
+    protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+            throws NameNotFoundException {
+        if (!sPackageInfos.containsKey(packageName)) {
+            throw new NameNotFoundException(packageName);
+        }
+        return sPackageInfos.get(packageName);
+    }
+
+    @Override
+    protected List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+        return sInstalledPackages;
+    }
+
+    @Override
+    protected int getPackageUidAsUser(String packageName, int flags, int userId)
+            throws NameNotFoundException {
+        if (!sPackageUids.containsKey(packageName)) {
+            throw new NameNotFoundException(packageName);
+        }
+        return sPackageUids.get(packageName);
+    }
+
+    /** Clear package state. */
+    @Resetter
+    public static void reset() {
+        sPackageInfos.clear();
+        sInstalledPackages.clear();
+        org.robolectric.shadows.ShadowApplicationPackageManager.reset();
+    }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
index 23c44b0..f90ea6a 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
@@ -34,7 +34,8 @@
     }
 
     @Implementation
-    protected static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+    protected static void schedule(int userId, Context ctx, long delay,
+            BackupManagerConstants constants) {
         callingUid = Binder.getCallingUid();
     }
 }
diff --git a/services/startop/Android.bp b/services/startop/Android.bp
new file mode 100644
index 0000000..093b4ec
--- /dev/null
+++ b/services/startop/Android.bp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+java_library_static {
+    name: "services.startop",
+
+    static_libs: [
+        // frameworks/base/startop/iorap
+        "services.startop.iorap",
+    ],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 148faad..6a153d5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -60,6 +60,7 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
@@ -81,6 +82,7 @@
 
 import java.util.ArrayList;
 
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class AlarmManagerServiceTest {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index f1cd0cd..57ee6dc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -43,7 +44,12 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
 import android.app.job.JobInfo;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -56,13 +62,16 @@
 import android.os.BatteryManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.SparseBooleanArray;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.JobStore;
 import com.android.server.job.controllers.QuotaController.ExecutionStats;
 import com.android.server.job.controllers.QuotaController.TimingSession;
 
@@ -96,9 +105,13 @@
     private BroadcastReceiver mChargingReceiver;
     private Constants mConstants;
     private QuotaController mQuotaController;
+    private int mSourceUid;
+    private IUidObserver mUidObserver;
 
     private MockitoSession mMockingSession;
     @Mock
+    private ActivityManagerInternal mActivityMangerInternal;
+    @Mock
     private AlarmManager mAlarmManager;
     @Mock
     private Context mContext;
@@ -107,6 +120,8 @@
     @Mock
     private UsageStatsManagerInternal mUsageStatsManager;
 
+    private JobStore mJobStore;
+
     @Before
     public void setUp() {
         mMockingSession = mockitoSession()
@@ -123,8 +138,17 @@
         when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
         when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
         // Called in QuotaController constructor.
+        IActivityManager activityManager = ActivityManager.getService();
+        spyOn(activityManager);
+        try {
+            doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
         when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
         when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+        doReturn(mActivityMangerInternal)
+                .when(() -> LocalServices.getService(ActivityManagerInternal.class));
         doReturn(mock(BatteryManagerInternal.class))
                 .when(() -> LocalServices.getService(BatteryManagerInternal.class));
         doReturn(mUsageStatsManager)
@@ -132,6 +156,9 @@
         // Used in JobStatus.
         doReturn(mock(PackageManagerInternal.class))
                 .when(() -> LocalServices.getService(PackageManagerInternal.class));
+        // Used in QuotaController.Handler.
+        mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir());
+        when(mJobSchedulerService.getJobStore()).thenReturn(mJobStore);
 
         // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
         // in the past, and QuotaController sometimes floors values at 0, so if the test time
@@ -150,10 +177,23 @@
         // Capture the listeners.
         ArgumentCaptor<BroadcastReceiver> receiverCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
+        ArgumentCaptor<IUidObserver> uidObserverCaptor =
+                ArgumentCaptor.forClass(IUidObserver.class);
         mQuotaController = new QuotaController(mJobSchedulerService);
 
         verify(mContext).registerReceiver(receiverCaptor.capture(), any());
         mChargingReceiver = receiverCaptor.getValue();
+        try {
+            verify(activityManager).registerUidObserver(
+                    uidObserverCaptor.capture(),
+                    eq(ActivityManager.UID_OBSERVER_PROCSTATE),
+                    eq(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE),
+                    any());
+            mUidObserver = uidObserverCaptor.getValue();
+            mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
     }
 
     @After
@@ -182,6 +222,25 @@
         mChargingReceiver.onReceive(mContext, intent);
     }
 
+    private void setProcessState(int procState) {
+        try {
+            doReturn(procState).when(mActivityMangerInternal).getUidProcessState(mSourceUid);
+            SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids();
+            spyOn(foregroundUids);
+            mUidObserver.onUidStateChanged(mSourceUid, procState, 0);
+            if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1))
+                        .put(eq(mSourceUid), eq(true));
+                assertTrue(foregroundUids.get(mSourceUid));
+            } else {
+                verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1)).delete(eq(mSourceUid));
+                assertFalse(foregroundUids.get(mSourceUid));
+            }
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
+    }
+
     private void setStandbyBucket(int bucketIndex) {
         int bucket;
         switch (bucketIndex) {
@@ -204,9 +263,18 @@
                 anyLong())).thenReturn(bucket);
     }
 
-    private void setStandbyBucket(int bucketIndex, JobStatus job) {
+    private void setStandbyBucket(int bucketIndex, JobStatus... jobs) {
         setStandbyBucket(bucketIndex);
-        job.setStandbyBucket(bucketIndex);
+        for (JobStatus job : jobs) {
+            job.setStandbyBucket(bucketIndex);
+        }
+    }
+
+    private void trackJobs(JobStatus... jobs) {
+        for (JobStatus job : jobs) {
+            mJobStore.add(job);
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+        }
     }
 
     private JobStatus createJobStatus(String testTag, int jobId) {
@@ -214,8 +282,11 @@
                 new ComponentName(mContext, "TestQuotaJobService"))
                 .setMinimumLatency(Math.abs(jobId) + 1)
                 .build();
-        return JobStatus.createFromJobInfo(
+        JobStatus js = JobStatus.createFromJobInfo(
                 jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+        // Make sure tests aren't passing just because the default bucket is likely ACTIVE.
+        js.setStandbyBucket(FREQUENT_INDEX);
+        return js;
     }
 
     private TimingSession createTimingSession(long start, long duration, int count) {
@@ -709,6 +780,7 @@
         verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
 
         JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1);
+        setStandbyBucket(standbyBucket, jobStatus);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
         mQuotaController.prepareForExecutionLocked(jobStatus);
         advanceElapsedClock(5 * MINUTE_IN_MILLIS);
@@ -1339,19 +1411,23 @@
         setDischarging();
 
         JobStatus jobStatus = createJobStatus("testTimerTracking_AllForeground", 1);
-        jobStatus.uidActive = true;
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
 
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
         mQuotaController.prepareForExecutionLocked(jobStatus);
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        // Change to a state that should still be considered foreground.
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
     /**
-     * Tests that Timers properly track overlapping foreground and background jobs.
+     * Tests that Timers properly track sessions when switching between foreground and background
+     * states.
      */
     @Test
     public void testTimerTracking_ForegroundAndBackground() {
@@ -1360,7 +1436,6 @@
         JobStatus jobBg1 = createJobStatus("testTimerTracking_ForegroundAndBackground", 1);
         JobStatus jobBg2 = createJobStatus("testTimerTracking_ForegroundAndBackground", 2);
         JobStatus jobFg3 = createJobStatus("testTimerTracking_ForegroundAndBackground", 3);
-        jobFg3.uidActive = true;
         mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
         mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
         mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
@@ -1368,6 +1443,7 @@
         List<TimingSession> expected = new ArrayList<>();
 
         // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         long start = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.prepareForExecutionLocked(jobBg1);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
@@ -1379,48 +1455,223 @@
 
         // Bg job starts while inactive, spans an entire active session, and ends after the
         // active session.
-        // Fg job starts after the bg job and ends before the bg job.
-        // Entire bg job duration should be counted since it started before active session. However,
-        // count should only be 1 since Timer shouldn't count fg jobs.
+        // App switching to foreground state then fg job starts.
+        // App remains in foreground state after coming to foreground, so there should only be one
+        // session.
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
         mQuotaController.prepareForExecutionLocked(jobBg2);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         mQuotaController.prepareForExecutionLocked(jobFg3);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
-        expected.add(createTimingSession(start, 30 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
         advanceElapsedClock(SECOND_IN_MILLIS);
 
         // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes
         // "inactive" and then bg job 2 starts. Then fg job ends.
-        // This should result in two TimingSessions with a count of one each.
+        // This should result in two TimingSessions:
+        //  * The first should have a count of 1
+        //  * The second should have a count of 2 since it will include both jobs
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
         mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
         mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+        setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
         mQuotaController.prepareForExecutionLocked(jobBg1);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         mQuotaController.prepareForExecutionLocked(jobFg3);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
-        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
         advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
         mQuotaController.prepareForExecutionLocked(jobBg2);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
-        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
     /**
+     * Tests that Timers properly track overlapping top and background jobs.
+     */
+    @Test
+    public void testTimerTracking_TopAndNonTop() {
+        setDischarging();
+
+        JobStatus jobBg1 = createJobStatus("testTimerTracking_TopAndNonTop", 1);
+        JobStatus jobBg2 = createJobStatus("testTimerTracking_TopAndNonTop", 2);
+        JobStatus jobFg1 = createJobStatus("testTimerTracking_TopAndNonTop", 3);
+        JobStatus jobTop = createJobStatus("testTimerTracking_TopAndNonTop", 4);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+        List<TimingSession> expected = new ArrayList<>();
+
+        // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.prepareForExecutionLocked(jobBg1);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+
+        // Bg job starts while inactive, spans an entire active session, and ends after the
+        // active session.
+        // App switching to top state then fg job starts.
+        // App remains in top state after coming to top, so there should only be one
+        // session.
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+        mQuotaController.prepareForExecutionLocked(jobBg2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+
+        // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to
+        // foreground_service and a new job starts. Shortly after, uid goes
+        // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs.
+        // This should result in two TimingSessions:
+        //  * The first should have a count of 1
+        //  * The second should have a count of 2, which accounts for the bg2 and fg, but not top
+        //    jobs.
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+        setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
+        mQuotaController.prepareForExecutionLocked(jobBg1);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mQuotaController.prepareForExecutionLocked(jobFg1);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        mQuotaController.prepareForExecutionLocked(jobBg2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+        mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
+    /**
+     * Tests that TOP jobs aren't stopped when an app runs out of quota.
+     */
+    @Test
+    public void testTracking_OutOfQuota_ForegroundAndBackground() {
+        setDischarging();
+
+        JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1);
+        JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2);
+        trackJobs(jobBg, jobTop);
+        setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window
+        // Now the package only has 20 seconds to run.
+        final long remainingTimeMs = 20 * SECOND_IN_MILLIS;
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+                        10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+
+        InOrder inOrder = inOrder(mJobSchedulerService);
+
+        // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        // Start the job.
+        mQuotaController.prepareForExecutionLocked(jobBg);
+        advanceElapsedClock(remainingTimeMs / 2);
+        // New job starts after UID is in the foreground. Since the app is now in the foreground, it
+        // should continue to have remainingTimeMs / 2 time remaining.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        advanceElapsedClock(remainingTimeMs);
+
+        // Wait for some extra time to allow for job processing.
+        inOrder.verify(mJobSchedulerService,
+                timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+                .onControllerStateChanged();
+        assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobBg));
+        assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobTop));
+        // Go to a background state.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        advanceElapsedClock(remainingTimeMs / 2 + 1);
+        inOrder.verify(mJobSchedulerService,
+                timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged();
+        // Top job should still be allowed to run.
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        // New jobs to run.
+        JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3);
+        JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4);
+        JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5);
+        setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg);
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged();
+        trackJobs(jobFg, jobTop);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        // App still in foreground so everything should be in quota.
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged();
+        // App is now in background and out of quota. Fg should now change to out of quota since it
+        // wasn't started. Top should remain in quota since it started when the app was in TOP.
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        trackJobs(jobBg2);
+        assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+    }
+
+    /**
      * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches
      * its quota.
      */
diff --git a/services/tests/rescueparty/Android.bp b/services/tests/rescueparty/Android.bp
new file mode 100644
index 0000000..6733af4
--- /dev/null
+++ b/services/tests/rescueparty/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2008 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.
+
+cc_test {
+    name: "log_rescueparty_reset_event_reported",
+    srcs: ["log_rescueparty_reset_event_reported.cpp"],
+    shared_libs: [
+        "libbase",
+        "libstatslog",
+    ],
+    gtest: false,
+}
diff --git a/services/tests/rescueparty/how_to_run.txt b/services/tests/rescueparty/how_to_run.txt
new file mode 100644
index 0000000..9528d39
--- /dev/null
+++ b/services/tests/rescueparty/how_to_run.txt
@@ -0,0 +1,9 @@
+# Per http://go/westworld-local-development#step3-test-atom-and-metric-locally-on-device ,
+# In one terminal:
+make statsd_testdrive
+./out/host/linux-x86/bin/statsd_testdrive 122
+
+# In another terminal:
+mma -j $(nproc) log_rescueparty_reset_event_reported
+adb push $OUT/testcases/log_rescueparty_reset_event_reported/arm64/log_rescueparty_reset_event_reported /data
+adb shell /data/log_rescueparty_reset_event_reported 1234
diff --git a/services/tests/rescueparty/log_rescueparty_reset_event_reported.cpp b/services/tests/rescueparty/log_rescueparty_reset_event_reported.cpp
new file mode 100644
index 0000000..4aea917
--- /dev/null
+++ b/services/tests/rescueparty/log_rescueparty_reset_event_reported.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2008 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 <statslog.h>
+#include <cstdlib>
+
+int main(int argc, const char** argv) {
+    int level = 0;
+    if (argc > 1) {
+        level = std::atoi(argv[1]);
+    }
+    android::util::stats_write(android::util::RESCUE_PARTY_RESET_REPORTED, level);
+}
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 e4fe599..00a60b9 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -117,7 +117,7 @@
         thread2.assertWaiting("Unexpected state for " + record2);
         thread2.interrupt();
 
-        mAms.mActiveUids.clear();
+        mAms.mProcessList.mActiveUids.clear();
     }
 
     private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
@@ -126,7 +126,7 @@
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         record.curProcStateSeq = curProcStateSeq;
         record.waitingForNetwork = true;
-        mAms.mActiveUids.put(uid, record);
+        mAms.mProcessList.mActiveUids.put(uid, record);
         return record;
     }
 
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 767eb60..419c736 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -29,6 +29,8 @@
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.util.DebugUtils.valueToString;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
 import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG;
 import static com.android.server.am.ActivityManagerService.Injector;
@@ -68,7 +70,10 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
-import com.android.server.AppOpsService;
+import com.android.server.appop.AppOpsService;
+import com.android.server.am.ProcessList.IsolatedUidRange;
+import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
+import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -109,7 +114,7 @@
         UidRecord.CHANGE_ACTIVE
     };
 
-    @Mock private Context mContext;
+    private Context mContext = getInstrumentation().getTargetContext();
     @Mock private AppOpsService mAppOpsService;
     @Mock private PackageManager mPackageManager;
 
@@ -128,8 +133,8 @@
         mInjector = new TestInjector();
         mAms = new ActivityManagerService(mInjector);
         mAms.mWaitForNetworkTimeoutMs = 2000;
-
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper());
     }
 
     @After
@@ -248,7 +253,7 @@
         final UidRecord uidRec = new UidRecord(uid, null /* atmInternal */);
         uidRec.waitingForNetwork = true;
         uidRec.hasInternetPermission = true;
-        mAms.mActiveUids.put(uid, uidRec);
+        mAms.mProcessList.mActiveUids.put(uid, uidRec);
 
         final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid);
         appRec.thread = Mockito.mock(IApplicationThread.class);
@@ -291,6 +296,113 @@
         }
     }
 
+    private void validateAppZygoteIsolatedUidRange(IsolatedUidRange uidRange) {
+        assertNotNull(uidRange);
+        assertTrue(uidRange.mFirstUid >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && uidRange.mFirstUid <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
+        assertTrue(uidRange.mLastUid >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && uidRange.mLastUid <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
+        assertTrue(uidRange.mLastUid > uidRange.mFirstUid
+                && ((uidRange.mLastUid - uidRange.mFirstUid + 1)
+                     == Process.NUM_UIDS_PER_APP_ZYGOTE));
+    }
+
+    private void verifyUidRangesNoOverlap(IsolatedUidRange uidRange1, IsolatedUidRange uidRange2) {
+        IsolatedUidRange lowRange = uidRange1.mFirstUid <= uidRange2.mFirstUid ? uidRange1 : uidRange2;
+        IsolatedUidRange highRange = lowRange == uidRange1  ? uidRange2 : uidRange1;
+
+        assertTrue(highRange.mFirstUid > lowRange.mLastUid);
+    }
+
+    @Test
+    public void testIsolatedUidRangeAllocator() {
+        final IsolatedUidRangeAllocator allocator = mAms.mProcessList.mAppIsolatedUidRangeAllocator;
+
+        // Create initial range
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.processName = "com.android.test.app";
+        appInfo.uid = 10000;
+        final IsolatedUidRange range = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+        validateAppZygoteIsolatedUidRange(range);
+        verifyIsolatedUidAllocator(range);
+
+        // Create a second range
+        ApplicationInfo appInfo2 = new ApplicationInfo();
+        appInfo2.processName = "com.android.test.app2";
+        appInfo2.uid = 10001;
+        IsolatedUidRange range2 = allocator.getOrCreateIsolatedUidRangeLocked(appInfo2);
+        validateAppZygoteIsolatedUidRange(range2);
+        verifyIsolatedUidAllocator(range2);
+
+        // Verify ranges don't overlap
+        verifyUidRangesNoOverlap(range, range2);
+
+        // Free range, reallocate and verify
+        allocator.freeUidRangeLocked(appInfo2);
+        range2 = allocator.getOrCreateIsolatedUidRangeLocked(appInfo2);
+        validateAppZygoteIsolatedUidRange(range2);
+        verifyUidRangesNoOverlap(range, range2);
+        verifyIsolatedUidAllocator(range2);
+
+        // Free both, then try to allocate the maximum number of UID ranges
+        allocator.freeUidRangeLocked(appInfo);
+        allocator.freeUidRangeLocked(appInfo2);
+
+        int maxNumUidRanges = (Process.LAST_APP_ZYGOTE_ISOLATED_UID
+                - Process.FIRST_APP_ZYGOTE_ISOLATED_UID + 1) / Process.NUM_UIDS_PER_APP_ZYGOTE;
+        for (int i = 0; i < maxNumUidRanges; i++) {
+            appInfo = new ApplicationInfo();
+            appInfo.uid = 10000 + i;
+            appInfo.processName = "com.android.test.app" + Integer.toString(i);
+            IsolatedUidRange uidRange = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+            validateAppZygoteIsolatedUidRange(uidRange);
+            verifyIsolatedUidAllocator(uidRange);
+        }
+
+        // Try to allocate another one and make sure it fails
+        appInfo = new ApplicationInfo();
+        appInfo.uid = 9000;
+        appInfo.processName = "com.android.test.app.failed";
+        IsolatedUidRange failedRange = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+
+        assertNull(failedRange);
+    }
+
+    public void verifyIsolatedUid(ProcessList.IsolatedUidRange range, int uid) {
+        assertTrue(uid >= range.mFirstUid && uid <= range.mLastUid);
+    }
+
+    public void verifyIsolatedUidAllocator(ProcessList.IsolatedUidRange range) {
+        int uid = range.allocateIsolatedUidLocked(0);
+        verifyIsolatedUid(range, uid);
+
+        int uid2 = range.allocateIsolatedUidLocked(0);
+        verifyIsolatedUid(range, uid2);
+        assertTrue(uid2 != uid);
+
+        // Free both
+        range.freeIsolatedUidLocked(uid);
+        range.freeIsolatedUidLocked(uid2);
+
+        // Allocate the entire range
+        for (int i = 0; i < (range.mLastUid - range.mFirstUid + 1); ++i) {
+            uid = range.allocateIsolatedUidLocked(0);
+            verifyIsolatedUid(range, uid);
+        }
+
+        // Ensure the next one fails
+        uid = range.allocateIsolatedUidLocked(0);
+        assertEquals(uid, -1);
+    }
+
+    @Test
+    public void testGlobalIsolatedUidAllocator() {
+        final IsolatedUidRange globalUidRange = mAms.mProcessList.mGlobalIsolatedUids;
+        assertEquals(globalUidRange.mFirstUid, Process.FIRST_ISOLATED_UID);
+        assertEquals(globalUidRange.mLastUid, Process.LAST_ISOLATED_UID);
+        verifyIsolatedUidAllocator(globalUidRange);
+    }
+
     @Test
     public void testBlockStateForUid() {
         final UidRecord uidRec = new UidRecord(TEST_UID, null /* atmInternal */);
@@ -603,7 +715,8 @@
 
         // Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it
         // will be removed from validateUids.
-        assertNotEquals("validateUids should not be empty", 0, mAms.mValidateUids.size());
+        assertNotEquals("validateUids should not be empty", 0,
+                mAms.mValidateUids.size());
         for (int i = 0; i < pendingItemsForUids.size(); ++i) {
             final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
             // Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd
@@ -741,7 +854,7 @@
         record.curProcStateSeq = curProcStateSeq;
         record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
-        mAms.mActiveUids.put(Process.myUid(), record);
+        mAms.mProcessList.mActiveUids.put(Process.myUid(), record);
 
         CustomThread thread = new CustomThread(record.networkStateLock, new Runnable() {
             @Override
@@ -764,7 +877,7 @@
             thread.assertTerminated(errMsg);
         }
 
-        mAms.mActiveUids.clear();
+        mAms.mProcessList.mActiveUids.clear();
     }
 
     private static class TestHandler extends Handler {
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 05cb48b..1dfce51 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -25,7 +25,8 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
-import com.android.server.AppOpsService;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,13 +63,15 @@
                 return false;
             }
         });
+        mService.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
     }
 
     @Test
     @UiThreadTest
     public void testCreateWorks() {
         AppErrorDialog.Data data = new AppErrorDialog.Data();
-        data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
+        data.proc = new ProcessRecord(mService, mContext.getApplicationInfo(), "name", 12345);
         data.result = new AppErrorResult();
 
         AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
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 9626990..cbdc6c3 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -34,7 +34,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.AppOpsService;
+import com.android.server.appop.AppOpsService;
 
 import org.junit.AfterClass;
 import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java
rename to services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
index 7e0dfcf..65c5781 100644
--- a/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.appops;
+package com.android.server.appop;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
rename to services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsServiceTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/appops/AppOpsServiceTest.java
rename to services/tests/servicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 4ae9bea..36f84d0 100644
--- a/services/tests/servicestests/src/com/android/server/appops/AppOpsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server;
+package com.android.server.appop;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_ERRORED;
@@ -36,6 +36,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.appop.AppOpsService;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/services/tests/servicestests/src/com/android/server/AppOpsUpgradeTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/AppOpsUpgradeTest.java
rename to services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index aac96a1..eb0c627 100644
--- a/services/tests/servicestests/src/com/android/server/AppOpsUpgradeTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.appop;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
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 db83505..6a9a121 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -1149,31 +1149,31 @@
 
     @Test
     public void beginFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.beginFullBackup(new FullBackupJob());
+        mTrampoline.beginFullBackup(mUserId, new FullBackupJob());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void beginFullBackup_forwarded() throws RemoteException {
         FullBackupJob fullBackupJob = new FullBackupJob();
-        when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
+        when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
 
         mTrampoline.initializeService();
-        assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
-        verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
+        assertTrue(mTrampoline.beginFullBackup(mUserId, fullBackupJob));
+        verify(mBackupManagerServiceMock).beginFullBackup(mUserId, fullBackupJob);
     }
 
     @Test
     public void endFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.endFullBackup();
+        mTrampoline.endFullBackup(mUserId);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void endFullBackup_forwarded() throws RemoteException {
         mTrampoline.initializeService();
-        mTrampoline.endFullBackup();
-        verify(mBackupManagerServiceMock).endFullBackup();
+        mTrampoline.endFullBackup(mUserId);
+        verify(mBackupManagerServiceMock).endFullBackup(mUserId);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
index 34edd9f..757a046 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -32,7 +32,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
-import android.os.RemoteException;
 import android.test.AndroidTestCase;
 import android.test.mock.MockPackageManager;
 import android.view.inputmethod.InputMethodInfo;
@@ -40,7 +39,6 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.R;
-import com.android.internal.view.IInputMethodManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,8 +59,8 @@
 
     private @Mock
     Resources mResources;
-    private @Mock
-    IInputMethodManager mIInputMethodManager;
+    @Mock
+    private OverlayPackagesProvider.Injector mInjector;
     private @Mock
     Context mTestContext;
     private Resources mRealResources;
@@ -81,6 +79,7 @@
                 InstrumentationRegistry.getTargetContext().getCacheDir());
 
         setSystemInputMethods();
+        setIsPerProfileModeEnabled(false);
         setRequiredAppsManagedDevice();
         setVendorRequiredAppsManagedDevice();
         setDisallowedAppsManagedDevice();
@@ -95,7 +94,7 @@
         setVendorDisallowedAppsManagedUser();
 
         mRealResources = InstrumentationRegistry.getTargetContext().getResources();
-        mHelper = new OverlayPackagesProvider(mTestContext, mIInputMethodManager);
+        mHelper = new OverlayPackagesProvider(mTestContext, mInjector);
     }
 
     @Test
@@ -165,6 +164,15 @@
     }
 
     @Test
+    public void testProfileOwnerImesAreRequiredForPerProfileImeMode() {
+        setSystemAppsWithLauncher("app.a", "app.b");
+        setSystemInputMethods("app.a");
+        setIsPerProfileModeEnabled(true);
+
+        verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, "app.b");
+    }
+
+    @Test
     public void testManagedUserImesAreRequired() {
         setSystemAppsWithLauncher("app.a", "app.b");
         setSystemInputMethods("app.a");
@@ -333,11 +341,11 @@
             InputMethodInfo inputMethodInfo = new InputMethodInfo(ri, false, null, null, 0, false);
             inputMethods.add(inputMethodInfo);
         }
-        try {
-            when(mIInputMethodManager.getInputMethodList()).thenReturn(inputMethods);
-        } catch (RemoteException e) {
-            fail(e.toString());
-        }
+        when(mInjector.getInputMethodListAsUser(eq(TEST_USER_ID))).thenReturn(inputMethods);
+    }
+
+    private void setIsPerProfileModeEnabled(boolean enabled) {
+        when(mInjector.isPerProfileImeEnabled()).thenReturn(enabled);
     }
 
     private void setSystemAppsWithLauncher(String... apps) {
diff --git a/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.java
new file mode 100644
index 0000000..e518844
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.display;
+
+import static com.android.server.display.AppSaturationController.TRANSLATION_VECTOR;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.os.UserHandle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.ColorDisplayService.ColorTransformController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.ref.WeakReference;
+
+@RunWith(AndroidJUnit4.class)
+public class AppSaturationControllerTest {
+
+    private static final String TEST_PACKAGE_NAME = "com.android.test";
+
+    private int mUserId;
+    private AppSaturationController mAppSaturationController;
+    private float[] mMatrix;
+
+    @Mock
+    private ColorTransformController mColorTransformController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mUserId = ActivityManager.getCurrentUser();
+        mAppSaturationController = new AppSaturationController();
+        mMatrix = new float[9];
+    }
+
+    @After
+    public void tearDown() {
+        mAppSaturationController = null;
+        mUserId = UserHandle.USER_NULL;
+    }
+
+    @Test
+    public void addColorTransformController_appliesExistingSaturation() {
+        final WeakReference<ColorTransformController> ref = new WeakReference<>(
+                mColorTransformController);
+        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+        mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+        AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
+        verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+    }
+
+    @Test
+    public void setSaturationLevel_resetToDefault() {
+        final WeakReference<ColorTransformController> ref = new WeakReference<>(
+                mColorTransformController);
+        mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+        verify(mColorTransformController, never())
+                .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+        AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
+        verify(mColorTransformController, times(1))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+        AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
+        verify(mColorTransformController, times(2))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+    }
+
+    @Test
+    public void setSaturationLevel_updateLevel() {
+        final WeakReference<ColorTransformController> ref = new WeakReference<>(
+                mColorTransformController);
+        mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+        verify(mColorTransformController, never())
+                .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+        AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
+        verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 70);
+        AppSaturationController.computeGrayscaleTransformMatrix(.7f, mMatrix);
+        verify(mColorTransformController, times(2))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+        AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
+        verify(mColorTransformController, times(3))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index abf9040..4742a73 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -389,7 +389,7 @@
         if (attr == null) return; //sampling not supported on device, skip remainder of test.
 
         boolean enabled = displayManager.setDisplayedContentSamplingEnabledInternal(0, true, 0, 0);
-        assertTrue(!enabled);
+        assertTrue(enabled);
 
         displayManager.setDisplayedContentSamplingEnabledInternal(0, false, 0, 0);
         DisplayedContentSample sample = displayManager.getDisplayedContentSampleInternal(0, 0, 0);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
new file mode 100644
index 0000000..eb9b98e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+import android.app.Instrumentation;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+
+/** Tests for {@link ArcInitiationActionFromAvrTest} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class ArcInitiationActionFromAvrTest {
+
+    private HdmiDeviceInfo mDeviceInfoForTests;
+    private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+    private HdmiCecController mHdmiCecController;
+    private HdmiControlService mHdmiControlService;
+    private FakeNativeWrapper mNativeWrapper;
+    private ArcInitiationActionFromAvr mAction;
+
+    private TestLooper mTestLooper = new TestLooper();
+    private boolean mSendCecCommandSuccess;
+    private boolean mShouldDispatchARCInitiated;
+    private boolean mArcInitSent;
+    private boolean mRequestActiveSourceSent;
+    private Instrumentation mInstrumentation;
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+        mHdmiControlService =
+                new HdmiControlService(mInstrumentation.getTargetContext()) {
+                    @Override
+                    void sendCecCommand(
+                            HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+                        switch (command.getOpcode()) {
+                            case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
+                                if (callback != null) {
+                                    callback.onSendCompleted(
+                                            mSendCecCommandSuccess
+                                                    ? SendMessageResult.SUCCESS
+                                                    : SendMessageResult.NACK);
+                                }
+                                mRequestActiveSourceSent = true;
+                                break;
+                            case Constants.MESSAGE_INITIATE_ARC:
+                                if (callback != null) {
+                                    callback.onSendCompleted(
+                                            mSendCecCommandSuccess
+                                                    ? SendMessageResult.SUCCESS
+                                                    : SendMessageResult.NACK);
+                                }
+                                mArcInitSent = true;
+                                if (mShouldDispatchARCInitiated) {
+                                    mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+                                            HdmiCecMessageBuilder.buildReportArcInitiated(
+                                                    Constants.ADDR_TV,
+                                                    Constants.ADDR_AUDIO_SYSTEM));
+                                }
+                                break;
+                            default:
+                        }
+                    }
+
+                    @Override
+                    boolean isPowerStandby() {
+                        return false;
+                    }
+
+                    @Override
+                    boolean isAddressAllocated() {
+                        return true;
+                    }
+
+                    @Override
+                    Looper getServiceLooper() {
+                        return mTestLooper.getLooper();
+                    }
+                };
+
+        mHdmiCecLocalDeviceAudioSystem =
+                new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+                    @Override
+                    HdmiDeviceInfo getDeviceInfo() {
+                        return mDeviceInfoForTests;
+                    }
+
+                    @Override
+                    void setArcStatus(boolean enabled) {
+                        // do nothing
+                    }
+
+                    @Override
+                    protected boolean isSystemAudioActivated() {
+                        return true;
+                    }
+                };
+
+        mHdmiCecLocalDeviceAudioSystem.init();
+        Looper looper = mTestLooper.getLooper();
+        mHdmiControlService.setIoLooper(looper);
+        mNativeWrapper = new FakeNativeWrapper();
+        mHdmiCecController =
+                HdmiCecController.createWithNativeWrapper(this.mHdmiControlService, mNativeWrapper);
+        mHdmiControlService.setCecController(mHdmiCecController);
+        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+        mHdmiControlService.initPortInfo();
+        mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
+
+        mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+    }
+
+    @Ignore("b/120845532")
+    @Test
+    public void arcInitiation_requestActiveSource() {
+        mSendCecCommandSuccess = true;
+        mShouldDispatchARCInitiated = true;
+        mRequestActiveSourceSent = false;
+        mArcInitSent = false;
+
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        assertThat(mArcInitSent).isTrue();
+        assertThat(mRequestActiveSourceSent).isTrue();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 7484edd..8607ec6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -18,8 +18,12 @@
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.MessageQueue;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.hdmi.HdmiCecController.NativeWrapper;
+
+import com.google.common.collect.Iterables;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -45,8 +49,8 @@
             };
 
     private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
-    private HdmiCecMessage mResultMessage;
     private int mMyPhysicalAddress = 0;
+    private HdmiPortInfo[] mHdmiPortInfo = null;
 
     @Override
     public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
@@ -89,9 +93,11 @@
 
     @Override
     public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
-        HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[1];
-        hdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
-        return hdmiPortInfo;
+        if (mHdmiPortInfo == null) {
+            mHdmiPortInfo = new HdmiPortInfo[1];
+            mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+        }
+        return mHdmiPortInfo;
     }
 
     @Override
@@ -112,11 +118,12 @@
         return new ArrayList<>(mResultMessages);
     }
 
-    public HdmiCecMessage getOnlyResultMessage() throws Exception {
-        if (mResultMessages.size() != 1) {
-            throw new Exception("There is not exactly one message");
-        }
-        return mResultMessages.get(0);
+    public HdmiCecMessage getOnlyResultMessage() throws IllegalArgumentException {
+        return Iterables.getOnlyElement(mResultMessages);
+    }
+
+    public void clearResultMessages() {
+        mResultMessages.clear();
     }
 
     public void setPollAddressResponse(int logicalAddress, int response) {
@@ -127,4 +134,10 @@
     protected void setPhysicalAddress(int physicalAddress) {
         mMyPhysicalAddress = physicalAddress;
     }
+
+    @VisibleForTesting
+    protected void setPortInfo(HdmiPortInfo[] hdmiPortInfo) {
+        mHdmiPortInfo = new HdmiPortInfo[hdmiPortInfo.length];
+        System.arraycopy(hdmiPortInfo, 0, mHdmiPortInfo, 0, hdmiPortInfo.length);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index da840be..5d8131f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -18,6 +18,7 @@
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
+
 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
@@ -25,15 +26,19 @@
 import static com.android.server.hdmi.Constants.ADDR_SPECIFIC_USE;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
+
 import static junit.framework.Assert.assertEquals;
 
 import android.content.Context;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.Looper;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 9e3a0ea..3a6cdc2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -15,23 +15,29 @@
  */
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE;
+
 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
+import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
+
 import static com.google.common.truth.Truth.assertThat;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
 import android.media.AudioManager;
 import android.os.Looper;
-import android.os.SystemProperties;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 
 import org.junit.Before;
@@ -41,7 +47,6 @@
 import org.junit.runners.JUnit4;
 
 import java.util.ArrayList;
-
 @SmallTest
 @RunWith(JUnit4.class)
 /** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
@@ -54,6 +59,7 @@
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
     private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+    private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
     private FakeNativeWrapper mNativeWrapper;
     private Looper mMyLooper;
     private TestLooper mTestLooper = new TestLooper();
@@ -61,90 +67,156 @@
     private int mMusicVolume;
     private int mMusicMaxVolume;
     private boolean mMusicMute;
+    private static final int SELF_PHYSICAL_ADDRESS = 0x2000;
+    private static final int HDMI_1_PHYSICAL_ADDRESS = 0x2100;
+    private static final int HDMI_2_PHYSICAL_ADDRESS = 0x2200;
+    private static final int HDMI_3_PHYSICAL_ADDRESS = 0x2300;
+    private int mInvokeDeviceEventState;
+    private HdmiDeviceInfo mDeviceInfo;
+    private boolean mMutingEnabled;
+    private boolean mArcSupport;
+    private HdmiPortInfo[] mHdmiPortInfo;
 
     @Before
     public void setUp() {
         mHdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
-                    @Override
-                    AudioManager getAudioManager() {
-                        return new AudioManager() {
-                            @Override
-                            public int getStreamVolume(int streamType) {
-                                switch (streamType) {
-                                    case STREAM_MUSIC:
-                                        return mMusicVolume;
-                                    default:
-                                        return 0;
-                                }
+            new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+                @Override
+                AudioManager getAudioManager() {
+                    return new AudioManager() {
+                        @Override
+                        public int getStreamVolume(int streamType) {
+                            switch (streamType) {
+                                case STREAM_MUSIC:
+                                    return mMusicVolume;
+                                default:
+                                    return 0;
                             }
+                        }
 
-                            @Override
-                            public boolean isStreamMute(int streamType) {
-                                switch (streamType) {
-                                    case STREAM_MUSIC:
-                                        return mMusicMute;
-                                    default:
-                                        return false;
-                                }
+                        @Override
+                        public boolean isStreamMute(int streamType) {
+                            switch (streamType) {
+                                case STREAM_MUSIC:
+                                    return mMusicMute;
+                                default:
+                                    return false;
                             }
+                        }
 
-                            @Override
-                            public int getStreamMaxVolume(int streamType) {
-                                switch (streamType) {
-                                    case STREAM_MUSIC:
-                                        return mMusicMaxVolume;
-                                    default:
-                                        return 100;
-                                }
+                        @Override
+                        public int getStreamMaxVolume(int streamType) {
+                            switch (streamType) {
+                                case STREAM_MUSIC:
+                                    return mMusicMaxVolume;
+                                default:
+                                    return 100;
                             }
+                        }
 
-                            @Override
-                            public void adjustStreamVolume(
-                                    int streamType, int direction, int flags) {
-                                switch (streamType) {
-                                    case STREAM_MUSIC:
-                                        if (direction == AudioManager.ADJUST_UNMUTE) {
-                                            mMusicMute = false;
-                                        } else if (direction == AudioManager.ADJUST_MUTE) {
-                                            mMusicMute = true;
-                                        }
-                                    default:
-                                }
+                        @Override
+                        public void adjustStreamVolume(
+                                int streamType, int direction, int flags) {
+                            switch (streamType) {
+                                case STREAM_MUSIC:
+                                    if (direction == AudioManager.ADJUST_UNMUTE) {
+                                        mMusicMute = false;
+                                    } else if (direction == AudioManager.ADJUST_MUTE) {
+                                        mMusicMute = true;
+                                    }
+                                    break;
+                                default:
                             }
+                        }
 
-                            @Override
-                            public void setWiredDeviceConnectionState(
+                        @Override
+                        public void setWiredDeviceConnectionState(
                                 int type, int state, String address, String name) {
-                                // Do nothing.
-                            }
-                        };
-                    }
+                            // Do nothing.
+                        }
+                    };
+                }
 
-                    @Override
-                    void wakeUp() {}
-                };
+                @Override
+                void wakeUp() {}
+
+                @Override
+                void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
+                    mDeviceInfo = device;
+                    mInvokeDeviceEventState = status;
+                }
+
+                @Override
+                void writeStringSetting(String key, String value) {
+                    // do nothing
+                }
+
+                @Override
+                boolean readBooleanSetting(String key, boolean defVal) {
+                    switch (key) {
+                        case Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE:
+                            return mMutingEnabled;
+                        case Constants.PROPERTY_ARC_SUPPORT:
+                            return mArcSupport;
+                        default:
+                            return defVal;
+                    }
+                }
+            };
 
         mMyLooper = mTestLooper.getLooper();
         mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
+        mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
+            @Override
+            void setIsActiveSource(boolean on) {
+                mIsActiveSource = on;
+            }
+
+            @Override
+            protected int getPreferredAddress() {
+                return ADDR_PLAYBACK_1;
+            }
+        };
         mHdmiCecLocalDeviceAudioSystem.init();
+        mHdmiCecLocalDevicePlayback.init();
         mHdmiControlService.setIoLooper(mMyLooper);
         mNativeWrapper = new FakeNativeWrapper();
+        mNativeWrapper.setPhysicalAddress(SELF_PHYSICAL_ADDRESS);
         mHdmiCecController =
-                HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+            HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+        mLocalDevices.add(mHdmiCecLocalDevicePlayback);
+        mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true);
+        mHdmiPortInfo = new HdmiPortInfo[4];
+        mHdmiPortInfo[0] =
+            new HdmiPortInfo(
+                0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS, true, false, false);
+        mHdmiPortInfo[1] =
+            new HdmiPortInfo(
+                2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS, true, false, false);
+        mHdmiPortInfo[2] =
+            new HdmiPortInfo(
+                1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS, true, false, false);
+        mHdmiPortInfo[3] =
+            new HdmiPortInfo(
+                4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
+        mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiControlService.initPortInfo();
         // No TV device interacts with AVR so system audio control won't be turned on here
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
-        SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
+        mNativeWrapper.clearResultMessages();
+        mMutingEnabled = true;
+        mArcSupport = true;
+        mInvokeDeviceEventState = 0;
+        mDeviceInfo = null;
     }
 
     @Test
-    public void handleGiveAudioStatus_volume_10_mute_true() {
+    public void handleGiveAudioStatus_volume_10_mute_true() throws Exception {
         mMusicVolume = 10;
         mMusicMute = true;
         mMusicMaxVolume = 20;
@@ -154,25 +226,24 @@
                         ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
         HdmiCecMessage messageGive =
                 HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
-                .isTrue();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive)).isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
     @Test
-    public void handleGiveSystemAudioModeStatus_originalOff() {
+    public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+                HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                        ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         HdmiCecMessage messageGive =
                 HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
-                .isTrue();
+            .isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
-    @Ignore("b/80297700")
     @Test
     public void handleRequestShortAudioDescriptor_featureDisabled() throws Exception {
         HdmiCecMessage expectedMessage =
@@ -184,9 +255,9 @@
 
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(false);
         assertThat(
-                        mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
-                                MESSAGE_REQUEST_SAD_LCPM))
-                .isTrue();
+            mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
+                MESSAGE_REQUEST_SAD_LCPM))
+            .isTrue();
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
@@ -200,15 +271,17 @@
                         Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
                         Constants.ABORT_NOT_IN_CORRECT_MODE);
 
-        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
+        mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(false);
         assertThat(
-                        mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
-                                MESSAGE_REQUEST_SAD_LCPM))
-                .isEqualTo(true);
+            mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
+                MESSAGE_REQUEST_SAD_LCPM))
+            .isEqualTo(true);
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
+    // Testing device has sadConfig.xml
+    @Ignore("b/120845532")
     @Test
     public void handleRequestShortAudioDescriptor_noAudioDeviceInfo() throws Exception {
         HdmiCecMessage expectedMessage =
@@ -218,17 +291,17 @@
                         Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
                         Constants.ABORT_UNABLE_TO_DETERMINE);
 
-        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+        mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
         assertThat(
-                        mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
-                                MESSAGE_REQUEST_SAD_LCPM))
-                .isEqualTo(true);
+            mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
+                MESSAGE_REQUEST_SAD_LCPM))
+            .isEqualTo(true);
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
     @Test
-    public void handleSetSystemAudioMode_setOn_orignalOff() {
+    public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception {
         mMusicMute = true;
         HdmiCecMessage messageSet =
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
@@ -238,25 +311,24 @@
         HdmiCecMessage expectedMessage =
                 HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
-                .isTrue();
+            .isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
         // Check if correctly turned on
+        mNativeWrapper.clearResultMessages();
         expectedMessage =
-                HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
-                .isTrue();
+            HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet)).isTrue();
         mTestLooper.dispatchAll();
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
-                .isTrue();
+            .isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
         assertThat(mMusicMute).isFalse();
     }
 
-    @Ignore("b/80297700")
     @Test
-    public void handleSystemAudioModeRequest_turnOffByTv() {
+    public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
         assertThat(mMusicMute).isFalse();
         // Check if feature correctly turned off
         HdmiCecMessage messageGive =
@@ -268,91 +340,91 @@
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(
                         ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
-                .isTrue();
+            .isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+
+        mNativeWrapper.clearResultMessages();
         expectedMessage =
-                HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+            HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
-                .isTrue();
+            .isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
         assertThat(mMusicMute).isTrue();
     }
 
-    @Ignore("b/80297700")
     @Test
-    public void onStandbyAudioSystem_currentSystemAudioControlOn() {
+    public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception {
         // Set system audio control on first
-        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+        mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
         // Check if standby correctly turns off the feature
         mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
         HdmiCecMessage expectedMessage =
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(
                         ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
-        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
         assertThat(mMusicMute).isTrue();
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_alwaysOn() {
+    public void systemAudioControlOnPowerOn_alwaysOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
         assertThat(
-                        mHdmiCecLocalDeviceAudioSystem.getActions(
-                                SystemAudioInitiationActionFromAvr.class))
-                .isNotEmpty();
+            mHdmiCecLocalDeviceAudioSystem.getActions(
+                SystemAudioInitiationActionFromAvr.class))
+            .isNotEmpty();
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_neverOn() {
+    public void systemAudioControlOnPowerOn_neverOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
         assertThat(
-                        mHdmiCecLocalDeviceAudioSystem.getActions(
-                                SystemAudioInitiationActionFromAvr.class))
-                .isEmpty();
+            mHdmiCecLocalDeviceAudioSystem.getActions(
+                SystemAudioInitiationActionFromAvr.class))
+            .isEmpty();
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_useLastState_off() {
+    public void systemAudioControlOnPowerOn_useLastState_off() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
         assertThat(
-                        mHdmiCecLocalDeviceAudioSystem.getActions(
-                                SystemAudioInitiationActionFromAvr.class))
-                .isEmpty();
+            mHdmiCecLocalDeviceAudioSystem.getActions(
+                SystemAudioInitiationActionFromAvr.class))
+            .isEmpty();
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_useLastState_on() {
+    public void systemAudioControlOnPowerOn_useLastState_on() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
         assertThat(
-                        mHdmiCecLocalDeviceAudioSystem.getActions(
-                                SystemAudioInitiationActionFromAvr.class))
-                .isNotEmpty();
+            mHdmiCecLocalDeviceAudioSystem.getActions(
+                SystemAudioInitiationActionFromAvr.class))
+            .isNotEmpty();
     }
 
     @Test
-    public void handleActiveSource_updateActiveSource() {
+    public void handleActiveSource_updateActiveSource() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
         ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
-                .isTrue();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)).isTrue();
         mTestLooper.dispatchAll();
         assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
-                .isTrue();
+            .isTrue();
     }
 
     @Test
-    public void terminateSystemAudioMode_systemAudioModeOff() {
-        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
+    public void terminateSystemAudioMode_systemAudioModeOff() throws Exception {
+        mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
         mMusicMute = false;
         HdmiCecMessage message =
@@ -361,13 +433,12 @@
         mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
         assertThat(mMusicMute).isFalse();
-        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message);
+        assertThat(mNativeWrapper.getResultMessages()).isEmpty();
     }
 
-    @Ignore("b/80297700")
     @Test
-    public void terminateSystemAudioMode_systemAudioModeOn() {
-        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+    public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
+        mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
         mMusicMute = false;
         HdmiCecMessage expectedMessage =
@@ -381,124 +452,244 @@
     }
 
     @Test
-    public void isPhysicalAddressMeOrBelow_isMe() {
-        int targetPhysicalAddress = 0x1000;
-        mNativeWrapper.setPhysicalAddress(0x1000);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
-            .isTrue();
-    }
-
-    @Test
-    public void isPhysicalAddressMeOrBelow_isBelow() {
-        int targetPhysicalAddress = 0x1100;
-        mNativeWrapper.setPhysicalAddress(0x1000);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
-            .isTrue();
-    }
-
-    @Test
-    public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() {
-        int targetPhysicalAddress = 0x3000;
-        mNativeWrapper.setPhysicalAddress(0x2000);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
-            .isFalse();
-
-        targetPhysicalAddress = 0x2200;
-        mNativeWrapper.setPhysicalAddress(0x3300);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
-            .isFalse();
-
-        targetPhysicalAddress = 0x2213;
-        mNativeWrapper.setPhysicalAddress(0x2212);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
-            .isFalse();
-
-        targetPhysicalAddress = 0x2340;
-        mNativeWrapper.setPhysicalAddress(0x2310);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
-            .isFalse();
-    }
-
-    @Test
-    public void handleRequestArcInitiate_isNotDirectConnectedToTv() {
-        HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
-        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildFeatureAbortCommand(
-                ADDR_AUDIO_SYSTEM, ADDR_TV,
-                Constants.MESSAGE_REQUEST_ARC_INITIATION,
-                Constants.ABORT_NOT_IN_CORRECT_MODE);
+    public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                        ADDR_AUDIO_SYSTEM,
+                        ADDR_TV,
+                        Constants.MESSAGE_REQUEST_ARC_INITIATION,
+                        Constants.ABORT_NOT_IN_CORRECT_MODE);
         mNativeWrapper.setPhysicalAddress(0x1100);
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
-            .isTrue();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
 
     @Test
-    public void handleRequestArcInitiate_startArcInitiationActionFromAvr() {
-        HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+    public void handleRequestArcInitiate_startArcInitiationActionFromAvr() throws Exception {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         mNativeWrapper.setPhysicalAddress(0x1000);
-        mHdmiCecLocalDeviceAudioSystem.removeAction(
-            ArcInitiationActionFromAvr.class);
+        mHdmiCecLocalDeviceAudioSystem.removeAction(ArcInitiationActionFromAvr.class);
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
-            .isTrue();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(ArcInitiationActionFromAvr.class)).isNotEmpty();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcInitiationActionFromAvr.class))
+            .isNotEmpty();
     }
 
     @Test
-    public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() {
+    public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
 
-        HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
-        mHdmiCecLocalDeviceAudioSystem.removeAction(
-            ArcTerminationActionFromAvr.class);
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceAudioSystem.removeAction(ArcTerminationActionFromAvr.class);
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
-            .isTrue();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)).isTrue();
         mTestLooper.dispatchAll();
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(ArcTerminationActionFromAvr.class)).isNotEmpty();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcTerminationActionFromAvr.class))
+            .isNotEmpty();
     }
 
     @Test
-    public void handleRequestArcTerminate_arcIsNotOn() {
+    public void handleRequestArcTerminate_arcIsNotOn() throws Exception {
         assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
-        HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
-        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildFeatureAbortCommand(
-                ADDR_AUDIO_SYSTEM, ADDR_TV,
-                Constants.MESSAGE_REQUEST_ARC_TERMINATION,
-                Constants.ABORT_NOT_IN_CORRECT_MODE);
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                        ADDR_AUDIO_SYSTEM,
+                        ADDR_TV,
+                        Constants.MESSAGE_REQUEST_ARC_TERMINATION,
+                        Constants.ABORT_NOT_IN_CORRECT_MODE);
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
-            .isTrue();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)).isTrue();
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
 
     @Test
-    public void handleRequestArcInit_arcIsNotSupported() {
-        HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
-        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildFeatureAbortCommand(
-                ADDR_AUDIO_SYSTEM, ADDR_TV,
-                Constants.MESSAGE_REQUEST_ARC_INITIATION,
-                Constants.ABORT_UNRECOGNIZED_OPCODE);
-        SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false");
+    public void handleRequestArcInit_arcIsNotSupported() throws Exception {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                        ADDR_AUDIO_SYSTEM,
+                        ADDR_TV,
+                        Constants.MESSAGE_REQUEST_ARC_INITIATION,
+                        Constants.ABORT_UNRECOGNIZED_OPCODE);
+        mArcSupport = false;
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
-            .isTrue();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
+
+    public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+                        ADDR_TUNER_1, ADDR_AUDIO_SYSTEM,
+                        SELF_PHYSICAL_ADDRESS, true);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                        ADDR_AUDIO_SYSTEM,
+                        ADDR_TUNER_1,
+                        Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
+                        Constants.ABORT_REFUSED);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(message)).isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void handleSystemAudioModeRequest_fromNonTV_tVSupport() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+                        ADDR_TUNER_1, ADDR_AUDIO_SYSTEM,
+                        SELF_PHYSICAL_ADDRESS, true);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                        ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, true);
+        mHdmiCecLocalDeviceAudioSystem.setTvSystemAudioModeSupport(true);
+
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(message)).isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void handleActiveSource_activeSourceFromTV_swithToArc() {
+        mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+
+        ActiveSource expectedActiveSource = ActiveSource.of(ADDR_TV, 0x0000);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)).isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource())
+            .isEqualTo(expectedActiveSource);
+    }
+
+    @Test
+    public void handleRoutingChange_currentActivePortIsHome() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, SELF_PHYSICAL_ADDRESS);
+
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS);
+        ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS);
+        int expectedLocalActivePort = Constants.CEC_SWITCH_HOME;
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource())
+            .isEqualTo(expectedActiveSource);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getRoutingPort())
+            .isEqualTo(expectedLocalActivePort);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Test
+    public void handleRoutingInformation_currentActivePortIsHDMI1() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, SELF_PHYSICAL_ADDRESS);
+        mHdmiCecLocalDeviceAudioSystem.setRoutingPort(mHdmiPortInfo[1].getId());
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildRoutingInformation(
+                        ADDR_AUDIO_SYSTEM, HDMI_1_PHYSICAL_ADDRESS);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingInformation(message)).isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Ignore("b/120845532")
+    @Test
+    public void handleRoutingChange_homeIsActive_playbackSendActiveSource() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x2000);
+
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, 0x2000);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue();
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
+
+    @Test
+    public void updateCecDevice_deviceNotExists_addDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
+        HdmiDeviceInfo newDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(newDevice);
+        assertThat(mDeviceInfo).isEqualTo(newDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(newDevice.getLogicalAddress())).isEqualTo(newDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+    }
+
+    @Test
+    public void updateCecDevice_deviceExists_doNothing() {
+        mInvokeDeviceEventState = 0;
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(oldDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(0);
+    }
+
+    @Test
+    public void updateCecDevice_deviceInfoDifferent_updateDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2300, 4, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(differentDevice);
+        assertThat(mDeviceInfo).isEqualTo(differentDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
+    }
+
+    @Test
+    public void handleReportPhysicalAddress_differentPath_addDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_2, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_2));
+        HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
+                .buildReportPhysicalAddressCommand(
+                        ADDR_PLAYBACK_2, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress);
+
+        mTestLooper.dispatchAll();
+        assertThat(mDeviceInfo).isEqualTo(differentDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
new file mode 100644
index 0000000..feae4ee
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Looper;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiCecLocalDevicePlayback} class. */
+public class HdmiCecLocalDevicePlaybackTest {
+
+    private HdmiControlService mHdmiControlService;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
+    private FakeNativeWrapper mNativeWrapper;
+    private Looper mMyLooper;
+    private TestLooper mTestLooper = new TestLooper();
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mPlaybackPhysicalAddress;
+
+    @Before
+    public void setUp() {
+        mHdmiControlService =
+            new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+                @Override
+                void wakeUp() {
+                }
+
+                @Override
+                boolean isControlEnabled() {
+                    return true;
+                }
+            };
+
+        mMyLooper = mTestLooper.getLooper();
+        mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
+        mHdmiCecLocalDevicePlayback.init();
+        mHdmiControlService.setIoLooper(mMyLooper);
+        mNativeWrapper = new FakeNativeWrapper();
+        mHdmiCecController =
+            HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+        mHdmiControlService.setCecController(mHdmiCecController);
+        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+        mLocalDevices.add(mHdmiCecLocalDevicePlayback);
+        mHdmiControlService.initPortInfo();
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+        mNativeWrapper.clearResultMessages();
+        mPlaybackPhysicalAddress = 0x2000;
+        mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
+    }
+
+    // Playback device does not handle routing control related feature right now
+    @Ignore("b/120845532")
+    @Test
+    public void handleSetStreamPath_underCurrentDevice() {
+        assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0);
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
+        // TODO(amyjojo): Move set and get LocalActivePath to Control Service.
+        assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(1);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index c7809d3..ef974f2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -18,6 +18,7 @@
 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
 import static com.android.server.hdmi.Constants.ADDR_TV;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.hardware.hdmi.HdmiDeviceInfo;
@@ -51,6 +52,14 @@
         assertThat(message).isEqualTo(buildMessage("05:A4:06:01"));
     }
 
+    @Test
+    public void buildRoutingInformation() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingInformation(
+                        ADDR_AUDIO_SYSTEM, 0x2100);
+        assertThat(message).isEqualTo(buildMessage("5F:81:21:00"));
+    }
+
     /**
      * Build a CEC message from a hex byte string with bytes separated by {@code :}.
      *
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 18c9a65..1f66074 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -20,13 +20,18 @@
 
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import android.hardware.hdmi.HdmiPortInfo;
 import android.os.Looper;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -102,6 +107,7 @@
     private TestLooper mTestLooper = new TestLooper();
     private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
     private boolean mStandbyMessageReceived;
+    private HdmiPortInfo[] mHdmiPortInfo;
 
     @Before
     public void SetUp() {
@@ -131,6 +137,16 @@
 
         mLocalDevices.add(mMyAudioSystemDevice);
         mLocalDevices.add(mMyPlaybackDevice);
+        mHdmiPortInfo = new HdmiPortInfo[4];
+        mHdmiPortInfo[0] =
+            new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+        mHdmiPortInfo[1] =
+            new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
+        mHdmiPortInfo[2] =
+            new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+        mHdmiPortInfo[3] =
+            new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
+        mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiControlService.initPortInfo();
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
 
@@ -159,4 +175,27 @@
         assertTrue(mMyPlaybackDevice.isDisabled());
         assertTrue(mMyAudioSystemDevice.isDisabled());
     }
+
+    @Test
+    public void pathToPort_pathExists_weAreNonTv() {
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        mHdmiControlService.initPortInfo();
+        assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1);
+        assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2);
+    }
+
+    @Test
+    public void pathToPort_pathExists_weAreTv() {
+        mNativeWrapper.setPhysicalAddress(0x0000);
+        mHdmiControlService.initPortInfo();
+        assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3);
+        assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4);
+    }
+
+    @Test
+    public void pathToPort_pathInvalid() {
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        mHdmiControlService.initPortInfo();
+        assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
new file mode 100644
index 0000000..985c647
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Slog;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiUtils.CodecSad;
+import com.android.server.hdmi.HdmiUtils.DeviceConfig;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiUtils} class. */
+public class HdmiUtilsTest {
+
+    private static final String TAG = "HdmiUtilsTest";
+
+    private final String mExampleXML =
+            "<!-- A sample Short Audio Descriptor configuration xml -->"
+                    + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">"
+                    + "<device type=\"VX_AUDIO_DEVICE_IN_HDMI_ARC\">"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"011a03\"/>"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"0d0506\"/>"
+                    + "</device>"
+                    + "<device type=\"AUDIO_DEVICE_IN_SPDIF\">"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"010203\"/>"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"040506\"/>"
+                    + "</device>"
+                    + "</config>";
+
+    @Test
+    public void pathToPort_isMe() {
+        int targetPhysicalAddress = 0x1000;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS);
+    }
+
+    @Test
+    public void pathToPort_isDirectlyBelow() {
+        int targetPhysicalAddress = 0x1100;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_isBelow() {
+        int targetPhysicalAddress = 0x1110;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_neitherMeNorBelow() {
+        int targetPhysicalAddress = 0x3000;
+        int myPhysicalAddress = 0x2000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2200;
+        myPhysicalAddress = 0x3300;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2213;
+        myPhysicalAddress = 0x2212;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2340;
+        myPhysicalAddress = 0x2310;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+    }
+
+    @Test
+    public void parseSampleXML() {
+        List<DeviceConfig> config = new ArrayList<>();
+        try {
+            config = HdmiUtils.ShortAudioDescriptorXmlParser.parse(
+                    new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8)));
+        } catch (IOException e) {
+            Slog.e(TAG, e.getMessage(), e);
+        } catch (XmlPullParserException e) {
+            Slog.e(TAG, e.getMessage(), e);
+        }
+
+        CodecSad expectedCodec1 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "011a03");
+        CodecSad expectedCodec2 = new CodecSad(Constants.AUDIO_CODEC_DD, "0d0506");
+        CodecSad expectedCodec3 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "010203");
+        CodecSad expectedCodec4 = new CodecSad(Constants.AUDIO_CODEC_DD, "040506");
+
+        List<CodecSad> expectedList1 = new ArrayList<>();
+        expectedList1.add(expectedCodec1);
+        expectedList1.add(expectedCodec2);
+
+        List<CodecSad> expectedList2 = new ArrayList<>();
+        expectedList2.add(expectedCodec3);
+        expectedList2.add(expectedCodec4);
+
+        DeviceConfig expectedDevice1 = new DeviceConfig(
+                "VX_AUDIO_DEVICE_IN_HDMI_ARC", expectedList1);
+        DeviceConfig expectedDevice2 = new DeviceConfig(
+                "AUDIO_DEVICE_IN_SPDIF", expectedList2);
+
+        List<DeviceConfig> expectedConfig = new ArrayList<>();
+        expectedConfig.add(expectedDevice1);
+        expectedConfig.add(expectedDevice2);
+
+        assertThat(config).isEqualTo(expectedConfig);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index d914b9a..440a49a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -26,9 +26,12 @@
 import android.media.AudioManager;
 import android.os.Looper;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -49,6 +52,9 @@
     private int mMsgRequestActiveSourceCount;
     private int mMsgSetSystemAudioModeCount;
     private int mQueryTvSystemAudioModeSupportCount;
+    private boolean mArcEnabled;
+    private boolean mIsPlaybackDevice;
+    private boolean mBroadcastActiveSource;
 
     @Before
     public void SetUp() {
@@ -80,6 +86,8 @@
                                     callback.onSendCompleted(SendMessageResult.NACK);
                                 }
                                 break;
+                            case Constants.MESSAGE_INITIATE_ARC:
+                                break;
                             default:
                                 throw new IllegalArgumentException("Unexpected message");
                         }
@@ -132,6 +140,22 @@
                     int getPhysicalAddress() {
                         return 0;
                     }
+
+                    @Override
+                    boolean isPlaybackDevice() {
+                        return mIsPlaybackDevice;
+                    }
+
+                    @Override
+                    public void setAndBroadcastActiveSourceFromOneDeviceType(
+                            int sourceAddress, int physicalAddress) {
+                        mBroadcastActiveSource = true;
+                    }
+
+                    @Override
+                    int pathToPortId(int path) {
+                        return -1;
+                    }
                 };
         mHdmiCecLocalDeviceAudioSystem =
                 new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@@ -148,6 +172,11 @@
                     HdmiDeviceInfo getDeviceInfo() {
                         return mDeviceInfoForTests;
                     }
+
+                    @Override
+                    void setArcStatus(boolean enabled) {
+                        mArcEnabled = enabled;
+                    }
                 };
         mHdmiCecLocalDeviceAudioSystem.init();
         Looper looper = mTestLooper.getLooper();
@@ -159,7 +188,7 @@
         resetTestVariables();
         mShouldDispatchActiveSource = false;
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
                 .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
 
         mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
@@ -171,7 +200,7 @@
         assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(0);
         assertFalse(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
                 .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
     }
 
@@ -206,14 +235,15 @@
         assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
         assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
 
-        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress).isEqualTo(1002);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
+            .isEqualTo(1002);
     }
 
     @Test
     public void testKnownActiveSource() {
         resetTestVariables();
         mTvSystemAudioModeSupport = true;
-        mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress = 1001;
+        mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress = 1001;
 
         mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
                 new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
@@ -233,7 +263,7 @@
         mTryCountBeforeSucceed = 3;
         assertThat(mTryCountBeforeSucceed)
                 .isAtMost(SystemAudioInitiationActionFromAvr.MAX_RETRY_COUNT);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
                 .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
 
         mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
@@ -246,12 +276,33 @@
         assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
     }
 
+    @Ignore("b/120845532")
+    @Test
+    public void testIsPlaybackDevice_cannotReceiveActiveSource() {
+        resetTestVariables();
+        mIsPlaybackDevice = true;
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
+            .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+                new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+        mTestLooper.dispatchAll();
+
+        assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+        assertThat(mBroadcastActiveSource).isTrue();
+        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
+    }
+
     private void resetTestVariables() {
         mMsgRequestActiveSourceCount = 0;
         mMsgSetSystemAudioModeCount = 0;
         mQueryTvSystemAudioModeSupportCount = 0;
         mTryCountBeforeSucceed = 0;
-        mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress =
+        mIsPlaybackDevice = false;
+        mBroadcastActiveSource = false;
+        mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress =
                 Constants.INVALID_PHYSICAL_ADDRESS;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java
new file mode 100644
index 0000000..e5529cb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2019 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.job;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Log;
+
+import com.android.server.job.JobConcurrencyManager.JobCountTracker;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+/**
+ * Test for {@link com.android.server.job.JobConcurrencyManager.JobCountTracker}.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class JobCountTrackerTest {
+    private static final String TAG = "JobCountTrackerTest";
+
+    private Random mRandom;
+    private JobCountTracker mJobCountTracker;
+
+    @Before
+    public void setUp() {
+        mRandom = new Random(1); // Always use the same series of pseudo random values.
+        mJobCountTracker = new JobCountTracker();
+    }
+
+    /**
+     * Represents running and pending jobs.
+     */
+    class Jobs {
+        public int runningFg;
+        public int runningBg;
+        public int pendingFg;
+        public int pendingBg;
+
+        public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
+            while (mRandom.nextDouble() < startRatio) {
+                if (mRandom.nextDouble() < fgJobRatio) {
+                    pendingFg++;
+                } else {
+                    pendingBg++;
+                }
+            }
+        }
+
+        public void maybeFinishJobs(double stopRatio) {
+            for (int i = runningBg; i > 0; i--) {
+                if (mRandom.nextDouble() < stopRatio) {
+                    runningBg--;
+                }
+            }
+            for (int i = runningFg; i > 0; i--) {
+                if (mRandom.nextDouble() < stopRatio) {
+                    runningFg--;
+                }
+            }
+        }
+    }
+
+
+    private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) {
+        mJobCountTracker.reset(totalMax, maxBg, minBg);
+
+        for (int i = 0; i < jobs.runningFg; i++) {
+            mJobCountTracker.incrementRunningJobCount(true);
+        }
+        for (int i = 0; i < jobs.runningBg; i++) {
+            mJobCountTracker.incrementRunningJobCount(false);
+        }
+
+        for (int i = 0; i < jobs.pendingFg; i++) {
+            mJobCountTracker.incrementPendingJobCount(true);
+        }
+        for (int i = 0; i < jobs.pendingBg; i++) {
+            mJobCountTracker.incrementPendingJobCount(false);
+        }
+
+        mJobCountTracker.onCountDone();
+
+        while ((jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true))
+                || (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false))) {
+            final boolean isStartingFg = mRandom.nextBoolean();
+
+            if (isStartingFg) {
+                if (jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) {
+                    jobs.pendingFg--;
+                    jobs.runningFg++;
+                    mJobCountTracker.onStartingNewJob(true);
+                }
+            } else {
+                if (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false)) {
+                    jobs.pendingBg--;
+                    jobs.runningBg++;
+                    mJobCountTracker.onStartingNewJob(false);
+                }
+            }
+        }
+
+        Log.i(TAG, "" + mJobCountTracker);
+    }
+
+    /**
+     * Used by the following testRandom* tests.
+     */
+    private void checkRandom(Jobs jobs, int numTests, int totalMax, int maxBg, int minBg,
+            double startRatio, double fgJobRatio, double stopRatio) {
+        for (int i = 0; i < numTests; i++) {
+
+            jobs.maybeFinishJobs(stopRatio);
+            jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
+
+            startPendingJobs(jobs, totalMax, maxBg, minBg);
+
+            assertThat(jobs.runningFg).isAtMost(totalMax);
+            assertThat(jobs.runningBg).isAtMost(totalMax);
+            assertThat(jobs.runningFg + jobs.runningBg).isAtMost(totalMax);
+            assertThat(jobs.runningBg).isAtMost(maxBg);
+        }
+    }
+
+    /**
+     * Randomly enqueue / stop jobs and make sure we won't run more jobs than we should.
+     */
+    @Test
+    public void testRandom1() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final int maxBg = 4;
+        final int minBg = 2;
+        final double stopRatio = 0.1;
+        final double fgJobRatio = 0.5;
+        final double startRatio = 0.1;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio , stopRatio);
+    }
+
+    @Test
+    public void testRandom2() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 2;
+        final int maxBg = 2;
+        final int minBg = 0;
+        final double stopRatio = 0.5;
+        final double fgJobRatio = 0.5;
+        final double startRatio = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+    }
+
+    @Test
+    public void testRandom3() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 2;
+        final int maxBg = 2;
+        final int minBg = 2;
+        final double stopRatio = 0.5;
+        final double fgJobRatio = 0.5;
+        final double startRatio = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+    }
+
+    @Test
+    public void testRandom4() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 10;
+        final int maxBg = 2;
+        final int minBg = 0;
+        final double stopRatio = 0.5;
+        final double fgJobRatio = 0.5;
+        final double startRatio = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+    }
+
+    @Test
+    public void testRandom5() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final int maxBg = 4;
+        final int minBg = 2;
+        final double stopRatio = 0.5;
+        final double fgJobRatio = 0.1;
+        final double startRatio = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+    }
+
+    @Test
+    public void testRandom6() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final int maxBg = 4;
+        final int minBg = 2;
+        final double stopRatio = 0.5;
+        final double fgJobRatio = 0.9;
+        final double startRatio = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+    }
+
+    @Test
+    public void testRandom7() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final int maxBg = 4;
+        final int minBg = 2;
+        final double stopRatio = 0.4;
+        final double fgJobRatio = 0.1;
+        final double startRatio = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+    }
+
+    @Test
+    public void testRandom8() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final int maxBg = 4;
+        final int minBg = 2;
+        final double stopRatio = 0.4;
+        final double fgJobRatio = 0.9;
+        final double startRatio = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+    }
+
+    /** Used by the following tests */
+    private void checkSimple(int totalMax, int maxBg, int minBg,
+            int runningFg, int runningBg, int pendingFg, int pendingBg,
+            int resultRunningFg, int resultRunningBg, int resultPendingFg, int resultPendingBg) {
+        final Jobs jobs = new Jobs();
+        jobs.runningFg = runningFg;
+        jobs.runningBg = runningBg;
+        jobs.pendingFg = pendingFg;
+        jobs.pendingBg = pendingBg;
+
+        startPendingJobs(jobs, totalMax, maxBg, minBg);
+
+        assertThat(jobs.runningFg).isEqualTo(resultRunningFg);
+        assertThat(jobs.runningBg).isEqualTo(resultRunningBg);
+
+        assertThat(jobs.pendingFg).isEqualTo(resultPendingFg);
+        assertThat(jobs.pendingBg).isEqualTo(resultPendingBg);
+    }
+
+
+    @Test
+    public void testBasic() {
+        // Args are:
+        // First 3: Total-max, bg-max, bg-min.
+        // Next 2:  Running FG / BG
+        // Next 2:  Pending FG / BG
+        // Next 4:  Result running FG / BG, pending FG/BG.
+        checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 1, 0, /*res run/pen=*/ 1, 0, 0, 0);
+
+        checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 0, /*res run/pen=*/ 6, 0, 4, 0);
+
+        // When there are BG jobs pending, 2 (min-BG) jobs should run.
+        checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0);
+        checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1);
+
+        checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
new file mode 100644
index 0000000..01199a3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 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.job;
+
+import android.util.KeyValueListParser;
+
+import com.android.server.job.JobSchedulerService.MaxJobCounts;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MaxJobCountsTest {
+
+    private void check(String config,
+            int defaultTotal, int defaultMaxBg, int defaultMinBg,
+            int expectedTotal, int expectedMaxBg, int expectedMinBg) {
+        final KeyValueListParser parser = new KeyValueListParser(',');
+        parser.setString(config);
+
+        final MaxJobCounts counts = new JobSchedulerService.MaxJobCounts(
+                defaultTotal, "total",
+                defaultMaxBg, "maxbg",
+                defaultMinBg, "minbg");
+
+        counts.parse(parser);
+
+        Assert.assertEquals(expectedTotal, counts.getTotalMax());
+        Assert.assertEquals(expectedMaxBg, counts.getMaxBg());
+        Assert.assertEquals(expectedMinBg, counts.getMinBg());
+    }
+
+    @Test
+    public void test() {
+        check("", /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
+        check("", /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
+        check("", /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
+        check("", /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0);
+        check("", /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4);
+        check("", /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
+        check("", /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
+        check("", /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
+
+        check("total=5,maxbg=4,minbg=3", /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
+        check("total=5", /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
+        check("maxbg=4", /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
+        check("minbg=3", /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 8a9e5d1..c2d4846 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -23,6 +23,7 @@
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -91,6 +92,9 @@
     private static final byte[] TEST_VAULT_HANDLE =
             new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
     private static final String TEST_APP_KEY_ALIAS = "rcleaver";
+    private static final byte[] TEST_APP_KEY_METADATA_NULL = null;
+    private static final byte[] TEST_APP_KEY_METADATA_NON_NULL =
+            "mdata".getBytes(StandardCharsets.UTF_8);
     private static final int TEST_GENERATION_ID = 2;
     private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PATTERN;
     private static final String TEST_CREDENTIAL = "pas123";
@@ -251,7 +255,7 @@
                 TEST_USER_ID,
                 TEST_RECOVERY_AGENT_UID,
                 TEST_APP_KEY_ALIAS,
-                WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
+                WrappedKey.fromSecretKey(mEncryptKey, applicationKey, TEST_APP_KEY_METADATA_NULL));
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
 
         mKeySyncTask.run();
@@ -267,7 +271,7 @@
                 TEST_USER_ID,
                 TEST_RECOVERY_AGENT_UID,
                 TEST_APP_KEY_ALIAS,
-                WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
+                WrappedKey.fromSecretKey(mEncryptKey, applicationKey, TEST_APP_KEY_METADATA_NULL));
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
@@ -545,18 +549,20 @@
         assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
         assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
         byte[] appKey = KeySyncUtils.decryptApplicationKey(
-                recoveryKey, keyData.getEncryptedKeyMaterial());
+                recoveryKey, keyData.getEncryptedKeyMaterial(), TEST_APP_KEY_METADATA_NULL);
         assertThat(appKey).isEqualTo(applicationKey.getEncoded());
     }
 
     @Test
-    public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
+    public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath_withNullKeyMetadata()
+            throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         mRecoverableKeyStoreDb.setServerParams(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
-        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS,
+                TEST_APP_KEY_METADATA_NULL);
 
         mKeySyncTask.run();
 
@@ -564,6 +570,33 @@
         verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
         List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
         assertThat(applicationKeys).hasSize(1);
+        WrappedApplicationKey keyData = applicationKeys.get(0);
+        assertThat(keyData.getAlias()).isEqualTo(TEST_APP_KEY_ALIAS);
+        assertThat(keyData.getMetadata()).isEqualTo(TEST_APP_KEY_METADATA_NULL);
+        assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+                .isEqualTo(TestData.CERT_PATH_1);
+    }
+
+    @Test
+    public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath_withNonNullKeyMetadata()
+            throws Exception {
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setServerParams(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+        when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS,
+                TEST_APP_KEY_METADATA_NON_NULL);
+
+        mKeySyncTask.run();
+
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
+        List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
+        assertThat(applicationKeys).hasSize(1);
+        WrappedApplicationKey keyData = applicationKeys.get(0);
+        assertThat(keyData.getAlias()).isEqualTo(TEST_APP_KEY_ALIAS);
+        assertThat(keyData.getMetadata()).isEqualTo(TEST_APP_KEY_METADATA_NON_NULL);
         assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
                 .isEqualTo(TestData.CERT_PATH_1);
     }
@@ -788,6 +821,11 @@
 
     private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias)
             throws Exception{
+        return addApplicationKey(userId, recoveryAgentUid, alias, TEST_APP_KEY_METADATA_NULL);
+    }
+
+    private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias,
+            byte[] metadata) throws Exception {
         SecretKey applicationKey = generateKey();
         mRecoverableKeyStoreDb.setServerParams(
                 userId, recoveryAgentUid, TEST_VAULT_HANDLE);
@@ -800,7 +838,7 @@
                 userId,
                 recoveryAgentUid,
                 alias,
-                WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
+                WrappedKey.fromSecretKey(mEncryptKey, applicationKey, metadata));
         return applicationKey;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index f832d3c..178fd10 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -22,6 +22,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import android.util.Pair;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -65,7 +67,8 @@
     private static final byte[] RECOVERY_RESPONSE_HEADER =
             "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
     private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
-
+    private static final byte[] NULL_METADATA = null;
+    private static final byte[] NON_NULL_METADATA = "somemetadata".getBytes(StandardCharsets.UTF_8);
 
     @Test
     public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -125,18 +128,35 @@
     }
 
     @Test
-    public void decryptApplicationKey_decryptsAnApplicationKeyEncryptedWithSecureBox()
-            throws Exception {
+    public void decryptApplicationKey_decryptsAnApplicationKey_nullMetadata() throws Exception {
         String alias = "phoebe";
         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
         SecretKey applicationKey = generateApplicationKey();
         Map<String, byte[]> encryptedKeys =
                 KeySyncUtils.encryptKeysWithRecoveryKey(
-                        recoveryKey, ImmutableMap.of(alias, applicationKey));
+                        recoveryKey,
+                        ImmutableMap.of(alias, Pair.create(applicationKey, NULL_METADATA)));
         byte[] encryptedKey = encryptedKeys.get(alias);
 
-        byte[] keyMaterial =
-                KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), encryptedKey);
+        byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                encryptedKey, NULL_METADATA);
+
+        assertArrayEquals(applicationKey.getEncoded(), keyMaterial);
+    }
+
+    @Test
+    public void decryptApplicationKey_decryptsAnApplicationKey_nonNullMetadata() throws Exception {
+        String alias = "phoebe";
+        SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
+        SecretKey applicationKey = generateApplicationKey();
+        Map<String, byte[]> encryptedKeys =
+                KeySyncUtils.encryptKeysWithRecoveryKey(
+                        recoveryKey,
+                        ImmutableMap.of(alias, Pair.create(applicationKey, NON_NULL_METADATA)));
+        byte[] encryptedKey = encryptedKeys.get(alias);
+
+        byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                encryptedKey, NON_NULL_METADATA);
 
         assertArrayEquals(applicationKey.getEncoded(), keyMaterial);
     }
@@ -147,12 +167,13 @@
         Map<String, byte[]> encryptedKeys =
                 KeySyncUtils.encryptKeysWithRecoveryKey(
                         KeySyncUtils.generateRecoveryKey(),
-                        ImmutableMap.of("casper", generateApplicationKey()));
+                        ImmutableMap.of("casper",
+                                Pair.create(generateApplicationKey(), NULL_METADATA)));
         byte[] encryptedKey = encryptedKeys.get(alias);
 
         try {
-            KeySyncUtils.decryptApplicationKey(
-                    KeySyncUtils.generateRecoveryKey().getEncoded(), encryptedKey);
+            KeySyncUtils.decryptApplicationKey(KeySyncUtils.generateRecoveryKey().getEncoded(),
+                    encryptedKey, NULL_METADATA);
             fail("Did not throw decrypting with bad key.");
         } catch (AEADBadTagException error) {
             // expected
@@ -160,6 +181,47 @@
     }
 
     @Test
+    public void decryptApplicationKey_throwsIfWrongMetadata() throws Exception {
+        String alias1 = "casper1";
+        String alias2 = "casper2";
+        String alias3 = "casper3";
+        SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
+
+        Map<String, byte[]> encryptedKeys =
+                KeySyncUtils.encryptKeysWithRecoveryKey(
+                        recoveryKey,
+                        ImmutableMap.of(
+                                alias1,
+                                Pair.create(generateApplicationKey(), NULL_METADATA),
+                                alias2,
+                                Pair.create(generateApplicationKey(), NON_NULL_METADATA),
+                                alias3,
+                                Pair.create(generateApplicationKey(), NON_NULL_METADATA)));
+
+        try {
+            KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                    encryptedKeys.get(alias1), NON_NULL_METADATA);
+            fail("Did not throw decrypting with wrong metadata.");
+        } catch (AEADBadTagException error) {
+            // expected
+        }
+        try {
+            KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                    encryptedKeys.get(alias2), NULL_METADATA);
+            fail("Did not throw decrypting with wrong metadata.");
+        } catch (AEADBadTagException error) {
+            // expected
+        }
+        try {
+            KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                    encryptedKeys.get(alias3), "different".getBytes(StandardCharsets.UTF_8));
+            fail("Did not throw decrypting with wrong metadata.");
+        } catch (AEADBadTagException error) {
+            // expected
+        }
+    }
+
+    @Test
     public void decryptRecoveryKey_decryptsALocallyEncryptedKey() throws Exception {
         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
         byte[] encrypted = KeySyncUtils.locallyEncryptRecoveryKey(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index 48afb8b..c295177 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import android.content.Context;
 import android.security.keystore.AndroidKeyStoreSecretKey;
@@ -60,6 +61,9 @@
     private static final int TEST_USER_ID = 1000;
     private static final int KEYSTORE_UID_SELF = -1;
     private static final int GCM_TAG_LENGTH_BITS = 128;
+    private static final byte[] NULL_METADATA = null;
+    private static final byte[] NON_NULL_METADATA = "test-metadata".getBytes(
+            StandardCharsets.UTF_8);
 
     private PlatformEncryptionKey mPlatformKey;
     private PlatformDecryptionKey mDecryptKey;
@@ -90,18 +94,29 @@
     }
 
     @Test
-    public void generateAndStoreKey_storesWrappedKey() throws Exception {
+    public void generateAndStoreKey_storesWrappedKey_nullMetadata() throws Exception {
         mRecoverableKeyGenerator.generateAndStoreKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NULL_METADATA);
 
         WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
         assertNotNull(wrappedKey);
+        assertNull(wrappedKey.getKeyMetadata());
+    }
+
+    @Test
+    public void generateAndStoreKey_storesWrappedKey_nonNullMetadata() throws Exception {
+        mRecoverableKeyGenerator.generateAndStoreKey(
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NON_NULL_METADATA);
+
+        WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
+        assertNotNull(wrappedKey);
+        assertArrayEquals(NON_NULL_METADATA, wrappedKey.getKeyMetadata());
     }
 
     @Test
     public void generateAndStoreKey_returnsRawMaterialOfCorrectLength() throws Exception {
         byte[] rawKey = mRecoverableKeyGenerator.generateAndStoreKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NON_NULL_METADATA);
 
         assertEquals(KEY_SIZE_BYTES, rawKey.length);
     }
@@ -109,7 +124,7 @@
     @Test
     public void generateAndStoreKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception {
         byte[] rawMaterial = mRecoverableKeyGenerator.generateAndStoreKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NULL_METADATA);
 
         WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
         Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM);
@@ -120,10 +135,30 @@
     }
 
     @Test
+    public void importKey_storesNullMetadata() throws Exception {
+        mRecoverableKeyGenerator.importKey(
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS,
+                randomBytes(KEY_SIZE_BYTES),
+                NULL_METADATA);
+        assertNull(mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS).getKeyMetadata());
+    }
+
+    @Test
+    public void importKey_storesNonNullMetadata() throws Exception {
+        mRecoverableKeyGenerator.importKey(
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS,
+                randomBytes(KEY_SIZE_BYTES),
+                NON_NULL_METADATA);
+        assertArrayEquals(NON_NULL_METADATA,
+                mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS).getKeyMetadata());
+    }
+
+    @Test
     public void importKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception {
         byte[] rawMaterial = randomBytes(KEY_SIZE_BYTES);
         mRecoverableKeyGenerator.importKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial,
+                NULL_METADATA);
 
         WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
         Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM);
@@ -145,10 +180,6 @@
         return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
     }
 
-    private static byte[] getUtf8Bytes(String s) {
-        return s.getBytes(StandardCharsets.UTF_8);
-    }
-
     private static byte[] randomBytes(int n) {
         byte[] bytes = new byte[n];
         new Random().nextBytes(bytes);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b15863d..c78b96d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -52,6 +52,7 @@
 import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.TrustedRootCertificates;
 import android.security.keystore.recovery.WrappedApplicationKey;
+import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -134,6 +135,8 @@
     private static final int GENERATION_ID = 1;
     private static final byte[] NONCE = getUtf8Bytes("nonce");
     private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
+    private static final byte[] KEY_METADATA_NULL = null;
+    private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata");
     private static final String KEY_ALGORITHM = "AES";
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
@@ -231,6 +234,77 @@
     }
 
     @Test
+    public void importKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
+
+        mRecoverableKeyStoreManager.importKeyWithMetadata(
+                TEST_ALIAS, keyMaterial, KEY_METADATA_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
+    public void importKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
+
+        mRecoverableKeyStoreManager.importKeyWithMetadata(
+                TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
+    public void importKeyWithMetadata_throwsIfInvalidLength() throws Exception {
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
+        try {
+            mRecoverableKeyStoreManager.importKeyWithMetadata(
+                    TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
+            fail("should have thrown");
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).contains("not contain 256 bits");
+        }
+    }
+
+    @Test
+    public void importKeyWithMetadata_throwsIfNullKey() throws Exception {
+        try {
+            mRecoverableKeyStoreManager.importKeyWithMetadata(
+                    TEST_ALIAS, /*keyBytes=*/ null, KEY_METADATA_NON_NULL);
+            fail("should have thrown");
+        } catch (NullPointerException e) {
+            assertThat(e.getMessage()).contains("is null");
+        }
+    }
+
+    @Test
+    public void generateKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
+    public void generateKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NON_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
     public void removeKey_removesAKey() throws Exception {
         int uid = Binder.getCallingUid();
         mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
@@ -1143,7 +1217,10 @@
         int status = 100;
         int status2 = 200;
         String alias = "key1";
-        WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
         Map<String, Integer> statuses =
                 mRecoverableKeyStoreManager.getRecoveryStatus();
@@ -1169,7 +1246,8 @@
     private static byte[] encryptedApplicationKey(
             SecretKey recoveryKey, byte[] applicationKey) throws Exception {
         return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
-                TEST_ALIAS, new SecretKeySpec(applicationKey, "AES")
+                TEST_ALIAS,
+                Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null)
         )).get(TEST_ALIAS);
     }
 
@@ -1203,7 +1281,7 @@
 
     private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion)
             throws Exception{
-        mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
+        mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
         // Simulate key sync.
         mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion);
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
index 944d6e0..9b4c3be 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -3,6 +3,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.security.keystore.recovery.TrustedRootCertificates;
+import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -64,10 +65,10 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys_emptyKeysList() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet());
@@ -75,13 +76,13 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys_singleNonWhitelistedKey() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
         String alias = "secureAlias";
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
@@ -89,14 +90,14 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys_singleWhitelistedKey() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
         String alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
         expectedResult.put(alias, rawKeys.get(alias));
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
@@ -104,21 +105,21 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
         String alias = "SECURE_ALIAS" + TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
 
         alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "1";
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
         expectedResult.put(alias, rawKeys.get(alias));
 
         alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "2";
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
         expectedResult.put(alias, rawKeys.get(alias));
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
index b5ee60e..9813ab7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -24,6 +24,7 @@
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
+import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,6 +33,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.nio.charset.StandardCharsets;
 import java.security.KeyStore;
 import java.util.HashMap;
 import java.util.Map;
@@ -47,26 +49,49 @@
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String KEY_ALGORITHM = "AES";
     private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
+    private static final String WRAPPED_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
     private static final int GENERATION_ID = 1;
     private static final int GCM_TAG_LENGTH_BYTES = 16;
     private static final int BITS_PER_BYTE = 8;
     private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
+    private static final byte[] NULL_METADATA = null;
+    private static final byte[] NON_NULL_METADATA = "keyMetadata".getBytes(StandardCharsets.UTF_8);
 
     @After
     public void tearDown() throws Exception {
         KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
         keyStore.load(/*param=*/ null);
-        keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
+        keyStore.deleteEntry(WRAPPED_KEY_ALIAS);
     }
 
+    // TODO: Add tests for non-null metadata
+
     @Test
-    public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception {
+    public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped_nullMetadata() throws Exception {
         PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
                 GENERATION_ID, generateAndroidKeyStoreKey());
         SecretKey rawKey = generateKey();
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NULL_METADATA);
+
+        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        cipher.init(
+                Cipher.UNWRAP_MODE,
+                wrappingKey.getKey(),
+                new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+        SecretKey unwrappedKey = (SecretKey) cipher.unwrap(
+                wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY);
+        assertEquals(rawKey, unwrappedKey);
+    }
+
+    @Test
+    public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped_nonNullMetadata()
+            throws Exception {
+        PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
+                GENERATION_ID, generateAndroidKeyStoreKey());
+        SecretKey rawKey = generateKey();
+
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NON_NULL_METADATA);
 
         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
         cipher.init(
@@ -84,27 +109,47 @@
                 GENERATION_ID, generateAndroidKeyStoreKey());
         SecretKey rawKey = generateKey();
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NULL_METADATA);
 
         assertEquals(GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
     }
 
     @Test
-    public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception {
+    public void decryptWrappedKeys_decryptsWrappedKeys_nullMetadata() throws Exception {
         String alias = "karlin";
         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
-                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
         keysByAlias.put(alias, wrappedKey);
 
-        Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
+        Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys(
                 new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias);
 
         assertEquals(1, unwrappedKeys.size());
         assertTrue(unwrappedKeys.containsKey(alias));
-        assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).getEncoded());
+        assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).first.getEncoded());
+        assertArrayEquals(null, unwrappedKeys.get(alias).second);
+    }
+
+    @Test
+    public void decryptWrappedKeys_decryptsWrappedKeys_nonNullMetadata() throws Exception {
+        String alias = "karlin";
+        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey appKey = generateKey();
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NON_NULL_METADATA);
+        HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
+        keysByAlias.put(alias, wrappedKey);
+
+        Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys(
+                new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias);
+
+        assertEquals(1, unwrappedKeys.size());
+        assertTrue(unwrappedKeys.containsKey(alias));
+        assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).first.getEncoded());
+        assertArrayEquals(NON_NULL_METADATA, unwrappedKeys.get(alias).second);
     }
 
     @Test
@@ -113,11 +158,11 @@
         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
-                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
         keysByAlias.put(alias, wrappedKey);
 
-        Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
+        Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys(
                 new PlatformDecryptionKey(GENERATION_ID, generateAndroidKeyStoreKey()),
                 keysByAlias);
 
@@ -128,7 +173,8 @@
     public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
-                new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey());
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey(),
+                /*metadata=*/ null);
         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
         keysByAlias.put("benji", wrappedKey);
 
@@ -156,19 +202,11 @@
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
         keyGenerator.init(new KeyGenParameterSpec.Builder(
-                WRAPPING_KEY_ALIAS,
+                WRAPPED_KEY_ALIAS,
                 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
         return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
     }
-
-    private PlatformDecryptionKey generatePlatformDecryptionKey() throws Exception {
-        return generatePlatformDecryptionKey(GENERATION_ID);
-    }
-
-    private PlatformDecryptionKey generatePlatformDecryptionKey(int generationId) throws Exception {
-        return new PlatformDecryptionKey(generationId, generateAndroidKeyStoreKey());
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
index 880255d..9c03df8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
@@ -55,12 +55,15 @@
 
     private static final String TEST_KEY_1_ALIAS = "key1";
     private static final byte[] TEST_KEY_1_BYTES = new byte[] { 66, 77, 88 };
+    private static final byte[] TEST_KEY_1_METADATA = new byte[] { 89, 87 };
 
     private static final String TEST_KEY_2_ALIAS = "key2";
     private static final byte[] TEST_KEY_2_BYTES = new byte[] { 99, 33, 11 };
+    private static final byte[] TEST_KEY_2_METADATA = new byte[] {};
 
     private static final String TEST_KEY_3_ALIAS = "key3";
     private static final byte[] TEST_KEY_3_BYTES = new byte[] { 2, 8, 100 };
+    private static final byte[] TEST_KEY_3_METADATA = new byte[] { 121 };
 
     @Test
     public void roundTrip_persistsCounterId() throws Exception {
@@ -144,6 +147,17 @@
     }
 
     @Test
+    public void roundTripKeys_0_persistsKeyMetadata_absent() throws Exception {
+        assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(0).getMetadata()).isEqualTo(null);
+    }
+
+    @Test
+    public void roundTripKeys_0_persistsKeyMetadata_present() throws Exception {
+        assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(0).getMetadata())
+                .isEqualTo(TEST_KEY_1_METADATA);
+    }
+
+    @Test
     public void roundTripKeys_1_persistsAlias() throws Exception {
         assertThat(roundTripKeys().get(1).getAlias()).isEqualTo(TEST_KEY_2_ALIAS);
     }
@@ -154,6 +168,17 @@
     }
 
     @Test
+    public void roundTripKeys_1_persistsKeyMetadata_absent() throws Exception {
+        assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(1).getMetadata()).isEqualTo(null);
+    }
+
+    @Test
+    public void roundTripKeys_1_persistsKeyMetadata_present() throws Exception {
+        assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(1).getMetadata())
+                .isEqualTo(TEST_KEY_2_METADATA);
+    }
+
+    @Test
     public void roundTripKeys_2_persistsAlias() throws Exception {
         assertThat(roundTripKeys().get(2).getAlias()).isEqualTo(TEST_KEY_3_ALIAS);
     }
@@ -164,28 +189,74 @@
     }
 
     @Test
-    public void serialize_doesNotThrowForTestSnapshot() throws Exception {
+    public void roundTripKeys_2_persistsKeyMetadata_absent() throws Exception {
+        assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(2).getMetadata()).isEqualTo(null);
+    }
+
+    @Test
+    public void roundTripKeys_2_persistsKeyMetadata_present() throws Exception {
+        assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(2).getMetadata())
+                .isEqualTo(TEST_KEY_3_METADATA);
+    }
+
+    @Test
+    public void serialize_doesNotThrowForTestSnapshotWithoutKeyMetadata() throws Exception {
         KeyChainSnapshotSerializer.serialize(
-                createTestKeyChainSnapshot(), new ByteArrayOutputStream());
+                createTestKeyChainSnapshot(/*withKeyMetadata=*/ false),
+                new ByteArrayOutputStream());
+    }
+
+    @Test
+    public void serialize_doesNotThrowForTestSnapshotWithKeyMetadata() throws Exception {
+        KeyChainSnapshotSerializer.serialize(
+                createTestKeyChainSnapshotWithKeyMetadata(), new ByteArrayOutputStream());
     }
 
     private static List<WrappedApplicationKey> roundTripKeys() throws Exception {
         return roundTrip().getWrappedApplicationKeys();
     }
 
+    private static List<WrappedApplicationKey> roundTripKeys(boolean withKeyMetadata)
+            throws Exception {
+        return roundTrip(withKeyMetadata).getWrappedApplicationKeys();
+    }
+
     private static KeyChainProtectionParams roundTripParams() throws Exception {
-        return roundTrip().getKeyChainProtectionParams().get(0);
+        return roundTrip(/*withKeyMetadata=*/ false).getKeyChainProtectionParams().get(0);
     }
 
     public static KeyChainSnapshot roundTrip() throws Exception {
-        KeyChainSnapshot snapshot = createTestKeyChainSnapshot();
+        return roundTrip(/*withKeyMetadata=*/ false);
+    }
+
+    public static KeyChainSnapshot roundTrip(boolean withKeyMetadata) throws Exception {
+        KeyChainSnapshot snapshot = createTestKeyChainSnapshot(withKeyMetadata);
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         KeyChainSnapshotSerializer.serialize(snapshot, byteArrayOutputStream);
         return KeyChainSnapshotDeserializer.deserialize(
                 new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
     }
 
-    private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception {
+    private static KeyChainSnapshot createTestKeyChainSnapshot(boolean withKeyMetadata)
+            throws Exception {
+        KeyChainSnapshot.Builder builder = new KeyChainSnapshot.Builder()
+                .setCounterId(COUNTER_ID)
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setServerParams(SERVER_PARAMS)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+                .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+                .setTrustedHardwareCertPath(CERT_PATH);
+        if (withKeyMetadata) {
+            builder.setWrappedApplicationKeys(createKeysWithMetadata());
+        } else {
+            builder.setWrappedApplicationKeys(createKeysWithoutMetadata());
+        }
+        return builder.build();
+    }
+
+    private static KeyChainSnapshot createTestKeyChainSnapshotWithKeyMetadata()
+            throws Exception {
         return new KeyChainSnapshot.Builder()
                 .setCounterId(COUNTER_ID)
                 .setSnapshotVersion(SNAPSHOT_VERSION)
@@ -193,16 +264,24 @@
                 .setMaxAttempts(MAX_ATTEMPTS)
                 .setEncryptedRecoveryKeyBlob(KEY_BLOB)
                 .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
-                .setWrappedApplicationKeys(createKeys())
+                .setWrappedApplicationKeys(createKeysWithMetadata())
                 .setTrustedHardwareCertPath(CERT_PATH)
                 .build();
     }
 
-    private static List<WrappedApplicationKey> createKeys() {
+    private static List<WrappedApplicationKey> createKeysWithoutMetadata() {
         ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
-        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
-        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
-        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES, /*metadata=*/ null));
+        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES, /*metadata=*/ null));
+        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES, /*metadata=*/ null));
+        return keyList;
+    }
+
+    private static List<WrappedApplicationKey> createKeysWithMetadata() {
+        ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES, TEST_KEY_1_METADATA));
+        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES, TEST_KEY_2_METADATA));
+        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES, TEST_KEY_3_METADATA));
         return keyList;
     }
 
@@ -221,10 +300,13 @@
         return keyChainProtectionParamsList;
     }
 
-    private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
-        return new WrappedApplicationKey.Builder()
+    private static WrappedApplicationKey createKey(String alias, byte[] bytes, byte[] metadata) {
+        WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder()
                 .setAlias(alias)
-                .setEncryptedKeyMaterial(bytes)
-                .build();
+                .setEncryptedKeyMaterial(bytes);
+        if (metadata != null) {
+            builder.setMetadata(metadata);
+        }
+        return builder.build();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
index 7130b42..35215c3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -60,6 +60,7 @@
     private static final String TEST_ROOT_ALIAS = "root_cert_alias";
     private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
     private static final long TEST_CERT_SERIAL = 1000L;
+    private static final byte[] TEST_KEY_METADATA = "test-key-metadata".getBytes(UTF_8);
 
     private static final String SQL_CREATE_V2_TABLE_KEYS =
             "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
@@ -120,14 +121,14 @@
     @Test
     public void onCreate() throws Exception {
         mDatabaseHelper.onCreate(mDatabase);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     @Test
     public void onUpgrade_beforeV2() throws Exception {
         mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1,
                 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     @Test
@@ -135,11 +136,11 @@
         createV2Tables();
         mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2,
                 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     @Test
-    public void onUpgrade_v2_to_v3_to_v4() throws Exception {
+    public void onUpgrade_v2_to_v3_to_v4_to_latest() throws Exception {
         createV2Tables();
 
         assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table;
@@ -148,9 +149,12 @@
 
         assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table;
 
-        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3,
+        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, /*newVersion=*/ 4);
+        checkAllColumns_v4();
+
+        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 4,
                 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     private boolean isRootOfTrustTableAvailable() {
@@ -160,11 +164,11 @@
         values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS);
         values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
         values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
-        return mDatabase.insert(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
+        return mDatabase.replace(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
                 > -1;
     }
 
-    private void checkAllColumns() throws Exception {
+    private void checkAllColumns_v4() throws Exception {
         // Check the table containing encrypted application keys
         ContentValues values = new ContentValues();
         values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
@@ -175,7 +179,7 @@
         values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
         values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
         values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
-        assertThat(mDatabase.insert(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+        assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
                 .isGreaterThan(-1L);
 
         // Check the table about user metadata
@@ -183,7 +187,8 @@
         values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
         values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID,
                 TEST_PLATFORM_KEY_GENERATION_ID);
-        assertThat(mDatabase.insert(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+        assertThat(
+                mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
                 .isGreaterThan(-1L);
 
         // Check the table about recovery service metadata
@@ -202,11 +207,32 @@
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
         assertThat(
-                mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
+                mDatabase.replace(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
                         values))
                 .isGreaterThan(-1L);
 
         // Check the table about recovery service and root of trust data introduced in V4
         assertThat(isRootOfTrustTableAvailable()).isTrue();
     }
+
+    private void checkAllColumns_latest() throws Exception {
+        // Check all columns of the previous version first.
+        checkAllColumns_v4();
+
+        ContentValues values = new ContentValues();
+        values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+        values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
+        values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
+        values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
+        values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
+        values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
+        values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
+        values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
+
+        // This column is added when upgrading from v4 to v5
+        values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, TEST_KEY_METADATA);
+
+        assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+                .isGreaterThan(-1L);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 6a26f8c..7de9ffc 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -80,25 +80,33 @@
     public void insertKey_replacesOldKey() {
         int userId = 12;
         int uid = 10009;
-        String alias = "test";
-        WrappedKey oldWrappedKey = new WrappedKey(
-                getUtf8Bytes("nonce1"),
-                getUtf8Bytes("keymaterial1"),
-                /*platformKeyGenerationId=*/ 1);
-        mRecoverableKeyStoreDb.insertKey(
-                userId, uid, alias, oldWrappedKey);
-        byte[] nonce = getUtf8Bytes("nonce2");
-        byte[] keyMaterial = getUtf8Bytes("keymaterial2");
-        WrappedKey newWrappedKey = new WrappedKey(
-                nonce, keyMaterial, /*platformKeyGenerationId=*/2);
+        String alias = "test-alias";
 
-        mRecoverableKeyStoreDb.insertKey(
-                userId, uid, alias, newWrappedKey);
+        byte[] nonce = getUtf8Bytes("nonce1");
+        byte[] keyMaterial = getUtf8Bytes("keymaterial1");
+        byte[] keyMetadata = null;
+        int generationId = 1;
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
         assertArrayEquals(nonce, retrievedKey.getNonce());
         assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
-        assertEquals(2, retrievedKey.getPlatformKeyGenerationId());
+        assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata());
+        assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
+
+        nonce = getUtf8Bytes("nonce2");
+        keyMaterial = getUtf8Bytes("keymaterial2");
+        keyMetadata = getUtf8Bytes("keymetadata2");
+        generationId = 2;
+        wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+        retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+        assertArrayEquals(nonce, retrievedKey.getNonce());
+        assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
+        assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata());
+        assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
     }
 
     @Test
@@ -108,10 +116,12 @@
         WrappedKey key1 = new WrappedKey(
                 getUtf8Bytes("nonce1"),
                 getUtf8Bytes("key1"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 1);
         WrappedKey key2 = new WrappedKey(
                 getUtf8Bytes("nonce2"),
                 getUtf8Bytes("key2"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 1);
 
         mRecoverableKeyStoreDb.insertKey(userId, /*uid=*/ 1, alias, key1);
@@ -133,6 +143,7 @@
         WrappedKey key = new WrappedKey(
                 getUtf8Bytes("nonce1"),
                 getUtf8Bytes("key1"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 1);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, key);
 
@@ -158,13 +169,16 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, 120);
+        byte[] keyMetadata = getUtf8Bytes("keymetametametadata");
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, 120);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
 
         assertArrayEquals(nonce, retrievedKey.getNonce());
         assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
+        assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata());
         assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
         assertEquals(status,retrievedKey.getRecoveryStatus());
     }
@@ -174,20 +188,37 @@
         int userId = 12;
         int uid = 1009;
         int generationId = 6;
-        String alias = "test";
-        byte[] nonce = getUtf8Bytes("nonce");
-        byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId);
-        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+        String alias1 = "alias1";
+        byte[] nonce1 = getUtf8Bytes("nonce1");
+        byte[] keyMaterial1 = getUtf8Bytes("keymaterial1");
+        byte[] keyMetadata1 = getUtf8Bytes("keyallmetadata1");
+        WrappedKey wrappedKey1 = new WrappedKey(nonce1, keyMaterial1, keyMetadata1, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias1, wrappedKey1);
+
+        String alias2 = "alias2";
+        byte[] nonce2 = getUtf8Bytes("nonce2");
+        byte[] keyMaterial2 = getUtf8Bytes("keymaterial2");
+        byte[] keyMetadata2 = null;
+        WrappedKey wrappedKey2 = new WrappedKey(nonce2, keyMaterial2, keyMetadata2, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey2);
 
         Map<String, WrappedKey> keys = mRecoverableKeyStoreDb.getAllKeys(userId, uid, generationId);
+        assertEquals(2, keys.size());
 
-        assertEquals(1, keys.size());
-        assertTrue(keys.containsKey(alias));
-        WrappedKey retrievedKey = keys.get(alias);
-        assertArrayEquals(nonce, retrievedKey.getNonce());
-        assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
-        assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
+        assertTrue(keys.containsKey(alias1));
+        WrappedKey retrievedKey1 = keys.get(alias1);
+        assertArrayEquals(nonce1, retrievedKey1.getNonce());
+        assertArrayEquals(keyMaterial1, retrievedKey1.getKeyMaterial());
+        assertArrayEquals(keyMetadata1, retrievedKey1.getKeyMetadata());
+        assertEquals(generationId, retrievedKey1.getPlatformKeyGenerationId());
+
+        assertTrue(keys.containsKey(alias2));
+        WrappedKey retrievedKey2 = keys.get(alias2);
+        assertArrayEquals(nonce2, retrievedKey2.getNonce());
+        assertArrayEquals(keyMaterial2, retrievedKey2.getKeyMaterial());
+        assertArrayEquals(keyMetadata2, retrievedKey2.getKeyMetadata());
+        assertEquals(generationId, retrievedKey2.getPlatformKeyGenerationId());
     }
 
     @Test
@@ -197,6 +228,7 @@
         WrappedKey wrappedKey = new WrappedKey(
                 getUtf8Bytes("nonce"),
                 getUtf8Bytes("keymaterial"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 5);
         mRecoverableKeyStoreDb.insertKey(
                 userId, uid, /*alias=*/ "test", wrappedKey);
@@ -212,7 +244,8 @@
         int generationId = 12;
         int uid = 10009;
         WrappedKey wrappedKey = new WrappedKey(
-                getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), generationId);
+                getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), /*metadata=*/ null,
+                generationId);
         mRecoverableKeyStoreDb.insertKey(
                 /*userId=*/ 1, uid, /*alias=*/ "test", wrappedKey);
 
@@ -255,7 +288,10 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
@@ -279,12 +315,16 @@
         String alias3 = "test3";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
+        byte[] keyMetadata = null;
 
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey);
-        WrappedKey wrappedKey2 = new WrappedKey(nonce, keyMaterial, generationId, status);
-        mRecoverableKeyStoreDb.insertKey(userId, uid, alias3, wrappedKey);
-        WrappedKey wrappedKeyWithDefaultStatus = new WrappedKey(nonce, keyMaterial, generationId);
+        WrappedKey wrappedKey2 = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias3, wrappedKey2);
+        WrappedKey wrappedKeyWithDefaultStatus = new WrappedKey(nonce, keyMaterial, keyMetadata,
+                generationId);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKeyWithDefaultStatus);
 
         Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
@@ -333,7 +373,10 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
@@ -357,7 +400,10 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 1f5c64e..bc1f798 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -42,6 +42,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.IUidObserver;
+import android.app.Person;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -1588,6 +1589,14 @@
     }
 
     /**
+     * Make a Person.
+     */
+    protected Person makePerson(CharSequence name, String key, String uri) {
+        final Person.Builder builder = new Person.Builder();
+        return builder.setName(name).setKey(key).setUri(uri).build();
+    }
+
+    /**
      * Make an component name, with the client context.
      */
     @NonNull
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 517b5ad..6d28ed1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -35,6 +35,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.SuspendDialogInfo;
@@ -226,8 +227,8 @@
         settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
         // now read and verify
         settingsUnderTest.readPackageRestrictionsLPr(0);
-        final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1).
-                readUserState(0);
+        final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
+                .readUserState(0);
         assertThat(readPus1.suspended, is(true));
         assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
         assertThat(readPus1.dialogInfo, equalTo(dialogInfo1));
@@ -235,16 +236,16 @@
         assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
                 is(true));
 
-        final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2).
-                readUserState(0);
+        final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2)
+                .readUserState(0);
         assertThat(readPus2.suspended, is(true));
         assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
         assertThat(readPus2.dialogInfo, is(nullValue()));
         assertThat(readPus2.suspendedAppExtras, is(nullValue()));
         assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
 
-        final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
-                readUserState(0);
+        final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
+                .readUserState(0);
         assertThat(readPus3.suspended, is(false));
         assertThat(readPus3.suspendingPackage, is(nullValue()));
         assertThat(readPus3.dialogInfo, is(nullValue()));
@@ -254,11 +255,59 @@
 
     @Test
     public void testPackageRestrictionsSuspendedDefault() {
-        final PackageSetting defaultSetting =  createPackageSetting(PACKAGE_NAME_1);
+        final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
         assertThat(defaultSetting.getSuspended(0), is(false));
     }
 
     @Test
+    public void testReadWritePackageRestrictions_distractionFlags() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object());
+        final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+        final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+        final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
+
+        final int distractionFlags1 = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+        ps1.setDistractionFlags(distractionFlags1, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+
+        final int distractionFlags2 = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS
+                | PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+        ps2.setDistractionFlags(distractionFlags2, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+        final int distractionFlags3 = PackageManager.RESTRICTION_NONE;
+        ps3.setDistractionFlags(distractionFlags3, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
+
+        settingsUnderTest.writePackageRestrictionsLPr(0);
+
+        settingsUnderTest.mPackages.clear();
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
+        // now read and verify
+        settingsUnderTest.readPackageRestrictionsLPr(0);
+        final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
+                .readUserState(0);
+        assertThat(readPus1.distractionFlags, is(distractionFlags1));
+
+        final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2)
+                .readUserState(0);
+        assertThat(readPus2.distractionFlags, is(distractionFlags2));
+
+        final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
+                .readUserState(0);
+        assertThat(readPus3.distractionFlags, is(distractionFlags3));
+    }
+
+    @Test
+    public void testPackageRestrictionsDistractionFlagsDefault() {
+        final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
+        assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE));
+    }
+
+    @Test
     public void testEnableDisable() {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
@@ -692,6 +741,7 @@
         assertThat(userState.notLaunched, is(notLaunched));
         assertThat(userState.stopped, is(stopped));
         assertThat(userState.suspended, is(false));
+        assertThat(userState.distractionFlags, is(0));
         if (oldUserState != null) {
             assertThat(userState.equals(oldUserState), is(not(userStateChanged)));
         }
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 f0ed612..8eaf35f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -22,6 +22,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 
+import android.content.pm.PackageManager;
 import android.content.pm.PackageUserState;
 import android.content.pm.SuspendDialogInfo;
 import android.os.PersistableBundle;
@@ -227,4 +228,19 @@
         assertThat(testUserState1.equals(testUserState2), is(true));
     }
 
+    @Test
+    public void testPackageUserState06() {
+        final PackageUserState userState1 = new PackageUserState();
+        assertThat(userState1.distractionFlags, is(PackageManager.RESTRICTION_NONE));
+        userState1.distractionFlags = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+
+        final PackageUserState copyOfUserState1 = new PackageUserState(userState1);
+        assertThat(userState1.distractionFlags, is(copyOfUserState1.distractionFlags));
+        assertThat(userState1.equals(copyOfUserState1), is(true));
+
+        final PackageUserState userState2 = new PackageUserState(userState1);
+        userState2.distractionFlags = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS;
+        assertThat(userState1.equals(userState2), is(false));
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 9b59f91..8d0365b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -248,6 +248,8 @@
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setRank(123)
+                .setPerson(makePerson("person", "personKey", "personUri"))
+                .setLongLived()
                 .setExtras(pb)
                 .build();
         si.addFlags(ShortcutInfo.FLAG_PINNED);
@@ -267,9 +269,12 @@
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getRank());
+        assertEquals("person", si.getPersons()[0].getName());
+        assertEquals("personKey", si.getPersons()[0].getKey());
+        assertEquals("personUri", si.getPersons()[0].getUri());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
         assertEquals("abc", si.getBitmapPath());
         assertEquals(456, si.getIconResourceId());
 
@@ -345,6 +350,8 @@
                 .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setRank(123)
+                .setPerson(makePerson("person", "personKey", "personUri"))
+                .setLongLived()
                 .setExtras(pb)
                 .build();
         sorig.addFlags(ShortcutInfo.FLAG_PINNED);
@@ -368,9 +375,12 @@
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getRank());
+        assertEquals("person", si.getPersons()[0].getName());
+        assertEquals("personKey", si.getPersons()[0].getKey());
+        assertEquals("personUri", si.getPersons()[0].getUri());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
         assertEquals("abc", si.getBitmapPath());
         assertEquals(456, si.getIconResourceId());
         assertEquals("string/r456", si.getIconResName());
@@ -388,9 +398,12 @@
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getRank());
+        assertEquals("person", si.getPersons()[0].getName());
+        assertEquals("personKey", si.getPersons()[0].getKey());
+        assertEquals("personUri", si.getPersons()[0].getUri());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
@@ -408,9 +421,12 @@
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals(null, si.getIntent());
         assertEquals(123, si.getRank());
+        assertEquals("person", si.getPersons()[0].getName());
+        assertEquals("personKey", si.getPersons()[0].getKey());
+        assertEquals("personUri", si.getPersons()[0].getUri());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
@@ -428,9 +444,11 @@
         assertEquals(null, si.getCategories());
         assertEquals(null, si.getIntent());
         assertEquals(0, si.getRank());
+        assertEquals(null, si.getPersons());
         assertEquals(null, si.getExtras());
 
-        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY
+                | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
@@ -692,6 +710,12 @@
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setPerson(makePerson("person", "", "")).build());
+        assertEquals("text", si.getText());
+        assertEquals("person", si.getPersons()[0].getName());
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setIntent(makeIntent("action2", ShortcutActivity.class)).build());
         assertEquals("text", si.getText());
         assertEquals("action2", si.getIntent().getAction());
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 823b7a5..3ebc6ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -544,6 +544,18 @@
         switchUser(-1, UserHandle.of(startUser), false);
     }
 
+    public void testSwitchUserByHandle_ThrowsException() {
+        synchronized (mUserSwitchLock) {
+            try {
+                ActivityManager am = getContext().getSystemService(ActivityManager.class);
+                am.switchUser(null);
+                fail("Expected IllegalArgumentException on passing in a null UserHandle.");
+            } catch (IllegalArgumentException expected) {
+                // Do nothing - exception is expected.
+            }
+        }
+    }
+
     @MediumTest
     public void testConcurrentUserCreate() throws Exception {
         int userCount = mUserManager.getUserCount();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
index 3b6b48b..f817e8e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
@@ -77,7 +77,6 @@
 
     @Mock IPackageManager mPM;
     @Mock Installer mInstaller;
-    private final Object mInstallLock = new Object();
 
     private PackageDynamicCodeLoading mPackageDynamicCodeLoading;
     private DexLogger mDexLogger;
@@ -103,7 +102,7 @@
         };
 
         // For test purposes capture log messages as well as sending to the event log.
-        mDexLogger = new DexLogger(mPM, mInstaller, mInstallLock, mPackageDynamicCodeLoading) {
+        mDexLogger = new DexLogger(mPM, mInstaller, mPackageDynamicCodeLoading) {
             @Override
                 void writeDclEvent(int uid, String message) {
                     super.writeDclEvent(uid, message);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 813fa82..d9faaa4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.pm.dex;
 
-import static com.android.server.pm.PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -31,16 +29,16 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import dalvik.system.DelegateLastClassLoader;
 import dalvik.system.DexClassLoader;
 import dalvik.system.PathClassLoader;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -412,12 +410,6 @@
 
     @Test
     public void testEncodeClassLoader() {
-        assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
-                SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.PathClassLoader"));
-        assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
-                SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DexClassLoader"));
-        assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
-                SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DelegateLastClassLoader"));
         assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
                 "dalvik.system.PathClassLoader"));
         assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
@@ -435,15 +427,8 @@
 
     @Test
     public void testEncodeClassLoaderChain() {
-        assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain(
-                SKIP_SHARED_LIBRARY_CHECK, "PCL[a]"));
-        assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
-                SKIP_SHARED_LIBRARY_CHECK));
         assertEquals("PCL[a];DLC[b]", DexoptUtils.encodeClassLoaderChain("PCL[a]",
                 "DLC[b]"));
-        assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
-                SKIP_SHARED_LIBRARY_CHECK));
-
         try {
             DexoptUtils.encodeClassLoaderChain("a", null);
             fail(); // exception is expected
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 561c61f..0dff03f 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -47,6 +47,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.power.PowerManagerService.Injector;
 import com.android.server.power.PowerManagerService.NativeWrapper;
+import com.android.server.power.batterysaver.BatterySaverPolicy;
 import com.android.server.power.batterysaver.BatterySavingStats;
 
 import org.junit.Rule;
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
rename to services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index acf511e..73eefcf 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.power;
+package com.android.server.power.batterysaver;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -31,13 +31,12 @@
 import com.android.frameworks.servicestests.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
-import com.android.server.power.batterysaver.BatterySavingStats;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * Tests for {@link com.android.server.power.BatterySaverPolicy}
+ * Tests for {@link com.android.server.power.batterysaver.BatterySaverPolicy}
  */
 public class BatterySaverPolicyTest extends AndroidTestCase {
     private static final boolean BATTERY_SAVER_ON = true;
@@ -62,7 +61,7 @@
     private static final String BATTERY_SAVER_INCORRECT_CONSTANTS = "vi*,!=,,true";
 
     private class BatterySaverPolicyForTest extends BatterySaverPolicy {
-        public BatterySaverPolicyForTest(Object lock, Context context,
+        BatterySaverPolicyForTest(Object lock, Context context,
                 BatterySavingStats batterySavingStats) {
             super(lock, context, batterySavingStats);
         }
@@ -269,15 +268,15 @@
         mBatterySaverPolicy.onChange();
         assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString())
-                .isEqualTo("{/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq=123, " +
-                "/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq=456}");
+                .isEqualTo("{/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq=123, "
+                        + "/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq=456}");
 
         mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_3;
 
         mBatterySaverPolicy.onChange();
         assertThat(mBatterySaverPolicy.getFileValues(true).toString())
-                .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=333, " +
-                        "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=444}");
+                .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=333, "
+                        + "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=444}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString())
                 .isEqualTo("{/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq=222}");
 
@@ -287,9 +286,9 @@
 
         mBatterySaverPolicy.onChange();
         assertThat(mBatterySaverPolicy.getFileValues(true).toString())
-                .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=1234567890, " +
-                        "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=14, " +
-                        "/sys/devices/system/cpu/cpu5/cpufreq/scaling_max_freq=15}");
+                .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=1234567890, "
+                        + "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=14, "
+                        + "/sys/devices/system/cpu/cpu5/cpufreq/scaling_max_freq=15}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
     }
 }
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 41d5691..8171469 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -61,6 +61,7 @@
 import android.view.Display;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -416,6 +417,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 119774928)
     public void testEnabledState() throws Exception {
         TestParoleListener paroleListener = new TestParoleListener();
         mController.addListener(paroleListener);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 8496a96..b348aee 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -699,10 +699,52 @@
         assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
     }
 
+    /** Verify the timeout message is delivered at the right time after past usage was reported */
+    @Test
+    public void testAppUsageObserver_PastUsage() throws Exception {
+        setTime(10_000L);
+        addAppUsageObserver(OBS_ID1, GROUP1, 6_000L);
+        setTime(20_000L);
+        startPastUsage(PKG_SOC1, 5_000);
+        setTime(21_000L);
+        assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Verify that the observer was removed
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /**
+     * Verify the timeout message is delivered at the right time after past usage was reported
+     * that overlaps with already known usage
+     */
+    @Test
+    public void testAppUsageObserver_PastUsageOverlap() throws Exception {
+        setTime(0L);
+        addAppUsageObserver(OBS_ID1, GROUP1, 20_000L);
+        setTime(10_000L);
+        startUsage(PKG_SOC1);
+        setTime(20_000L);
+        stopUsage(PKG_SOC1);
+        setTime(25_000L);
+        startPastUsage(PKG_SOC1, 9_000);
+        setTime(26_000L);
+        // the 4 seconds of overlapped usage should not be counted
+        assertFalse(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        setTime(30_000L);
+        assertTrue(mLimitReachedLatch.await(4_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Verify that the observer was removed
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
     private void startUsage(String packageName) {
         mController.noteUsageStart(packageName, USER_ID);
     }
 
+    private void startPastUsage(String packageName, int timeAgo) {
+        mController.noteUsageStart(packageName, USER_ID, timeAgo);
+    }
+
     private void stopUsage(String packageName) {
         mController.noteUsageStop(packageName, USER_ID);
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 8b65e76..20f72bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -613,7 +613,7 @@
     }
 
     @Test
-    public void testGetAllowedPackages() throws Exception {
+    public void testGetAllowedPackages_byUser() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
                     mIpm, approvalLevel);
@@ -681,6 +681,30 @@
     }
 
     @Test
+    public void testGetAllowedPackages() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+        loadXml(service);
+        service.mApprovalLevel = APPROVAL_BY_PACKAGE;
+        loadXml(service);
+
+        List<String> allowedPackages = new ArrayList<>();
+        allowedPackages.add("this.is.a.package.name");
+        allowedPackages.add("another.package");
+        allowedPackages.add("secondary");
+        allowedPackages.add("this.is.another.package");
+        allowedPackages.add("package");
+        allowedPackages.add("component");
+        allowedPackages.add("bananas!");
+
+        Set<String> actual = service.getAllowedPackages();
+        assertEquals(allowedPackages.size(), actual.size());
+        for (String pkg : allowedPackages) {
+            assertTrue(actual.contains(pkg));
+        }
+    }
+
+    @Test
     public void testOnUserRemoved() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
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 83c1c76..94b21af 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3511,9 +3511,9 @@
     }
 
     @Test
-    public void testAppOverlay() throws Exception {
-        mBinderService.setAppOverlaysAllowed(PKG, mUid, false);
-        assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid));
+    public void testBubble() throws Exception {
+        mBinderService.setBubblesAllowed(PKG, mUid, false);
+        assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid));
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 65e640f..b9ae7d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -56,8 +56,8 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.R;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -87,14 +87,7 @@
     private final String channelId = "channel";
     private NotificationChannel channel =
             new NotificationChannel(channelId, "test", NotificationManager.IMPORTANCE_DEFAULT);
-    private final String channelIdLong =
-            "give_a_developer_a_string_argument_and_who_knows_what_they_will_pass_in_there";
     private final String groupId = "group";
-    private final String groupIdOverride = "other_group";
-    private final String groupIdLong =
-            "0|com.foo.bar|g:content://com.foo.bar.ui/account%3A-0000000/account/";
-    private NotificationChannel channelLongId =
-            new NotificationChannel(channelIdLong, "long", NotificationManager.IMPORTANCE_DEFAULT);
     private NotificationChannel defaultChannel =
             new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "test",
                     NotificationManager.IMPORTANCE_UNSPECIFIED);
@@ -408,73 +401,27 @@
     }
 
     @Test
-    public void testLogmakerShortChannel() {
+    public void testLogMaker() {
+        long timestamp = 1000L;
         StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        final LogMaker logMaker = record.getLogMaker();
+        final LogMaker logMaker = record.getLogMaker(timestamp);
+
+        assertNull(logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX));
         assertEquals(channelId,
                 (String) logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID));
         assertEquals(channel.getImportance(),
                 logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE));
-    }
-
-    @Test
-    public void testLogmakerLongChannel() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-        true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-        false /* lights */, false /*defaultLights */, null /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channelLongId);
-        final String loggedId = (String)
-            record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID);
-        assertEquals(channelIdLong.substring(0,10), loggedId.substring(0, 10));
-    }
-
-    @Test
-    public void testLogmakerNoGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /*defaultLights */, null /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        assertNull(record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-    }
-
-    @Test
-    public void testLogmakerShortGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, groupId /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        assertEquals(groupId,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-    }
-
-    @Test
-    public void testLogmakerLongGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, groupIdLong /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        final String loggedId = (String)
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID);
-        assertEquals(groupIdLong.substring(0,10), loggedId.substring(0, 10));
-    }
-
-    @Test
-    public void testLogmakerOverrideGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, groupId /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        assertEquals(groupId,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-        record.setOverrideGroupKey(groupIdOverride);
-        assertEquals(groupIdOverride,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-        record.setOverrideGroupKey(null);
-        assertEquals(groupId,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+        assertEquals(record.getLifespanMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
+        assertEquals(record.getFreshnessMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
+        assertEquals(record.getExposureMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
+        assertEquals(record.getInterruptionMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS));
     }
 
     @Test
@@ -825,4 +772,72 @@
 
         assertNotEquals(-1, record.getLastAudiblyAlertedMs());
     }
+
+    @Test
+    public void testIsNewEnoughForAlerting_new() {
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue(record.isNewEnoughForAlerting(record.mUpdateTimeMs));
+    }
+
+    @Test
+    public void testIsNewEnoughForAlerting_old() {
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse(record.isNewEnoughForAlerting(record.mUpdateTimeMs + (1000 * 60 * 60)));
+    }
+
+    @Test
+    public void testIgnoreImportanceAdjustmentsForOemLockedChannels() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setImportanceLockedByOEM(true);
+
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
+
+        Bundle bundle = new Bundle();
+        bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+        Adjustment adjustment = new Adjustment(
+                PKG_O, record.getKey(), bundle, "", record.getUserId());
+
+        record.addAdjustment(adjustment);
+        record.applyAdjustments();
+        record.calculateImportance();
+
+        assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
+    }
+
+    @Test
+    public void testApplyImportanceAdjustmentsForNonOemLockedChannels() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setImportanceLockedByOEM(false);
+
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
+
+        Bundle bundle = new Bundle();
+        bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+        Adjustment adjustment = new Adjustment(
+                PKG_O, record.getKey(), bundle, "", record.getUserId());
+
+        record.addAdjustment(adjustment);
+        record.applyAdjustments();
+        record.calculateImportance();
+
+        assertEquals(IMPORTANCE_LOW, record.getImportance());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0b73481..24a1f8c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -809,7 +809,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
-        channel.setAllowAppOverlay(false);
+        channel.setAllowBubbles(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -826,7 +826,7 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
+        assertEquals(channel.canBubble(), savedChannel.canBubble());
 
         verify(mHandler, never()).requestSort();
     }
@@ -840,7 +840,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
-        channel.setAllowAppOverlay(false);
+        channel.setAllowBubbles(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -857,7 +857,7 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
+        assertEquals(channel.canBubble(), savedChannel.canBubble());
     }
 
     @Test
@@ -969,16 +969,16 @@
     }
 
     @Test
-    public void testLockFields_appOverlay() {
+    public void testLockFields_allowBubble() {
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
         assertEquals(0,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
                         .getUserLockedFields());
 
         final NotificationChannel update = getChannel();
-        update.setAllowAppOverlay(false);
+        update.setAllowBubbles(false);
         mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true);
-        assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY,
+        assertEquals(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false)
                         .getUserLockedFields());
     }
@@ -2161,30 +2161,147 @@
     }
 
     @Test
-    public void testAllowAppOverlay_defaults() throws Exception {
-        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+    public void testAllowBubbles_defaults() throws Exception {
+        assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         loadStreamXml(baos, false);
 
-        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+        assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O));
         assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
     }
 
     @Test
-    public void testAllowAppOverlay_xml() throws Exception {
-        mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false);
-        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
-        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+    public void testAllowBubbles_xml() throws Exception {
+        mHelper.setBubblesAllowed(PKG_O, UID_O, false);
+        assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         loadStreamXml(baos, false);
 
-        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
-        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+        assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
                 mHelper.getAppLockedFields(PKG_O, UID_O));
     }
+
+    @Test
+    public void testLockChannelsForOEM_emptyList() {
+        mHelper.lockChannelsForOEM(null);
+        mHelper.lockChannelsForOEM(new String[0]);
+        // no exception
+    }
+
+    @Test
+    public void testLockChannelsForOEM_appWide() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+        // different uids, same package
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+        mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
+        mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_onlyGivenPkg() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, 30, b, false, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+        assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 30, b.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_channelSpecific() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+        // different uids, same package
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+        mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
+        mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
+
+        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_channelDoesNotExistYet_appWide() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+
+        mHelper.createNotificationChannel(PKG_O, 3, b, true, false);
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_channelDoesNotExistYet_channelSpecific() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":a", PKG_O + ":b"});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
+                .isImportanceLockedByOEM());
+
+        mHelper.createNotificationChannel(PKG_O, UID_O, b, true, false);
+        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testUpdateNotificationChannel_oemLockedImportance() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE);
+        update.setAllowBubbles(false);
+
+        mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
+
+        assertEquals(IMPORTANCE_HIGH,
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
+        assertEquals(false,
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble());
+
+        mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
+
+        assertEquals(IMPORTANCE_HIGH,
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 5638cb36..c79e1db0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,8 +15,11 @@
  */
 package com.android.server.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 
+import static junit.framework.TestCase.assertEquals;
+
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -153,7 +156,7 @@
                 .build();
         mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("B")
@@ -163,7 +166,7 @@
                 .build();
         mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("C")
@@ -171,7 +174,7 @@
                 .build();
         mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("D")
@@ -179,7 +182,7 @@
                 .build();
         mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("E")
@@ -188,7 +191,7 @@
                 .build();
         mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mAudioAttributes = new AudioAttributes.Builder()
                 .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -197,11 +200,16 @@
                 .build();
     }
 
-    private NotificationChannel getDefaultChannel() {
+    private NotificationChannel getLowChannel() {
         return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
                 IMPORTANCE_LOW);
     }
 
+    private NotificationChannel getDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+                IMPORTANCE_DEFAULT);
+    }
+
     @Test
     public void testSortShouldRespectCritical() throws Exception {
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(7);
@@ -285,4 +293,40 @@
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
         mHelper.sort(notificationList);
     }
+    
+    @Test
+    public void testGroupNotifications_highestIsProxy() {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<>();
+        // this should be the last in the list, except it's in a group with a high child
+        Notification lowSummaryN = new Notification.Builder(mContext, "")
+                .setGroup("group")
+                .setGroupSummary(true)
+                .build();
+        NotificationRecord lowSummary = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "summary", 0, 0, lowSummaryN, USER,
+                null, System.currentTimeMillis()), getLowChannel());
+        notificationList.add(lowSummary);
+
+        Notification lowN = new Notification.Builder(mContext, "").build();
+        NotificationRecord low = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "low", 0, 0, lowN, USER,
+                null, System.currentTimeMillis()), getLowChannel());
+        low.setContactAffinity(0.5f);
+        notificationList.add(low);
+
+        Notification highChildN = new Notification.Builder(mContext, "")
+                .setGroup("group")
+                .setGroupSummary(false)
+                .build();
+        NotificationRecord highChild = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "child", 0, 0, highChildN, USER,
+                null, System.currentTimeMillis()), getDefaultChannel());
+        notificationList.add(highChild);
+
+        mHelper.sort(notificationList);
+
+        assertEquals(lowSummary, notificationList.get(0));
+        assertEquals(highChild, notificationList.get(1));
+        assertEquals(low, notificationList.get(2));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 49f134f..174c5fa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -299,6 +299,59 @@
         assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
     }
 
+    @Test
+    public void testClearData() {
+        // snooze 2 from same package
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+
+        // clear data
+        mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+        // nothing snoozed; alarms canceled
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+        // twice for initial snooze, twice for canceling the snooze
+        verify(mAm, times(4)).cancel(any(PendingIntent.class));
+    }
+
+    @Test
+    public void testClearData_otherRecordsUntouched() {
+        // 2 packages, 2 users
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
+        NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        mSnoozeHelper.snooze(r3, 1000);
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+
+        // clear data
+        mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+        // once for each initial snooze, once for canceling one snooze
+        verify(mAm, times(4)).cancel(any(PendingIntent.class));
+    }
+
     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
             UserHandle user, String groupKey, boolean groupSummary) {
         Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
index b315e51..efefee1 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
@@ -16,6 +16,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -26,6 +27,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.Log;
 import android.util.Xml.Encoding;
 
 import com.android.server.UiServiceTestCase;
@@ -46,10 +48,12 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class SlicePermissionManagerTest extends UiServiceTestCase {
+    private static final String TAG = "SlicePerManTest";
 
     @Test
     public void testGrant() {
-        File sliceDir = new File(mContext.getDataDir(), "system/slices");
+        File sliceDir = new File(mContext.getCacheDir(), "testGrantSlices");
+        Log.v(TAG, "testGrant: slice permissions stored in " + sliceDir.getAbsolutePath());
         SlicePermissionManager permissions = new SlicePermissionManager(mContext,
                 TestableLooper.get(this).getLooper(), sliceDir);
         Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
@@ -59,11 +63,15 @@
         permissions.grantSliceAccess("my.pkg", 0, "provider.pkg", 0, uri);
 
         assertTrue(permissions.hasPermission("my.pkg", 0, uri));
+
+        // Cleanup.
+        assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
     }
 
     @Test
     public void testBackup() throws XmlPullParserException, IOException {
-        File sliceDir = new File(mContext.getDataDir(), "system/slices");
+        File sliceDir = new File(mContext.getCacheDir(), "testBackupSlices");
+        Log.v(TAG, "testBackup: slice permissions stored in " + sliceDir.getAbsolutePath());
         Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority("authority")
                 .path("something").build();
@@ -90,7 +98,10 @@
                 TestableLooper.get(this).getLooper());
         permissions.readRestore(parser);
 
-        assertTrue(permissions.hasFullAccess("com.android.mypkg", 10));
+        if (!permissions.hasFullAccess("com.android.mypkg", 10)) {
+            fail("com.android.mypkg@10 did not have full access. backup file: "
+                    + output.toString());
+        }
         assertTrue(permissions.hasPermission("com.android.otherpkg", 0,
                 ContentProvider.maybeAddUserId(uri, 1)));
         permissions.removePkg("com.android.lastpkg", 1);
@@ -102,8 +113,9 @@
     }
 
     @Test
-    public void testInvalid() throws Exception {
-        File sliceDir = new File(mContext.getCacheDir(), "slices-test");
+    public void testInvalid() {
+        File sliceDir = new File(mContext.getCacheDir(), "testInvalidSlices");
+        Log.v(TAG, "testInvalid: slice permissions stored in " + sliceDir.getAbsolutePath());
         if (!sliceDir.exists()) {
             sliceDir.mkdir();
         }
@@ -118,7 +130,8 @@
 
             @Override
             public void writeTo(XmlSerializer out) throws IOException {
-                throw new RuntimeException("this doesn't work");
+                throw new RuntimeException("this RuntimeException inside junk.writeTo() "
+                        + "should be caught and suppressed by surrounding code");
             }
         };
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 5bf3d2d..56f4a85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -36,6 +36,9 @@
 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.eq;
+import static org.mockito.Mockito.doAnswer;
 
 import android.platform.test.annotations.Presubmit;
 
@@ -54,11 +57,6 @@
 @Presubmit
 public class ActivityDisplayTests extends ActivityTestsBase {
 
-    @Before
-    public void setUp() throws Exception {
-        setupActivityTaskManagerService();
-    }
-
     @Test
     public void testLastFocusedStackIsUpdatedWhenMovingStack() {
         // Create a stack at bottom.
@@ -277,4 +275,60 @@
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
         assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1));
     }
+
+    @Test
+    public void testRemoveStackInWindowingModes() {
+        removeStackTests(() -> mRootActivityContainer.removeStacksInWindowingModes(
+                WINDOWING_MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void testRemoveStackWithActivityTypes() {
+        removeStackTests(
+                () -> mRootActivityContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD));
+    }
+
+    private void removeStackTests(Runnable runnable) {
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack stack1 = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final ActivityStack stack2 = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final ActivityStack stack3 = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack(
+                stack1).setTaskId(1).build();
+        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(
+                stack2).setTaskId(2).build();
+        final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack(
+                stack3).setTaskId(3).build();
+        final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack(
+                stack4).setTaskId(4).build();
+
+        // Reordering stacks while removing stacks.
+        doAnswer(invocation -> {
+            display.positionChildAtTop(stack3, false);
+            return true;
+        }).when(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(),
+                any());
+
+        // Removing stacks from the display while removing stacks.
+        doAnswer(invocation -> {
+            display.removeChild(stack2);
+            return true;
+        }).when(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(),
+                any());
+
+        runnable.run();
+        verify(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(),
+                any());
+        verify(mSupervisor).removeTaskByIdLocked(eq(task3.taskId), anyBoolean(), anyBoolean(),
+                any());
+        verify(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(),
+                any());
+        verify(mSupervisor).removeTaskByIdLocked(eq(task1.taskId), anyBoolean(), anyBoolean(),
+                any());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index cac9cf6..ee22861 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -70,8 +70,6 @@
 
     @Before
     public void setUpAMLO() throws Exception {
-        setupActivityTaskManagerService();
-
         mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
 
         // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index b6f1817..8be63fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -19,7 +19,12 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+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.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
@@ -36,10 +41,14 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.ActivityOptions;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PauseActivityItem;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
 import android.util.MutableBoolean;
 
 import androidx.test.filters.MediumTest;
@@ -63,8 +72,7 @@
 
     @Before
     public void setUp() throws Exception {
-        setupActivityTaskManagerService();
-        mStack = new StackBuilder(mRootActivityContainer).build();
+        mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
         mTask = mStack.getChildAt(0);
         mActivity = mTask.getTopActivity();
     }
@@ -210,4 +218,149 @@
         assertNull(mActivity.pendingOptions);
         assertNotNull(activity2.pendingOptions);
     }
+
+    @Test
+    public void testNewOverrideConfigurationIncrementsSeq() {
+        final Configuration newConfig = new Configuration();
+
+        final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
+        mActivity.onRequestedOverrideConfigurationChanged(newConfig);
+        assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+    }
+
+    @Test
+    public void testNewParentConfigurationIncrementsSeq() {
+        final Configuration newConfig = new Configuration(
+                mTask.getRequestedOverrideConfiguration());
+        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+
+        final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
+        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+        assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+    }
+
+    @Test
+    public void testNotifiesSeqIncrementToAppToken() {
+        final Configuration appWindowTokenRequestedOrientation = mock(Configuration.class);
+        mActivity.mAppWindowToken = mock(AppWindowToken.class);
+        doReturn(appWindowTokenRequestedOrientation).when(mActivity.mAppWindowToken)
+                .getRequestedOverrideConfiguration();
+
+        final Configuration newConfig = new Configuration();
+        newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
+
+        final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
+        mActivity.onRequestedOverrideConfigurationChanged(newConfig);
+        assertEquals(prevSeq + 1, appWindowTokenRequestedOrientation.seq);
+        verify(mActivity.mAppWindowToken).onMergedOverrideConfigurationChanged();
+    }
+
+    @Test
+    public void testSetsRelaunchReason_NotDragResizing() {
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+
+        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
+        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                mActivity.getConfiguration()));
+
+        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+
+        mActivity.ensureActivityConfiguration(0, false, false);
+
+        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE,
+                mActivity.mRelaunchReason);
+    }
+
+    @Test
+    public void testSetsRelaunchReason_DragResizing() {
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+
+        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
+        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                mActivity.getConfiguration()));
+
+        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+        doReturn(true).when(mTask.getTask()).isDragResizing();
+
+        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+
+        mActivity.ensureActivityConfiguration(0, false, false);
+
+        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE,
+                mActivity.mRelaunchReason);
+    }
+
+    @Test
+    public void testSetsRelaunchReason_NonResizeConfigChanges() {
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+
+        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
+        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                mActivity.getConfiguration()));
+
+        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
+        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        newConfig.fontScale = 5;
+        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+        mActivity.mRelaunchReason =
+                ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+
+        mActivity.ensureActivityConfiguration(0, false, false);
+
+        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE,
+                mActivity.mRelaunchReason);
+    }
+
+    @Test
+    public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+
+        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
+        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                mActivity.getConfiguration()));
+
+        mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
+        final Configuration newConfig = new Configuration(mActivity.getConfiguration());
+        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_LANDSCAPE
+                : Configuration.ORIENTATION_PORTRAIT;
+
+        // Mimic the behavior that display doesn't handle app's requested orientation.
+        doAnswer(invocation -> {
+            mTask.onConfigurationChanged(newConfig);
+            return null;
+        }).when(mActivity.mAppWindowToken).setOrientation(anyInt(), any(), any());
+
+        final int requestedOrientation;
+        switch (newConfig.orientation) {
+            case Configuration.ORIENTATION_LANDSCAPE:
+                requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+                break;
+            case Configuration.ORIENTATION_PORTRAIT:
+                requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+                break;
+            default:
+                throw new IllegalStateException("Orientation in new config should be either"
+                        + "landscape or portrait.");
+        }
+        mActivity.setRequestedOrientation(requestedOrientation);
+
+        final ActivityConfigurationChangeItem expected =
+                ActivityConfigurationChangeItem.obtain(newConfig);
+        verify(mService.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()),
+                eq(mActivity.appToken), eq(expected));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index f7b5d26..59e71c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -64,7 +64,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setupActivityTaskManagerService();
         mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index b2a2869..35c1ede 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -36,6 +36,8 @@
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -73,7 +75,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setupActivityTaskManagerService();
         mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
         mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */));
@@ -264,13 +265,13 @@
 
         // Do not move display to back because there is still another stack.
         stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
-        verify(stack2.getWindowContainerController()).positionChildAtBottom(any(),
+        verify(stack2.getTaskStack()).positionChildAtBottom(any(),
                 eq(false) /* includingParents */);
 
         // Also move display to back because there is only one stack left.
         display.removeChild(stack1);
         stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
-        verify(stack2.getWindowContainerController()).positionChildAtBottom(any(),
+        verify(stack2.getTaskStack()).positionChildAtBottom(any(),
                 eq(true) /* includingParents */);
     }
 
@@ -687,6 +688,62 @@
     }
 
     @Test
+    public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+        activity.launchCount = 1;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertEquals(1, mTask.mActivities.size());
+        assertEquals(1, mStack.getAllTasks().size());
+    }
+
+    @Test
+    public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+        activity.launchCount = 3;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertThat(mTask.mActivities).isEmpty();
+        assertThat(mStack.getAllTasks()).isEmpty();
+    }
+
+    @Test
+    public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
+        activity.launchCount = 1;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertEquals(1, mTask.mActivities.size());
+        assertEquals(1, mStack.getAllTasks().size());
+    }
+
+    @Test
+    public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
+        activity.launchCount = 3;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertThat(mTask.mActivities).isEmpty();
+        assertThat(mStack.getAllTasks()).isEmpty();
+    }
+
+    @Test
     public void testFinishCurrentActivity() {
         // Create 2 activities on a new display.
         final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index 2ba2fdb..96db38b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -56,7 +56,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mService = createActivityTaskManagerService();
         mFactory = mock(Factory.class);
         mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
         mStarter = spy(new ActivityStarter(mController, mService,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 7a6b2b5..a381023 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -117,7 +117,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setupActivityTaskManagerService();
         mController = mock(ActivityStartController.class);
         mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
         clearInvocations(mActivityMetricsLogger);
@@ -143,7 +142,7 @@
                 .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .build();
-        assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
+        assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class);
         mStarter.updateBounds(task2, bounds);
 
         verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
@@ -643,10 +642,10 @@
             boolean hasForegroundActivities, boolean callerIsRecents,
             boolean callerIsTempWhitelisted) {
         // window visibility
-        doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
-                callingUid);
-        doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager)
-                .isAnyWindowVisibleForUid(realCallingUid);
+        doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+                .isAnyNonToastWindowVisibleForUid(callingUid);
+        doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+                .isAnyNonToastWindowVisibleForUid(realCallingUid);
         // process importance
         doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid);
         doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 569c6d4..68df87e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -31,6 +31,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -62,11 +63,11 @@
 import android.view.DisplayInfo;
 
 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.am.ActivityManagerService;
 import com.android.server.am.PendingIntentController;
+import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.uri.UriGrantsManagerInternal;
 
@@ -110,22 +111,19 @@
     @Before
     public void setUpBase() {
         mTestInjector.setUp();
+
+        mService = new TestActivityTaskManagerService(mContext);
+        mSupervisor = mService.mStackSupervisor;
+        mRootActivityContainer = mService.mRootActivityContainer;
     }
 
     @After
     public void tearDownBase() {
         mTestInjector.tearDown();
-    }
-
-    ActivityTaskManagerService createActivityTaskManagerService() {
-        mService = new TestActivityTaskManagerService(mContext);
-        mSupervisor = mService.mStackSupervisor;
-        mRootActivityContainer = mService.mRootActivityContainer;
-        return mService;
-    }
-
-    void setupActivityTaskManagerService() {
-        createActivityTaskManagerService();
+        if (mService != null) {
+            mService.setWindowManager(null);
+            mService = null;
+        }
     }
 
     /** Creates a {@link TestActivityDisplay}. */
@@ -144,6 +142,13 @@
         return display;
     }
 
+    /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+    TestActivityDisplay addNewActivityDisplayAt(DisplayInfo info, int position) {
+        final TestActivityDisplay display = createNewActivityDisplay(info);
+        mRootActivityContainer.addChild(display, position);
+        return display;
+    }
+
     /**
      * Builder for creating new activities.
      */
@@ -154,6 +159,7 @@
         private final ActivityTaskManagerService mService;
 
         private ComponentName mComponent;
+        private String mTargetActivity;
         private TaskRecord mTaskRecord;
         private int mUid;
         private boolean mCreateTask;
@@ -170,6 +176,11 @@
             return this;
         }
 
+        ActivityBuilder setTargetActivity(String targetActivity) {
+            mTargetActivity = targetActivity;
+            return this;
+        }
+
         static ComponentName getDefaultComponent() {
             return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
                     DEFAULT_COMPONENT_PACKAGE_NAME);
@@ -224,6 +235,10 @@
             aInfo.applicationInfo = new ApplicationInfo();
             aInfo.applicationInfo.packageName = mComponent.getPackageName();
             aInfo.applicationInfo.uid = mUid;
+            aInfo.packageName = mComponent.getPackageName();
+            if (mTargetActivity != null) {
+                aInfo.targetActivity = mTargetActivity;
+            }
             aInfo.flags |= mActivityFlags;
             aInfo.launchMode = mLaunchMode;
 
@@ -234,7 +249,13 @@
                     mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
             spyOn(activity);
             activity.mAppWindowToken = mock(AppWindowToken.class);
+            doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
+            doCallRealMethod().when(activity.mAppWindowToken)
+                    .setOrientation(anyInt(), any(), any());
+            doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
             doNothing().when(activity).removeWindowContainer();
+            doReturn(mock(Configuration.class)).when(activity.mAppWindowToken)
+                    .getRequestedOverrideConfiguration();
 
             if (mTaskRecord != null) {
                 mTaskRecord.addActivityToTop(activity);
@@ -346,6 +367,7 @@
                 mStack.addTask(task, true, "creating test task");
                 task.setStack(mStack);
                 task.setTask();
+                mStack.getTaskStack().addChild(task.mTask, 0);
             }
 
             task.touchActiveTime();
@@ -365,7 +387,10 @@
                 setTask();
             }
 
-            private void setTask() {
+            void setTask() {
+                Task mockTask = mock(Task.class);
+                mockTask.mTaskRecord = this;
+                doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
                 setTask(mock(Task.class));
             }
         }
@@ -597,7 +622,7 @@
 
         @SuppressWarnings("TypeParameterUnusedInFormals")
         @Override
-        <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+        ActivityStack createStackUnchecked(int windowingMode, int activityType,
                 int stackId, boolean onTop) {
             return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
                     .setWindowingMode(windowingMode).setActivityType(activityType)
@@ -619,7 +644,13 @@
         }
     }
 
+    private static WindowManagerService sMockWindowManagerService;
+
     private static WindowManagerService prepareMockWindowManager() {
+        if (sMockWindowManagerService != null) {
+            return sMockWindowManagerService;
+        }
+
         final WindowManagerService service = mock(WindowManagerService.class);
         service.mRoot = mock(RootWindowContainer.class);
 
@@ -631,6 +662,7 @@
             return null;
         }).when(service).inSurfaceTransaction(any());
 
+        sMockWindowManagerService = service;
         return service;
     }
 
@@ -639,10 +671,9 @@
      * method is called. Note that its functionality depends on the implementations of the
      * construction arguments.
      */
-    protected static class TestActivityStack<T extends StackWindowController>
-            extends ActivityStack<T> {
+    protected static class TestActivityStack
+            extends ActivityStack {
         private int mOnActivityRemovedFromStackCount = 0;
-        private T mContainerController;
 
         static final int IS_TRANSLUCENT_UNSET = 0;
         static final int IS_TRANSLUCENT_FALSE = 1;
@@ -682,20 +713,20 @@
         }
 
         @Override
-        protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-            mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController();
+        protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
+            mTaskStack = WindowTestUtils.createMockTaskStack();
 
             // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
             // will be moved to the full screen stack.
             if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 outBounds.set(0, 0, 100, 100);
             }
-            return mContainerController;
+
         }
 
         @Override
-        T getWindowContainerController() {
-            return mContainerController;
+        TaskStack getTaskStack() {
+            return mTaskStack;
         }
 
         void setIsTranslucent(boolean isTranslucent) {
@@ -785,27 +816,23 @@
         }
 
         @SuppressWarnings("TypeParameterUnusedInFormals")
-        <T extends ActivityStack> T build() {
+        ActivityStack build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
             if (mWindowingMode == WINDOWING_MODE_PINNED) {
-                return (T) new PinnedActivityStack(mDisplay, stackId,
-                        mRootActivityContainer.mStackSupervisor, mOnTop) {
+                return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor,
+                        mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
                     @Override
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
                     }
 
                     @Override
-                    PinnedStackWindowController createStackWindowController(int displayId,
-                            boolean onTop, Rect outBounds) {
-                        PinnedStackWindowController controller =
-                                mock(PinnedStackWindowController.class);
-                        controller.mContainer = mock(TaskStack.class);
-                        return controller;
+                    void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
+                        mTaskStack = mock(TaskStack.class);
                     }
                 };
             } else {
-                return (T) new TestActivityStack(mDisplay, stackId,
+                return new TestActivityStack(mDisplay, stackId,
                         mRootActivityContainer.mStackSupervisor, mWindowingMode,
                         mActivityType, mOnTop, mCreateActivity);
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index fa42289..123de2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,7 +39,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -52,15 +54,34 @@
 @Presubmit
 public class AppTransitionTests extends WindowTestsBase {
 
+    private static RootWindowContainer sOriginalRootWindowContainer;
+
     private DisplayContent mDc;
 
+    @BeforeClass
+    public static void setUpRootWindowContainerMock() {
+        final WindowManagerService wm = TestSystemServices.getWindowManagerService();
+        // For unit test, we don't need to test performSurfacePlacement to prevent some abnormal
+        // interaction with surfaceflinger native side.
+        sOriginalRootWindowContainer = wm.mRoot;
+        // Creating spied mock of RootWindowContainer shouldn't be done in @Before, since it will
+        // create unnecessary nested spied objects chain, because WindowManagerService object under
+        // test is a single instance shared among all tests that extend WindowTestsBase class.
+        // Instead it should be done once before running all tests in this test class.
+        wm.mRoot = spy(wm.mRoot);
+        doNothing().when(wm.mRoot).performSurfacePlacement(anyBoolean());
+    }
+
+    @AfterClass
+    public static void tearDownRootWindowContainerMock() {
+        final WindowManagerService wm = TestSystemServices.getWindowManagerService();
+        wm.mRoot = sOriginalRootWindowContainer;
+        sOriginalRootWindowContainer = null;
+    }
+
     @Before
     public void setUp() throws Exception {
         mDc = mWm.getDefaultDisplayContentLocked();
-        // For unit test,  we don't need to test performSurfacePlacement to prevent some
-        // abnormal interaction with surfaceflinger native side.
-        mWm.mRoot = spy(mWm.mRoot);
-        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
     }
 
     @Test
@@ -160,7 +181,7 @@
         assertTrue(dc1.mOpeningApps.size() > 0);
 
         // Move stack to another display.
-        stack1.getController().reparent(dc2.getDisplayId(),  new Rect(), true);
+        stack1.reparent(dc2.getDisplayId(),  new Rect(), true);
 
         // Verify if token are cleared from both pending transition list in former display.
         assertFalse(dc1.mOpeningApps.contains(token1));
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index dcfb879..d0b9225 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -87,8 +87,8 @@
         verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
 
         callbackCaptor.getValue().onAnimationFinished(mSpec);
-        verify(mTransaction).destroy(eq(leash));
-        verify(mTransaction).destroy(eq(animationBoundsLayer));
+        verify(mTransaction).reparent(eq(leash), eq(null));
+        verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null));
         assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
     }
 
@@ -100,8 +100,8 @@
         final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
 
         mToken.mSurfaceAnimator.cancelAnimation();
-        verify(mTransaction).destroy(eq(leash));
-        verify(mTransaction).destroy(eq(animationBoundsLayer));
+        verify(mTransaction).reparent(eq(leash), eq(null));
+        verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null));
         assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index ee1c8df..f99cd4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -212,7 +212,7 @@
         mDimmer.updateDims(mTransaction, new Rect());
         verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
                 SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
-        verify(mHost.getPendingTransaction()).destroy(dimLayer);
+        verify(mHost.getPendingTransaction()).reparent(dimLayer, null);
     }
 
     @Test
@@ -269,7 +269,7 @@
         mDimmer.updateDims(mTransaction, new Rect());
         verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
                 SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
-        verify(mTransaction).destroy(dimLayer);
+        verify(mTransaction).reparent(dimLayer, null);
     }
 
     private SurfaceControl getDimLayer() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3826fac..f399463 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -59,6 +59,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.view.DisplayCutout;
+import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -584,15 +585,17 @@
 
     @Test
     public void testOnDescendantOrientationRequestChanged() {
-        final DisplayContent dc = createNewDisplay();
+        final DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = 1080;
+        info.logicalHeight = 1920;
+        info.logicalDensityDpi = 240;
+        final DisplayContent dc = createNewDisplay(info);
+        dc.configureDisplayPolicy();
         mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
-        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
-                ? SCREEN_ORIENTATION_PORTRAIT
-                : SCREEN_ORIENTATION_LANDSCAPE;
 
         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
         window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
 
         ActivityRecord activityRecord = mock(ActivityRecord.class);
 
@@ -603,21 +606,22 @@
         verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
                 same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
         final Configuration newDisplayConfig = captor.getValue();
-        assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE, newDisplayConfig.orientation);
     }
 
     @Test
     public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
-        final DisplayContent dc = createNewDisplay();
+        final DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = 1080;
+        info.logicalHeight = 1920;
+        info.logicalDensityDpi = 240;
+        final DisplayContent dc = createNewDisplay(info);
         dc.getDisplayRotation().setFixedToUserRotation(true);
         mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
-        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
-                ? SCREEN_ORIENTATION_PORTRAIT
-                : SCREEN_ORIENTATION_LANDSCAPE;
 
         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
         window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
 
         ActivityRecord activityRecord = mock(ActivityRecord.class);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 6b31e6f..d05711e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -33,7 +33,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.ContentResolver;
@@ -63,6 +62,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -86,7 +86,7 @@
 
     private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
 
-    private WindowManagerService mMockWm;
+    private static WindowManagerService sMockWm;
     private DisplayContent mMockDisplayContent;
     private DisplayPolicy mMockDisplayPolicy;
     private Context mMockContext;
@@ -108,13 +108,16 @@
 
     private DisplayRotation mTarget;
 
+    @BeforeClass
+    public static void setUpOnce() {
+        sMockWm = mock(WindowManagerService.class);
+        sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+    }
+
     @Before
     public void setUp() {
         FakeSettingsProvider.clearSettingsProvider();
 
-        mMockWm = mock(WindowManagerService.class);
-        mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
-
         mPreviousStatusBarManagerInternal = LocalServices.getService(
                 StatusBarManagerInternal.class);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -452,7 +455,7 @@
         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
         assertTrue(waitForUiHandler());
 
-        verify(mMockWm).updateRotation(false, false);
+        verify(sMockWm).updateRotation(false, false);
     }
 
     @Test
@@ -608,23 +611,6 @@
     // ========================
     // Non-rotation API Tests
     // ========================
-    @Test
-    public void testRespectsAppRequestedOrientationByDefault() throws Exception {
-        mBuilder.build();
-
-        assertTrue("Display rotation should respect app requested orientation by"
-                + " default.", mTarget.respectAppRequestedOrientation());
-    }
-
-    @Test
-    public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
-        mBuilder.build();
-        mTarget.setFixedToUserRotation(true);
-
-        assertFalse("Display rotation shouldn't respect app requested orientation if"
-                + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
-    }
-
     /**
      * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
      * according to given parameters.
@@ -833,8 +819,9 @@
                     .thenReturn(mFakeSettingsProvider.getIContentProvider());
 
             mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
-            mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+            mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy,
                     mMockDisplayWindowSettings, mMockContext, new Object());
+            reset(sMockWm);
 
             captureObservers();
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index f1c6eab..68d8bc0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -20,11 +20,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 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.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -96,8 +94,8 @@
     private WindowState createDropTargetWindow(String name, int ownerId) {
         final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken(
                 mDisplayContent);
-        final TaskStack stack = createStackControllerOnStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+        final TaskStack stack = createTaskStackOnDisplay(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         final Task task = createTaskInStack(stack, ownerId);
         task.addChild(token, 0);
 
@@ -125,7 +123,6 @@
         mDisplayContent = spy(mDisplayContent);
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
-        when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
 
         synchronized (mWm.mGlobalLock) {
             mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
@@ -176,7 +173,6 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
-            assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
             mToken = mTarget.performDrag(
                     new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
                     data);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 8c3dec7..b28ae40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -71,7 +71,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mService = createActivityTaskManagerService();
         mPersister = new TestLaunchParamsPersister();
         mController = new LaunchParamsController(mService, mPersister);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 8635364..aeda473 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -102,8 +102,6 @@
         mFolder = new File(cacheFolder, "launch_params_tests");
         deleteRecursively(mFolder);
 
-        setupActivityTaskManagerService();
-
         mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
         final DisplayInfo info = new DisplayInfo();
         info.uniqueId = mDisplayUniqueId;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index a9f150b..e24eb75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -34,7 +34,12 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -47,10 +52,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
@@ -115,6 +116,7 @@
 
     private LockTaskController mLockTaskController;
     private Context mContext;
+    private String mPackageName;
     private String mLockToAppSetting;
 
     @Before
@@ -122,6 +124,7 @@
         MockitoAnnotations.initMocks(this);
 
         mContext = getInstrumentation().getTargetContext();
+        mPackageName = mContext.getPackageName();
         mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
 
@@ -146,6 +149,7 @@
 
     @After
     public void tearDown() throws Exception {
+        mLockTaskController.setWindowManager(null);
         Settings.Secure.putString(mContext.getContentResolver(),
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
     }
@@ -223,7 +227,7 @@
         // THEN lock task mode should be started
         verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
         // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
+        verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
     }
 
     @Test
@@ -390,9 +394,9 @@
         // THEN lock task mode should have been finished
         verifyLockTaskStopped(times(1));
         // THEN the keyguard should be shown
-        verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
+        verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL));
         // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
+        verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */));
     }
 
     @Test
@@ -509,9 +513,9 @@
                 & ~DISABLE_HOME;
         int expectedFlags2 = DISABLE2_MASK;
         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
 
         // reset invocation counter
         reset(mStatusBarService);
@@ -526,9 +530,9 @@
         expectedFlags2 = DISABLE2_MASK
                 & ~DISABLE2_NOTIFICATION_SHADE;
         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
     }
 
     @Test
@@ -548,9 +552,9 @@
 
         // THEN status bar shouldn't change
         verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
     }
 
     @Test
@@ -657,14 +661,14 @@
         verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
         // THEN the status bar should have been disabled
         verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         // THEN recents should have been notified
         verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
         // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
-                TEST_USER_ID);
+        verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME),
+                eq(TEST_USER_ID));
     }
 
     private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
@@ -672,11 +676,12 @@
         verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
         // THEN the status bar should have been disabled
         verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
+                any(IBinder.class), eq(mPackageName));
         verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
+                any(IBinder.class), eq(mPackageName));
         // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
+        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(),
+                eq(TEST_USER_ID));
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
index efd7d25..beaac8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
@@ -52,7 +52,6 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mService = createActivityTaskManagerService();
         mService.mH.runWithScissors(() -> {
             mHandler = new TestHandler(null, mClock);
         }, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index ad2a708..413b6f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -143,8 +143,8 @@
 
     @Test
     public void testTimeout_scaled() throws Exception {
-        mWm.setAnimationScale(2, 5.0f);
         try {
+            mWm.setAnimationScale(2, 5.0f);
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                     "testWin");
             final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 58302d6..e3bacf9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -82,7 +82,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setupActivityTaskManagerService();
         mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
     }
@@ -323,8 +322,8 @@
     public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
         // Create stack/task on default display.
         final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
-        final TestActivityStack targetStack =
-                new StackBuilder(mRootActivityContainer).setOnTop(false).build();
+        final TestActivityStack targetStack = (TestActivityStack) new StackBuilder(
+                mRootActivityContainer).setOnTop(false).build();
         final TaskRecord targetTask = targetStack.getChildAt(0);
 
         // Create Recents on top of the display.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..45fe5d2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for RootWindowContainer.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+public class RootWindowContainerTests extends WindowTestsBase {
+
+    private static final int FAKE_CALLING_UID = 667;
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_oneToastOneNonToastBothVisible() {
+        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+        toastyToast.mHasSurface = true;
+        app.mHasSurface = true;
+
+        assertTrue(toastyToast.isVisible());
+        assertTrue(app.isVisible());
+        assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_onlyToastVisible() {
+        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+        toastyToast.mHasSurface = true;
+
+        assertTrue(toastyToast.isVisible());
+        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() {
+        final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar", FAKE_CALLING_UID);
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+
+        assertFalse(topBar.isVisible());
+        assertFalse(app.isVisible());
+        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+}
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index a8b6dc3..dc96480 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -49,7 +49,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setupActivityTaskManagerService();
         mRunningTasks = new RunningTasks();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 36eccd1..03aba39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -33,6 +33,8 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -58,7 +60,6 @@
 import android.view.WindowManager;
 import android.widget.TextView;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -80,8 +81,8 @@
 @Presubmit
 public class ScreenDecorWindowTests {
 
-    private final Context mContext = InstrumentationRegistry.getTargetContext();
-    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private final Context mContext = getInstrumentation().getTargetContext();
+    private final Instrumentation mInstrumentation = getInstrumentation();
 
     private WindowManager mWm;
     private ArrayList<View> mWindows = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
deleted file mode 100644
index 5690b58..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
+++ /dev/null
@@ -1,105 +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.wm;
-
-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 android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link StackWindowController}.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:StackWindowControllerTests
- */
-@SmallTest
-@Presubmit
-public class StackWindowControllerTests extends WindowTestsBase {
-    @Test
-    public void testRemoveContainer() {
-        final StackWindowController stackController =
-                createStackControllerOnDisplay(mDisplayContent);
-        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
-
-        final TaskStack stack = stackController.mContainer;
-        assertNotNull(stack);
-        assertNotNull(task);
-        stackController.removeContainer();
-        // Assert that the container was removed.
-        assertNull(stackController.mContainer);
-        assertNull(stack.getDisplayContent());
-        assertNull(task.getDisplayContent());
-        assertNull(task.mStack);
-    }
-
-    @Test
-    public void testRemoveContainer_deferRemoval() {
-        final StackWindowController stackController =
-                createStackControllerOnDisplay(mDisplayContent);
-        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
-
-        final TaskStack stack = stackController.mContainer;
-        // Stack removal is deferred if one of its child is animating.
-        task.setLocalIsAnimating(true);
-
-        stackController.removeContainer();
-        // For the case of deferred removal the stack controller will no longer be connected to the
-        // container, but the task controller will still be connected to the its container until
-        // the stack window container is removed.
-        assertNull(stackController.mContainer);
-        assertNull(stack.getController());
-        assertNotNull(task);
-
-        stack.removeImmediately();
-        // After removing, the task will be isolated.
-        assertNull(task.getParent());
-        assertEquals(task.getChildCount(), 0);
-        assertNull(task.getController());
-    }
-
-    @Test
-    public void testReparent() {
-        // Create first stack on primary display.
-        final StackWindowController stack1Controller =
-                createStackControllerOnDisplay(mDisplayContent);
-        final TaskStack stack1 = stack1Controller.mContainer;
-        final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1Controller);
-        task1.mOnDisplayChangedCalled = false;
-
-        // Create second display and put second stack on it.
-        final DisplayContent dc = createNewDisplay();
-        final StackWindowController stack2Controller =
-                createStackControllerOnDisplay(dc);
-        final TaskStack stack2 = stack2Controller.mContainer;
-
-        // Reparent
-        stack1Controller.reparent(dc.getDisplayId(), new Rect(), true /* onTop */);
-        assertEquals(dc, stack1.getDisplayContent());
-        final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
-        final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
-        assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
-        assertTrue(task1.mOnDisplayChangedCalled);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index d14f30d..ad80cd6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -96,7 +96,7 @@
         callbackCaptor.getValue().onAnimationFinished(mSpec);
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+        verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
         // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
     }
 
@@ -106,7 +106,7 @@
         final SurfaceControl firstLeash = mAnimatable.mLeash;
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
 
-        verify(mTransaction).destroy(eq(firstLeash));
+        verify(mTransaction).reparent(eq(firstLeash), eq(null));
         assertFalse(mAnimatable.mFinishedCallbackCalled);
 
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -133,7 +133,7 @@
         assertNotAnimating(mAnimatable);
         verify(mSpec).onAnimationCancelled(any());
         assertTrue(mAnimatable.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+        verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
     }
 
     @Test
@@ -155,7 +155,7 @@
         verifyZeroInteractions(mSpec);
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+        verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
     }
 
     @Test
@@ -171,11 +171,11 @@
         assertNotAnimating(mAnimatable);
         assertAnimating(mAnimatable2);
         assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
-        verify(mTransaction, never()).destroy(eq(leash));
+        verify(mTransaction, never()).reparent(eq(leash), eq(null));
         callbackCaptor.getValue().onAnimationFinished(mSpec);
         assertNotAnimating(mAnimatable2);
         assertTrue(mAnimatable2.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(leash));
+        verify(mTransaction).reparent(eq(leash), eq(null));
     }
 
     @Test
@@ -198,7 +198,7 @@
         mDeferFinishAnimatable.mEndDeferFinishCallback.run();
         assertNotAnimating(mAnimatable2);
         assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash));
+        verify(mTransaction).reparent(eq(mDeferFinishAnimatable.mLeash), eq(null));
     }
 
     private void assertAnimating(MyAnimatable animatable) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 53e99fa..ace179a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -75,10 +75,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setupActivityTaskManagerService();
-        mService.mSupportsFreeformWindowManagement = true;
-        when(mSupervisor.canUseActivityOptionsLaunchBounds(any())).thenCallRealMethod();
-
         mActivity = new ActivityBuilder(mService).build();
         mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
         mActivity.info.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index c343fe7..8c6ac23 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -18,7 +18,6 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -58,10 +57,6 @@
         assertNotNull(mWm.mTaskPositioningController);
         mTarget = mWm.mTaskPositioningController;
 
-        when(mWm.mInputManager.transferTouchFocus(
-                any(InputChannel.class),
-                any(InputChannel.class))).thenReturn(true);
-
         mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
         mWindow.mInputChannel = new InputChannel();
         synchronized (mWm.mGlobalLock) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 7da85af..e182c45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -21,6 +21,18 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.sameInstance;
@@ -41,6 +53,7 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.Xml;
 import android.view.DisplayInfo;
+import android.view.Surface;
 
 import androidx.test.filters.MediumTest;
 
@@ -77,7 +90,6 @@
     @Before
     public void setUp() throws Exception {
         TaskRecord.setTaskRecordFactory(null);
-        setupActivityTaskManagerService();
         mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
     }
 
@@ -133,17 +145,6 @@
         assertTrue(task.returnsToHomeStack());
     }
 
-    /** Ensures that bounds are clipped to their parent. */
-    @Test
-    public void testAppBounds_BoundsClipping() {
-        final Rect shiftedBounds = new Rect(mParentBounds);
-        shiftedBounds.offset(10, 10);
-        final Rect expectedBounds = new Rect(mParentBounds);
-        expectedBounds.intersect(shiftedBounds);
-        testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
-                expectedBounds);
-    }
-
     /** Ensures that empty bounds are not propagated to the configuration. */
     @Test
     public void testAppBounds_EmptyBounds() {
@@ -167,36 +168,201 @@
         final Rect insetBounds = new Rect(mParentBounds);
         insetBounds.inset(5, 5, 5, 5);
         testStackBoundsConfiguration(
-                WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
+                WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
     }
 
-    /** Ensures that full screen free form bounds are clipped */
+    /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
     @Test
-    public void testAppBounds_FullScreenFreeFormBounds() {
+    public void testBoundsOnModeChangeFreeformToFullscreen() {
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+        ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        TaskRecord task = stack.getChildAt(0);
+        task.getRootActivity().mAppWindowToken.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
         DisplayInfo info = new DisplayInfo();
         display.mDisplay.getDisplayInfo(info);
         final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
-        testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
-                mParentBounds);
+        final Rect freeformBounds = new Rect(fullScreenBounds);
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        task.setBounds(freeformBounds);
+
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN inherits bounds
+        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(fullScreenBounds, task.getBounds());
+        assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
+
+        // FREEFORM restores bounds
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
+    }
+
+    /**
+     * This is a temporary hack to trigger an onConfigurationChange at the task level after an
+     * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
+     * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
+     * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
+     */
+    private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
+        activity.setRequestedOrientation(orientation);
+        ConfigurationContainer taskRecord = activity.getParent();
+        taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
+    }
+
+    /**
+     * Tests that a task with forced orientation has orientation-consistent bounds within the
+     * parent.
+     */
+    @Test
+    public void testFullscreenBoundsForcedOrientation() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+        final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+        DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = fullScreenBounds.width();
+        info.logicalHeight = fullScreenBounds.height();
+        ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
+        assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
+        // Override display orientation. Normally this is available via DisplayContent, but DC
+        // is mocked-out.
+        display.getRequestedOverrideConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
+        display.onRequestedOverrideConfigurationChanged(
+                display.getRequestedOverrideConfiguration());
+        ActivityStack stack = new StackBuilder(mRootActivityContainer)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+        TaskRecord task = stack.getChildAt(0);
+        ActivityRecord root = task.getTopActivity();
+        assertEquals(root, task.getTopActivity());
+
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed portrait fits within parent
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(root, task.getRootActivity());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+        assertTrue(task.getBounds().width() < task.getBounds().height());
+        assertEquals(fullScreenBounds.height(), task.getBounds().height());
+
+        // Top activity gets used
+        ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
+        assertEquals(top, task.getTopActivity());
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(task.getBounds().width(), fullScreenBounds.width());
+
+        // Setting app to unspecified restores
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED);
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed landscape and changing display
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
+        // simulate display orientation changing (normally done via DisplayContent)
+        display.getRequestedOverrideConfiguration().orientation =
+                Configuration.ORIENTATION_PORTRAIT;
+        display.setBounds(fullScreenBoundsPort);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+        // in FREEFORM, no constraint
+        final Rect freeformBounds = new Rect(display.getBounds());
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.setBounds(freeformBounds);
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN letterboxes bounds
+        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+        // FREEFORM restores bounds as before
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
+    }
+
+    @Test
+    public void testUpdatesForcedOrientationInBackground() {
+        final DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = 1920;
+        info.logicalHeight = 1080;
+        final ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
+        doCallRealMethod().when(display.mDisplayContent).setDisplayRotation(any());
+        display.mDisplayContent.setDisplayRotation(mock(DisplayRotation.class));
+        doCallRealMethod().when(display.mDisplayContent).onDescendantOrientationChanged(any(),
+                any());
+        doCallRealMethod().when(display.mDisplayContent).setRotation(anyInt());
+        doAnswer(invocation -> {
+            display.mDisplayContent.setRotation(Surface.ROTATION_0);
+            return null;
+        }).when(display.mDisplayContent).updateOrientationFromAppTokens(any(), any(), anyBoolean());
+
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+        final TaskRecord task = stack.getChildAt(0);
+        final ActivityRecord activity = task.getRootActivity();
+
+        // Wire up app window token and task.
+        doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt(), any(), any());
+        doCallRealMethod().when(activity.mAppWindowToken).onDescendantOrientationChanged(any(),
+                any());
+        doReturn(task.mTask).when(activity.mAppWindowToken).getParent();
+
+        // Wire up task and stack.
+        task.mTask.mTaskRecord = task;
+        doCallRealMethod().when(task.mTask).onDescendantOrientationChanged(any(), any());
+        doReturn(stack.getTaskStack()).when(task.mTask).getParent();
+
+        // Wire up stack and display content.
+        doCallRealMethod().when(stack.mTaskStack).onDescendantOrientationChanged(any(), any());
+        doReturn(display.mDisplayContent).when(stack.mTaskStack).getParent();
+
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        assertTrue("Bounds of the task should be pillarboxed.",
+                task.getBounds().width() < task.getBounds().height());
+
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+        assertTrue("Bounds of the task should be fullscreen.",
+                task.getBounds().equals(new Rect(0, 0, 1920, 1080)));
     }
 
     /** Ensures that the alias intent won't have target component resolved. */
     @Test
     public void testTaskIntentActivityAlias() {
-        final String aliasActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
-        final String targetActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+        final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
+        final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+        final ComponentName aliasComponent =
+                new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
+        final ComponentName targetComponent =
+                new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
+
         final Intent intent = new Intent();
-        intent.setComponent(new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity));
+        intent.setComponent(aliasComponent);
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
         info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
-        info.targetActivity = targetActivity;
+        info.targetActivity = targetClassName;
 
         final TaskRecord task = TaskRecord.create(mService, 1 /* taskId */, info, intent,
                 null /* taskDescription */);
-        assertEquals("The alias activity component should be saved in task intent.", aliasActivity,
+        assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
                 task.intent.getComponent().getClassName());
+
+        ActivityRecord aliasActivity = new ActivityBuilder(mService).setComponent(
+                aliasComponent).setTargetActivity(targetClassName).build();
+        assertEquals("Should be the same intent filter.", true,
+                task.isSameIntentFilter(aliasActivity));
+
+        ActivityRecord targetActivity = new ActivityBuilder(mService).setComponent(
+                targetComponent).build();
+        assertEquals("Should be the same intent filter.", true,
+                task.isSameIntentFilter(targetActivity));
+
+        ActivityRecord defaultActivity = new ActivityBuilder(mService).build();
+        assertEquals("Should not be the same intent filter.", false,
+                task.isSameIntentFilter(defaultActivity));
     }
 
     private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 624ef9b..ca815ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -39,6 +39,7 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Surface;
+import android.view.SurfaceControl;
 
 import androidx.test.filters.SmallTest;
 
@@ -65,7 +66,7 @@
         final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer,
                 ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
                 WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
-        mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test",
+        mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
                 Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
                 ORIENTATION_PORTRAIT);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index f01e9f0..74ccccc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -47,8 +47,8 @@
 
     @Before
     public void setUp() throws Exception {
-        mPinnedStack = createStackControllerOnStackOnDisplay(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+        mPinnedStack = createTaskStackOnDisplay(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         // Stack should contain visible app window to be considered visible.
         final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
         assertFalse(mPinnedStack.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 7ac3318..2554237 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -20,8 +20,12 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -106,4 +110,62 @@
         assertNull(stack.getDisplayContent());
         assertNull(task.mStack);
     }
+
+    @Test
+    public void testRemoveContainer() {
+        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+
+        assertNotNull(stack);
+        assertNotNull(task);
+        stack.removeIfPossible();
+        // Assert that the container was removed.
+        assertNull(stack.getParent());
+        assertEquals(0, stack.getChildCount());
+        assertNull(stack.getDisplayContent());
+        assertNull(task.getDisplayContent());
+        assertNull(task.mStack);
+    }
+
+    @Test
+    public void testRemoveContainer_deferRemoval() {
+        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+
+        // Stack removal is deferred if one of its child is animating.
+        task.setLocalIsAnimating(true);
+
+        stack.removeIfPossible();
+        // For the case of deferred removal the task controller will still be connected to the its
+        // container until the stack window container is removed.
+        assertNotNull(stack.getParent());
+        assertNotEquals(0, stack.getChildCount());
+        assertNotNull(task);
+
+        stack.removeImmediately();
+        // After removing, the task will be isolated.
+        assertNull(task.getParent());
+        assertEquals(0, task.getChildCount());
+        assertNull(task.getController());
+    }
+
+    @Test
+    public void testReparent() {
+        // Create first stack on primary display.
+        final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1);
+        task1.mOnDisplayChangedCalled = false;
+
+        // Create second display and put second stack on it.
+        final DisplayContent dc = createNewDisplay();
+        final TaskStack stack2 = createTaskStackOnDisplay(dc);
+
+        // Reparent
+        stack1.reparent(dc.getDisplayId(), new Rect(), true /* onTop */);
+        assertEquals(dc, stack1.getDisplayContent());
+        final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
+        final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
+        assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
+        assertTrue(task1.mOnDisplayChangedCalled);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 3e32ed3..ae211d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -40,8 +40,7 @@
 
     @Test
     public void testRemoveContainer() {
-        final StackWindowController stackController1 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
         final WindowTestUtils.TestAppWindowToken appToken =
                 WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
@@ -49,14 +48,13 @@
         task.removeIfPossible();
         // Assert that the container was removed.
         assertNull(task.getParent());
-        assertEquals(task.getChildCount(), 0);
+        assertEquals(0, task.getChildCount());
         assertNull(appToken.getParent());
     }
 
     @Test
     public void testRemoveContainer_deferRemoval() {
-        final StackWindowController stackController1 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
         final WindowTestUtils.TestAppWindowToken appToken =
                 WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
@@ -67,22 +65,20 @@
         // For the case of deferred removal the task will still be connected to the its app token
         // until the task window container is removed.
         assertNotNull(task.getParent());
-        assertNotEquals(task.getChildCount(), 0);
+        assertNotEquals(0, task.getChildCount());
         assertNotNull(appToken.getParent());
 
         task.removeImmediately();
         assertNull(task.getParent());
-        assertEquals(task.getChildCount(), 0);
+        assertEquals(0, task.getChildCount());
         assertNull(appToken.getParent());
     }
 
     @Test
     public void testReparent() {
-        final StackWindowController stackController1 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
-        final StackWindowController stackController2 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController2 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2);
 
         boolean gotException = false;
@@ -93,20 +89,17 @@
         }
         assertTrue("Should not be able to reparent to the same parent", gotException);
 
-        final StackWindowController stackController3 =
-                createStackControllerOnDisplay(mDisplayContent);
-        stackController3.setContainer(null);
         gotException = false;
         try {
-            task.reparent(stackController3, 0, false/* moveParents */);
+            task.reparent(null, 0, false/* moveParents */);
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
-        assertTrue("Should not be able to reparent to a stack that doesn't have a container",
+        assertTrue("Should not be able to reparent to a stack that doesn't exist",
                 gotException);
 
         task.reparent(stackController2, 0, false/* moveParents */);
-        assertEquals(stackController2.mContainer, task.getParent());
+        assertEquals(stackController2, task.getParent());
         assertEquals(0, task.positionInParent());
         assertEquals(1, task2.positionInParent());
     }
@@ -114,20 +107,17 @@
     @Test
     public void testReparent_BetweenDisplays() {
         // Create first stack on primary display.
-        final StackWindowController stack1Controller =
-                createStackControllerOnDisplay(mDisplayContent);
-        final TaskStack stack1 = stack1Controller.mContainer;
-        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1Controller);
+        final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1);
         task.mOnDisplayChangedCalled = false;
         assertEquals(mDisplayContent, stack1.getDisplayContent());
 
         // Create second display and put second stack on it.
         final DisplayContent dc = createNewDisplay();
-        final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
-        final TaskStack stack2 = stack2Controller.mContainer;
-        final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2Controller);
+        final TaskStack stack2 = createTaskStackOnDisplay(dc);
+        final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2);
         // Reparent and check state
-        task.reparent(stack2Controller, 0, false /* moveParents */);
+        task.reparent(stack2, 0, false /* moveParents */);
         assertEquals(stack2, task.getParent());
         assertEquals(0, task.positionInParent());
         assertEquals(1, task2.positionInParent());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java b/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java
new file mode 100644
index 0000000..b151fb7
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.InputChannel;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.LocalServices;
+import com.android.server.LockGuard;
+import com.android.server.Watchdog;
+import com.android.server.input.InputManagerService;
+import com.android.server.policy.WindowManagerPolicy;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.quality.Strictness;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A Test utility class to create a mock {@link WindowManagerService} instance for tests.
+ */
+class TestSystemServices {
+    private static StaticMockitoSession sMockitoSession;
+    private static WindowManagerService sService;
+    private static TestWindowManagerPolicy sPolicy;
+
+    static void setUpWindowManagerService() {
+        sMockitoSession = mockitoSession()
+                .spyStatic(LockGuard.class)
+                .spyStatic(Watchdog.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+
+        runWithDexmakerShareClassLoader(TestSystemServices::setUpTestWindowService);
+    }
+
+    static void tearDownWindowManagerService() {
+        waitUntilWindowManagerHandlersIdle();
+        removeLocalServices();
+        sService = null;
+        sPolicy = null;
+
+        sMockitoSession.finishMocking();
+    }
+
+    private static void setUpTestWindowService() {
+        doReturn(null).when(() -> LockGuard.installLock(any(), anyInt()));
+        doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
+
+        final Context context = getInstrumentation().getTargetContext();
+        spyOn(context);
+
+        doReturn(null).when(context)
+                .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
+        doReturn(null).when(context)
+                .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
+                        any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
+
+        final ContentResolver contentResolver = context.getContentResolver();
+        spyOn(contentResolver);
+        doNothing().when(contentResolver)
+                .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
+                        anyInt());
+
+        final AppOpsManager appOpsManager = mock(AppOpsManager.class);
+        doReturn(appOpsManager).when(context)
+                .getSystemService(eq(Context.APP_OPS_SERVICE));
+
+        removeLocalServices();
+
+        final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
+        LocalServices.addService(DisplayManagerInternal.class, dmi);
+
+        final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
+        LocalServices.addService(PowerManagerInternal.class, pmi);
+        final PowerSaveState state = new PowerSaveState.Builder().build();
+        doReturn(state).when(pmi).getLowPowerState(anyInt());
+
+        final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, ami);
+
+        final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
+        LocalServices.addService(ActivityTaskManagerInternal.class, atmi);
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Runnable runnable = invocationOnMock.getArgument(0);
+            if (runnable != null) {
+                runnable.run();
+            }
+            return null;
+        }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
+
+        final InputManagerService ims = mock(InputManagerService.class);
+        // InputChannel is final and can't be mocked.
+        final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
+        if (input != null && input.length > 1) {
+            doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+        }
+
+        final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+        final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
+        doReturn(wmLock).when(atms).getGlobalLock();
+
+        sPolicy = new TestWindowManagerPolicy(TestSystemServices::getWindowManagerService);
+        sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms);
+
+        sService.onInitReady();
+
+        final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        // Display creation is driven by the ActivityManagerService via
+        // ActivityStackSupervisor. We emulate those steps here.
+        sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
+    }
+
+    private static void removeLocalServices() {
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+        LocalServices.removeServiceForTest(WindowManagerInternal.class);
+        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+    }
+
+    static WindowManagerService getWindowManagerService() {
+        return sService;
+    }
+
+    static void cleanupWindowManagerHandlers() {
+        final WindowManagerService wm = getWindowManagerService();
+        if (wm == null) {
+            return;
+        }
+        wm.mH.removeCallbacksAndMessages(null);
+        wm.mAnimationHandler.removeCallbacksAndMessages(null);
+        SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
+    }
+
+    static void waitUntilWindowManagerHandlersIdle() {
+        final WindowManagerService wm = getWindowManagerService();
+        if (wm == null) {
+            return;
+        }
+        // Removing delayed FORCE_GC message decreases time for waiting idle.
+        wm.mH.removeMessages(WindowManagerService.H.FORCE_GC);
+        waitHandlerIdle(wm.mH);
+        waitHandlerIdle(wm.mAnimationHandler);
+        waitHandlerIdle(SurfaceAnimationThread.getHandler());
+    }
+
+    private static void waitHandlerIdle(Handler handler) {
+        if (!handler.hasMessagesOrCallbacks()) {
+            return;
+        }
+        final CountDownLatch latch = new CountDownLatch(1);
+        // Wait for delayed messages are processed.
+        handler.getLooper().getQueue().addIdleHandler(() -> {
+            if (handler.hasMessagesOrCallbacks()) {
+                return true; // keep idle handler.
+            }
+            latch.countDown();
+            return false; // remove idle handler.
+        });
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index d9ef10d..0a4a8a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,12 +16,15 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
@@ -58,7 +61,7 @@
         boolean mDockedResizingForTest = false;
         WindowStateWithTask(WindowManagerService wm, IWindow iWindow, WindowToken windowToken,
                 WindowManager.LayoutParams attrs, Task t) {
-            super(wm, null, iWindow, windowToken, null, 0, 0, attrs, 0, 0,
+            super(wm, mock(Session.class), iWindow, windowToken, null, 0, 0, attrs, 0, 0,
                     false /* ownerCanAddInternalSystemWindow */);
             mTask = t;
         }
@@ -113,9 +116,9 @@
 
     @Before
     public void setUp() throws Exception {
-        mWindowToken = WindowTestUtils.createTestAppWindowToken(
-                mWm.getDefaultDisplayContentLocked());
-        mStubStack = new TaskStack(mWm, 0, null);
+        mWindowToken = createAppWindowToken(mWm.getDefaultDisplayContentLocked(),
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        mStubStack = WindowTestUtils.createMockTaskStack();
     }
 
     // Do not use this function directly in the tests below. Instead, use more explicit function
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
deleted file mode 100644
index 04e433e..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * 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.wm;
-
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.content.Context;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManagerInternal;
-import android.os.PowerSaveState;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-
-import com.android.server.LocalServices;
-import com.android.server.input.InputManagerService;
-import com.android.server.policy.WindowManagerPolicy;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
- * to properly tear it down after.
- *
- * <p>
- * Usage:
- * <pre>
- * {@literal @}Rule
- *  public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
- * </pre>
- */
-public class WindowManagerServiceRule implements TestRule {
-
-    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) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                runWithDexmakerShareClassLoader(this::setUp);
-                try {
-                    base.evaluate();
-                } finally {
-                    tearDown();
-                }
-            }
-
-            private void setUp() {
-                final Context context = getInstrumentation().getTargetContext();
-
-                removeServices();
-
-                LocalServices.addService(DisplayManagerInternal.class,
-                        mock(DisplayManagerInternal.class));
-
-                LocalServices.addService(PowerManagerInternal.class,
-                        mock(PowerManagerInternal.class));
-                final PowerManagerInternal pm =
-                        LocalServices.getService(PowerManagerInternal.class);
-                doNothing().when(pm).registerLowPowerModeObserver(any());
-                PowerSaveState state = new PowerSaveState.Builder().build();
-                doReturn(state).when(pm).getLowPowerState(anyInt());
-
-                LocalServices.addService(ActivityManagerInternal.class,
-                        mock(ActivityManagerInternal.class));
-                LocalServices.addService(ActivityTaskManagerInternal.class,
-                        mock(ActivityTaskManagerInternal.class));
-                final ActivityTaskManagerInternal atm =
-                        LocalServices.getService(ActivityTaskManagerInternal.class);
-                doAnswer((InvocationOnMock invocationOnMock) -> {
-                    final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
-                    if (runnable != null) {
-                        runnable.run();
-                    }
-                    return null;
-                }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt());
-
-                InputManagerService ims = mock(InputManagerService.class);
-                // 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(), anyInt());
-                }
-                ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
-                when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());
-
-                mService = WindowManagerService.main(context, ims, false, false,
-                        mPolicy = new TestWindowManagerPolicy(
-                                WindowManagerServiceRule.this::getWindowManagerService), atms);
-                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();
-
-                final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-                // Display creation is driven by the ActivityManagerService via
-                // ActivityStackSupervisor. We emulate those steps here.
-                mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
-            }
-
-            private void removeServices() {
-                LocalServices.removeServiceForTest(DisplayManagerInternal.class);
-                LocalServices.removeServiceForTest(PowerManagerInternal.class);
-                LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-                LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
-                LocalServices.removeServiceForTest(WindowManagerInternal.class);
-                LocalServices.removeServiceForTest(WindowManagerPolicy.class);
-            }
-
-            private void tearDown() {
-                cancelAllPendingAnimations();
-                waitUntilWindowManagerHandlersIdle();
-                destroyAllSurfaceTransactions();
-                destroyAllSurfaceControls();
-                removeServices();
-                mService = null;
-                mPolicy = null;
-            }
-        };
-    }
-
-    WindowManagerService getWindowManagerService() {
-        return mService;
-    }
-
-    private void cancelAllPendingAnimations() {
-        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
-            final SurfaceControl sc = reference.get();
-            if (sc != null) {
-                mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
-            }
-        }
-    }
-
-    void waitUntilWindowManagerHandlersIdle() {
-        final WindowManagerService wm = getWindowManagerService();
-        if (wm == null) {
-            return;
-        }
-        wm.mH.removeCallbacksAndMessages(null);
-        wm.mAnimationHandler.removeCallbacksAndMessages(null);
-        SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
-        wm.mH.runWithScissors(() -> { }, 0);
-        wm.mAnimationHandler.runWithScissors(() -> { }, 0);
-        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/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
deleted file mode 100644
index 343d359..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.wm;
-
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Build/InstallRun:
- *  atest FrameworksServicesTests:WindowManagerServiceRuleTest
- */
-@Presubmit
-@SmallTest
-public class WindowManagerServiceRuleTest {
-
-    @Rule
-    public final WindowManagerServiceRule mRule = new WindowManagerServiceRule();
-
-    @Test
-    public void testWindowManagerSetUp() {
-        assertThat(mRule.getWindowManagerService(), notNullValue());
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 7f78034..c09cd46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -31,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -56,6 +58,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 import android.view.DisplayCutout;
+import android.view.InsetsSource;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
@@ -326,6 +329,20 @@
     }
 
     @Test
+    public void testVisibleWithInsetsProvider() throws Exception {
+        final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        topBar.mHasSurface = true;
+        assertTrue(topBar.isVisible());
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(app);
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR));
+        assertFalse(topBar.isVisible());
+    }
+
+    @Test
     public void testIsSelfOrAncestorWindowAnimating() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 20fc5ae..44e998b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -103,12 +103,10 @@
     }
 
     /**
-     * Creates a mock instance of {@link StackWindowController}.
+     * Creates a mock instance of {@link TestTaskStack}.
      */
-    public static StackWindowController createMockStackWindowContainerController() {
-        StackWindowController controller = mock(StackWindowController.class);
-        controller.mContainer = mock(TestTaskStack.class);
-        return controller;
+    public static TaskStack createMockTaskStack() {
+        return mock(TestTaskStack.class);
     }
 
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
@@ -201,6 +199,11 @@
         }
 
         @Override
+        void onParentSet() {
+            // Do nothing.
+        }
+
+        @Override
         boolean isOnTop() {
             return mOnTop;
         }
@@ -283,10 +286,9 @@
         }
     }
 
-    public static TestTask createTestTask(StackWindowController stackWindowController) {
-        return new TestTask(sNextTaskId++, stackWindowController.mContainer, 0,
-                stackWindowController.mService, RESIZE_MODE_UNRESIZEABLE, false,
-                mock(TaskRecord.class));
+    public static TestTask createTestTask(TaskStack stack) {
+        return new TestTask(sNextTaskId++, stack, 0, stack.mWmService, RESIZE_MODE_UNRESIZEABLE,
+                false, mock(TaskRecord.class));
     }
 
     public static class TestIApplicationToken implements IApplicationToken {
@@ -334,5 +336,10 @@
 
             mHasSurface = hadSurface;
         }
+
+        @Override
+        void onParentSet() {
+            // Do nothing;
+        }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5c3368b..e38118e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -41,7 +41,6 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.util.Log;
@@ -53,6 +52,7 @@
 import com.android.server.AttributeCache;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
@@ -95,17 +95,22 @@
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
-    @Rule
-    public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
-
-    static WindowState.PowerManagerWrapper sPowerManagerWrapper;  // TODO(roosa): make non-static.
+    static WindowState.PowerManagerWrapper sPowerManagerWrapper;
 
     @BeforeClass
     public static void setUpOnceBase() {
         AttributeCache.init(getInstrumentation().getTargetContext());
+
+        TestSystemServices.setUpWindowManagerService();
+
         sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
     }
 
+    @AfterClass
+    public static void tearDonwOnceBase() {
+        TestSystemServices.tearDownWindowManagerService();
+    }
+
     @Before
     public void setUpBase() {
         // If @Before throws an exception, the error isn't logged. This will make sure any failures
@@ -115,7 +120,7 @@
 
             final Context context = getInstrumentation().getTargetContext();
 
-            mWm = mWmRule.getWindowManagerService();
+            mWm = TestSystemServices.getWindowManagerService();
             beforeCreateDisplay();
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -192,8 +197,8 @@
                 mDisplayContent.mInputMethodTarget = null;
             }
 
-            // Wait until everything is really cleaned up.
-            waitUntilHandlersIdle();
+            // Cleaned up everything in Handler.
+            TestSystemServices.cleanupWindowManagerHandlers();
         } catch (Exception e) {
             Log.e(TAG, "Failed to tear down test", e);
             throw e;
@@ -214,7 +219,7 @@
      * Waits until the main handler for WM has processed all messages.
      */
     void waitUntilHandlersIdle() {
-        mWmRule.waitUntilWindowManagerHandlersIdle();
+        TestSystemServices.waitUntilWindowManagerHandlersIdle();
     }
 
     private WindowToken createWindowToken(
@@ -234,8 +239,7 @@
 
     WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int
             windowingMode, int activityType) {
-        final TaskStack stack = createStackControllerOnStackOnDisplay(windowingMode, activityType,
-                dc).mContainer;
+        final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
         final Task task = createTaskInStack(stack, 0 /* userId */);
         final WindowTestUtils.TestAppWindowToken appWindowToken =
                 WindowTestUtils.createTestAppWindowToken(dc);
@@ -251,6 +255,14 @@
         }
     }
 
+    WindowState createWindow(WindowState parent, int type, String name, int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            return (parent == null)
+                    ? createWindow(parent, type, mDisplayContent, name, ownerId)
+                    : createWindow(parent, type, parent.mToken, name, ownerId);
+        }
+    }
+
     WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
             int type, DisplayContent dc, String name) {
         synchronized (mWm.mGlobalLock) {
@@ -271,7 +283,16 @@
         synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(
                     dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
-            return createWindow(parent, type, token, name);
+            return createWindow(parent, type, token, name, 0 /* ownerId */);
+        }
+    }
+
+    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
+            int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            final WindowToken token = createWindowToken(
+                    dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
+            return createWindow(parent, type, token, name, ownerId);
         }
     }
 
@@ -293,6 +314,14 @@
     }
 
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
+            int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            return createWindow(parent, type, token, name, ownerId,
+                    false /* ownerCanAddInternalSystemWindow */);
+        }
+    }
+
+    WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
             int ownerId, boolean ownerCanAddInternalSystemWindow) {
         return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
                 mWm, mMockSession, mIWindow);
@@ -319,28 +348,20 @@
     /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
     TaskStack createTaskStackOnDisplay(DisplayContent dc) {
         synchronized (mWm.mGlobalLock) {
-            return createStackControllerOnDisplay(dc).mContainer;
+            return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
         }
     }
 
-    StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
-        synchronized (mWm.mGlobalLock) {
-            return createStackControllerOnStackOnDisplay(
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
-        }
-    }
-
-    StackWindowController createStackControllerOnStackOnDisplay(
-            int windowingMode, int activityType, DisplayContent dc) {
+    TaskStack createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
         synchronized (mWm.mGlobalLock) {
             final Configuration overrideConfig = new Configuration();
             overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
             overrideConfig.windowConfiguration.setActivityType(activityType);
             final int stackId = ++sNextStackId;
-            final StackWindowController controller = new StackWindowController(stackId, null,
-                    dc.getDisplayId(), true /* onTop */, new Rect(), mWm);
-            controller.onRequestedOverrideConfigurationChanged(overrideConfig);
-            return controller;
+            final TaskStack stack = new TaskStack(mWm, stackId, mock(ActivityStack.class));
+            dc.setStackOnDisplay(stackId, true, stack);
+            stack.onRequestedOverrideConfigurationChanged(overrideConfig);
+            return stack;
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
new file mode 100644
index 0000000..8d83497
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+
+/**
+ * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:WindowTraceBufferTest
+ */
+@SmallTest
+@Presubmit
+public class WindowTraceBufferTest {
+    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+    private File mFile;
+
+    @Before
+    public void setUp() throws Exception {
+        final Context testContext = getInstrumentation().getContext();
+        mFile = testContext.getFileStreamPath("tracing_test.dat");
+        mFile.delete();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mFile.delete();
+    }
+
+    @Test
+    public void testTraceQueueBuffer_addItem() throws Exception {
+        ProtoOutputStream toWrite1 = getDummy(1);
+        ProtoOutputStream toWrite2 = getDummy(2);
+        ProtoOutputStream toWrite3 = getDummy(3);
+        byte[] toWrite1Bytes = toWrite1.getBytes();
+        byte[] toWrite2Bytes = toWrite2.getBytes();
+        byte[] toWrite3Bytes = toWrite3.getBytes();
+
+        final int objectSize = toWrite1.getBytes().length;
+        final int bufferCapacity = objectSize * 2;
+
+        final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity);
+
+        buffer.add(toWrite1);
+        assertTrue("First element should be in the list",
+                buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite1Bytes)));
+
+        buffer.add(toWrite2);
+        assertTrue("First element should be in the list",
+                buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite1Bytes)));
+        assertTrue("Second element should be in the list",
+                buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite2Bytes)));
+
+        buffer.add(toWrite3);
+
+        assertTrue("Third element should not be in the list",
+                buffer.mBuffer.stream().noneMatch(p -> Arrays.equals(p, toWrite3Bytes)));
+
+        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
+        assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
+                buffer.mBufferSize, bufferCapacity);
+        assertEquals("Buffer is full, available space should be 0",
+                buffer.getAvailableSpace(), 0);
+    }
+
+    private ProtoOutputStream getDummy(int value) {
+        ProtoOutputStream toWrite = new ProtoOutputStream();
+        toWrite.write(MAGIC_NUMBER, value);
+        toWrite.flush();
+
+        return toWrite;
+    }
+
+    private WindowTraceBuffer buildQueueBuffer(int size) throws IOException {
+        return new WindowTraceQueueBuffer(size, mFile, false);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 8e1ede1..2ed11fe 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -23,7 +23,6 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -62,6 +61,8 @@
 
     private static final long ONE_MINUTE = 60_000L;
 
+    private static final Integer ONE = new Integer(1);
+
     /** Collection of data for each user that has reported usage */
     @GuardedBy("mLock")
     private final SparseArray<UserData> mUsers = new SparseArray<>();
@@ -79,11 +80,11 @@
         private @UserIdInt
         int userId;
 
-        /** Set of the currently active entities */
-        private final ArraySet<String> currentlyActive = new ArraySet<>();
+        /** Count of the currently active entities */
+        public final ArrayMap<String, Integer> currentlyActive = new ArrayMap<>();
 
         /** Map from entity name for quick lookup */
-        private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
+        public final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
 
         private UserData(@UserIdInt int userId) {
             this.userId = userId;
@@ -94,7 +95,7 @@
             // TODO: Consider using a bloom filter here if number of actives becomes large
             final int size = entities.length;
             for (int i = 0; i < size; i++) {
-                if (currentlyActive.contains(entities[i])) {
+                if (currentlyActive.containsKey(entities[i])) {
                     return true;
                 }
             }
@@ -137,7 +138,7 @@
             pw.print(" Currently Active:");
             final int nActive = currentlyActive.size();
             for (int i = 0; i < nActive; i++) {
-                pw.print(currentlyActive.valueAt(i));
+                pw.print(currentlyActive.keyAt(i));
                 pw.print(", ");
             }
             pw.println();
@@ -233,6 +234,7 @@
         protected long mUsageTimeMs;
         protected int mActives;
         protected long mLastKnownUsageTimeMs;
+        protected long mLastUsageEndTimeMs;
         protected WeakReference<UserData> mUserRef;
         protected WeakReference<ObserverAppData> mObserverAppRef;
         protected PendingIntent mLimitReachedCallback;
@@ -271,9 +273,15 @@
         @GuardedBy("mLock")
         void noteUsageStart(long startTimeMs, long currentTimeMs) {
             if (mActives++ == 0) {
+                // If last known usage ended after the start of this usage, there is overlap
+                // between the last usage session and this one. Avoid double counting by only
+                // counting from the end of the last session. This has a rare side effect that some
+                // usage will not be accounted for if the previous session started and stopped
+                // within this current usage.
+                startTimeMs = mLastUsageEndTimeMs > startTimeMs ? mLastUsageEndTimeMs : startTimeMs;
                 mLastKnownUsageTimeMs = startTimeMs;
                 final long timeRemaining =
-                        mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs;
+                        mTimeLimitMs - mUsageTimeMs - currentTimeMs + startTimeMs;
                 if (timeRemaining > 0) {
                     if (DEBUG) {
                         Slog.d(TAG, "Posting timeout for " + mObserverId + " for "
@@ -287,7 +295,7 @@
                     mActives = mObserved.length;
                     final UserData user = mUserRef.get();
                     if (user == null) return;
-                    final Object[] array = user.currentlyActive.toArray();
+                    final Object[] array = user.currentlyActive.keySet().toArray();
                     Slog.e(TAG,
                             "Too many noted usage starts! Observed entities: " + Arrays.toString(
                                     mObserved) + "   Active Entities: " + Arrays.toString(array));
@@ -300,6 +308,8 @@
             if (--mActives == 0) {
                 final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs;
                 mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs;
+
+                mLastUsageEndTimeMs = stopTimeMs;
                 if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) {
                     // Crossed the limit
                     if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId);
@@ -312,7 +322,7 @@
                     mActives = 0;
                     final UserData user = mUserRef.get();
                     if (user == null) return;
-                    final Object[] array = user.currentlyActive.toArray();
+                    final Object[] array = user.currentlyActive.keySet().toArray();
                     Slog.e(TAG,
                             "Too many noted usage stops! Observed entities: " + Arrays.toString(
                                     mObserved) + "   Active Entities: " + Arrays.toString(array));
@@ -409,7 +419,6 @@
     }
 
     class SessionUsageGroup extends UsageGroup {
-        private long mLastUsageEndTimeMs;
         private long mNewSessionThresholdMs;
         private PendingIntent mSessionEndCallback;
 
@@ -451,7 +460,6 @@
         public void noteUsageStop(long stopTimeMs) {
             super.noteUsageStop(stopTimeMs);
             if (mActives == 0) {
-                mLastUsageEndTimeMs = stopTimeMs;
                 if (mUsageTimeMs >= mTimeLimitMs) {
                     // Usage has ended. Schedule the session end callback to be triggered once
                     // the new session threshold has been reached
@@ -467,7 +475,10 @@
             UserData user = mUserRef.get();
             if (user == null) return;
             if (mListener != null) {
-                mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback);
+                mListener.onSessionEnd(mObserverId,
+                                       user.userId,
+                                       mUsageTimeMs,
+                                       mSessionEndCallback);
             }
         }
 
@@ -599,7 +610,7 @@
         // TODO: Consider using a bloom filter here if number of actives becomes large
         final int size = group.mObserved.length;
         for (int i = 0; i < size; i++) {
-            if (user.currentlyActive.contains(group.mObserved[i])) {
+            if (user.currentlyActive.containsKey(group.mObserved[i])) {
                 // Entity is currently active. Start group's usage.
                 group.noteUsageStart(currentTimeMs);
             }
@@ -717,21 +728,28 @@
     /**
      * Called when an entity becomes active.
      *
-     * @param name   The entity that became active
-     * @param userId The user
+     * @param name      The entity that became active
+     * @param userId    The user
+     * @param timeAgoMs Time since usage was started
      */
-    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+    public void noteUsageStart(String name, int userId, long timeAgoMs)
+            throws IllegalArgumentException {
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
             if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active");
-            if (user.currentlyActive.contains(name)) {
-                throw new IllegalArgumentException(
-                        "Unable to start usage for " + name + ", already in use");
+
+            final int index = user.currentlyActive.indexOfKey(name);
+            if (index >= 0) {
+                final Integer count = user.currentlyActive.valueAt(index);
+                if (count != null) {
+                    // There are multiple instances of this entity. Just increment the count.
+                    user.currentlyActive.setValueAt(index, count + 1);
+                    return;
+                }
             }
             final long currentTime = getUptimeMillis();
 
-            // Add to the list of active entities
-            user.currentlyActive.add(name);
+            user.currentlyActive.put(name, ONE);
 
             ArrayList<UsageGroup> groups = user.observedMap.get(name);
             if (groups == null) return;
@@ -739,12 +757,22 @@
             final int size = groups.size();
             for (int i = 0; i < size; i++) {
                 UsageGroup group = groups.get(i);
-                group.noteUsageStart(currentTime);
+                group.noteUsageStart(currentTime - timeAgoMs, currentTime);
             }
         }
     }
 
     /**
+     * Called when an entity becomes active.
+     *
+     * @param name   The entity that became active
+     * @param userId The user
+     */
+    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+        noteUsageStart(name, userId, 0);
+    }
+
+    /**
      * Called when an entity becomes inactive.
      *
      * @param name   The entity that became inactive
@@ -754,10 +782,21 @@
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
             if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive");
-            if (!user.currentlyActive.remove(name)) {
+
+            final int index = user.currentlyActive.indexOfKey(name);
+            if (index < 0) {
                 throw new IllegalArgumentException(
                         "Unable to stop usage for " + name + ", not in use");
             }
+
+            final Integer count = user.currentlyActive.valueAt(index);
+            if (!count.equals(ONE)) {
+                // There are multiple instances of this entity. Just decrement the count.
+                user.currentlyActive.setValueAt(index, count - 1);
+                return;
+            }
+
+            user.currentlyActive.removeAt(index);
             final long currentTime = getUptimeMillis();
 
             // Check if any of the groups need to watch for this entity
@@ -769,6 +808,7 @@
                 UsageGroup group = groups.get(i);
                 group.noteUsageStop(currentTime);
             }
+
         }
     }
 
@@ -780,7 +820,8 @@
 
     @GuardedBy("mLock")
     private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
                 timeout);
     }
 
@@ -800,7 +841,27 @@
         mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
     }
 
-    void dump(PrintWriter pw) {
+    void dump(String[] args, PrintWriter pw) {
+        if (args != null) {
+            for (int i = 0; i < args.length; i++) {
+                String arg = args[i];
+                if ("actives".equals(arg)) {
+                    synchronized (mLock) {
+                        final int nUsers = mUsers.size();
+                        for (int user = 0; user < nUsers; user++) {
+                            final ArrayMap<String, Integer> actives =
+                                    mUsers.valueAt(user).currentlyActive;
+                            final int nActive = actives.size();
+                            for (int active = 0; active < nActive; active++) {
+                                pw.println(actives.keyAt(active));
+                            }
+                        }
+                    }
+                    return;
+                }
+            }
+        }
+
         synchronized (mLock) {
             pw.println("\n  App Time Limits");
             final int nUsers = mUsers.size();
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 94cc650..9a5bd13 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -22,6 +22,7 @@
 import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
 import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
 import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.END_OF_DAY;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
@@ -66,7 +67,7 @@
     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     public Configuration activeConfiguration;
-    public EventList events;
+    public EventList events = new EventList();
 
     // A string cache. This is important as when we're parsing XML files, we don't want to
     // keep hundreds of strings that have the same contents. We will read the string
@@ -112,6 +113,9 @@
 
     }
 
+    public IntervalStats() {
+    }
+
     /**
      * Gets the UsageStats object for the given package, or creates one and adds it internally.
      */
@@ -253,6 +257,7 @@
             case ROLLOVER_FOREGROUND_SERVICE:
             case CONTINUE_PREVIOUS_DAY:
             case CONTINUING_FOREGROUND_SERVICE:
+            case DEVICE_SHUTDOWN:
                 return true;
         }
         return false;
@@ -281,8 +286,9 @@
     @VisibleForTesting
     public void update(String packageName, String className, long timeStamp, int eventType,
             int instanceId) {
-        if (eventType == FLUSH_TO_DISK) {
-            // FLUSH_TO_DISK are sent to all packages.
+        if (eventType == DEVICE_SHUTDOWN
+                || eventType == FLUSH_TO_DISK) {
+            // DEVICE_SHUTDOWN and FLUSH_TO_DISK are sent to all packages.
             final int size = packageStats.size();
             for (int i = 0; i < size; i++) {
                 UsageStats usageStats = packageStats.valueAt(i);
@@ -321,9 +327,6 @@
      */
     @VisibleForTesting
     public void addEvent(Event event) {
-        if (events == null) {
-            events = new EventList();
-        }
         // Cache common use strings
         event.mPackage = getCachedStringRef(event.mPackage);
         if (event.mClass != null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 57dc08f..76a3aa8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,6 +18,7 @@
 
 import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION;
 import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
 import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
@@ -53,6 +54,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
@@ -105,6 +107,8 @@
     private static final boolean ENABLE_KERNEL_UPDATES = true;
     private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
 
+    private static final char TOKEN_DELIMITER = '/';
+
     // Handler message types.
     static final int MSG_REPORT_EVENT = 0;
     static final int MSG_FLUSH_TO_DISK = 1;
@@ -135,6 +139,10 @@
     /** Manages app time limit observers */
     AppTimeLimitController mAppTimeLimit;
 
+    final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
+    final SparseArray<String> mVisibleActivities = new SparseArray();
+
+
     private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
             new UsageStatsManagerInternal.AppIdleStateChangeListener() {
                 @Override
@@ -270,7 +278,7 @@
                     mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
-                if (userId >=0) {
+                if (userId >= 0) {
                     mAppStandby.postCheckIdleStates(userId);
                 }
             }
@@ -409,11 +417,30 @@
      */
     void shutdown() {
         synchronized (mLock) {
+            mHandler.removeMessages(MSG_REPORT_EVENT);
+            Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime());
+            // orderly shutdown, the last event is DEVICE_SHUTDOWN.
+            reportEventToAllUserId(event);
             flushToDiskLocked();
         }
     }
 
     /**
+     * After power button is pressed for 3.5 seconds
+     * (as defined in {@link com.android.internal.R.integer#config_veryLongPressTimeout}),
+     * report DEVICE_SHUTDOWN event and persist the database. If the power button is pressed for 10
+     * seconds and the device is shutdown, the database is already persisted and we are not losing
+     * data.
+     * This method is called from PhoneWindowManager, do not synchronize on mLock otherwise
+     * PhoneWindowManager may be blocked.
+     */
+    void prepareForPossibleShutdown() {
+        Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime());
+        mHandler.obtainMessage(MSG_REPORT_EVENT_TO_ALL_USERID, event).sendToTarget();
+        mHandler.sendEmptyMessage(MSG_FLUSH_TO_DISK);
+    }
+
+    /**
      * Called by the Binder stub.
      */
     void reportEvent(Event event, int userId) {
@@ -434,17 +461,46 @@
             mAppStandby.reportEvent(event, elapsedRealtime, userId);
             switch (event.mEventType) {
                 case Event.ACTIVITY_RESUMED:
-                    try {
-                        mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
-                    } catch (IllegalArgumentException iae) {
-                        Slog.e(TAG, "Failed to note usage start", iae);
+                    synchronized (mVisibleActivities) {
+                        mVisibleActivities.put(event.mInstanceId, event.getClassName());
+                        try {
+                            mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+                        } catch (IllegalArgumentException iae) {
+                            Slog.e(TAG, "Failed to note usage start", iae);
+                        }
                     }
                     break;
-                case Event.ACTIVITY_PAUSED:
-                    try {
-                        mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
-                    } catch (IllegalArgumentException iae) {
-                        Slog.e(TAG, "Failed to note usage stop", iae);
+                case Event.ACTIVITY_STOPPED:
+                case Event.ACTIVITY_DESTROYED:
+                    ArraySet<String> tokens;
+                    synchronized (mUsageReporters) {
+                        tokens = mUsageReporters.removeReturnOld(event.mInstanceId);
+                    }
+                    if (tokens != null) {
+                        synchronized (tokens) {
+                            final int size = tokens.size();
+                            // Stop usage on behalf of a UsageReporter that stopped
+                            for (int i = 0; i < size; i++) {
+                                final String token = tokens.valueAt(i);
+                                try {
+                                    mAppTimeLimit.noteUsageStop(
+                                            buildFullToken(event.getPackageName(), token), userId);
+                                } catch (IllegalArgumentException iae) {
+                                    Slog.w(TAG, "Failed to stop usage for during reporter death: "
+                                            + iae);
+                                }
+                            }
+                        }
+                    }
+
+                    synchronized (mVisibleActivities) {
+                        if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) {
+                            try {
+                                mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+                            } catch (IllegalArgumentException iae) {
+                                Slog.w(TAG, "Failed to note usage stop", iae);
+                            }
+                        }
                     }
                     break;
             }
@@ -599,6 +655,14 @@
         return beginTime <= currentTime && beginTime < endTime;
     }
 
+    private String buildFullToken(String packageName, String token) {
+        final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
+        sb.append(packageName);
+        sb.append(TOKEN_DELIMITER);
+        sb.append(token);
+        return sb.toString();
+    }
+
     private void flushToDiskLocked() {
         final int userCount = mUserState.size();
         for (int i = 0; i < userCount; i++) {
@@ -627,8 +691,7 @@
                     String arg = args[i];
                     if ("--checkin".equals(arg)) {
                         checkin = true;
-                    } else
-                    if ("-c".equals(arg)) {
+                    } else if ("-c".equals(arg)) {
                         compact = true;
                     } else if ("flush".equals(arg)) {
                         flushToDiskLocked();
@@ -637,6 +700,15 @@
                     } else if ("is-app-standby-enabled".equals(arg)) {
                         pw.println(mAppStandby.mAppIdleEnabled);
                         return;
+                    } else if ("apptimelimit".equals(arg)) {
+                        if (i + 1 >= args.length) {
+                            mAppTimeLimit.dump(null, pw);
+                        } else {
+                            final String[] remainingArgs =
+                                    Arrays.copyOfRange(args, i + 1, args.length);
+                            mAppTimeLimit.dump(remainingArgs, pw);
+                        }
+                        return;
                     } else if (arg != null && !arg.startsWith("-")) {
                         // Anything else that doesn't start with '-' is a pkg to filter
                         pkg = arg;
@@ -666,7 +738,7 @@
                 mAppStandby.dumpState(args, pw);
             }
 
-            mAppTimeLimit.dump(pw);
+            mAppTimeLimit.dump(null, pw);
         }
     }
 
@@ -1231,16 +1303,82 @@
             final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
-                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
+                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId,
+                        userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void reportUsageStart(IBinder activity, String token, String callingPackage) {
+            reportPastUsageStart(activity, token, 0, callingPackage);
+        }
+
+        @Override
+        public void reportPastUsageStart(IBinder activity, String token, long timeAgoMs,
+                String callingPackage) {
+
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                ArraySet<String> tokens;
+                synchronized (mUsageReporters) {
+                    tokens = mUsageReporters.get(activity.hashCode());
+                    if (tokens == null) {
+                        tokens = new ArraySet();
+                        mUsageReporters.put(activity.hashCode(), tokens);
+                    }
+                }
+
+                synchronized (tokens) {
+                    if (!tokens.add(token)) {
+                        throw new IllegalArgumentException(token + " for " + callingPackage
+                                + " is already reported as started for this activity");
+                    }
+                }
+
+                mAppTimeLimit.noteUsageStart(buildFullToken(callingPackage, token),
+                        userId, timeAgoMs);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
+
+        @Override
+        public void reportUsageStop(IBinder activity, String token, String callingPackage) {
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                ArraySet<String> tokens;
+                synchronized (mUsageReporters) {
+                    tokens = mUsageReporters.get(activity.hashCode());
+                    if (tokens == null) {
+                        throw new IllegalArgumentException(
+                                "Unknown reporter trying to stop token " + token + " for "
+                                        + callingPackage);
+                    }
+                }
+
+                synchronized (tokens) {
+                    if (!tokens.remove(token)) {
+                        throw new IllegalArgumentException(token + " for " + callingPackage
+                                + " is already reported as stopped for this activity");
+                    }
+                }
+                mAppTimeLimit.noteUsageStop(buildFullToken(callingPackage, token), userId);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
     }
 
     void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
             long timeLimitMs, PendingIntent callbackIntent, int userId) {
-        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs,
+                callbackIntent,
                 userId);
     }
 
@@ -1369,6 +1507,11 @@
         }
 
         @Override
+        public void prepareForPossibleShutdown() {
+            UsageStatsService.this.prepareForPossibleShutdown();
+        }
+
+        @Override
         public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
             mAppStandby.addListener(listener);
             listener.onParoleStateChanged(isAppIdleParoleOn());
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 5128ae1..2d1098c7 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
 import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
 import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
@@ -134,6 +135,18 @@
             updateRolloverDeadline();
         }
 
+        // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp
+        // is last time UsageStatsDatabase is persisted to disk.
+        final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
+        if (currentDailyStats != null) {
+            final int size = currentDailyStats.events.size();
+            if (size == 0 || currentDailyStats.events.get(size - 1).mEventType != DEVICE_SHUTDOWN) {
+                // The last event in event list is not DEVICE_SHUTDOWN, then we insert one.
+                final Event event = new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved);
+                currentDailyStats.addEvent(event);
+            }
+        }
+
         if (mDatabase.isNewUpdate()) {
             notifyNewUpdate();
         }
@@ -175,7 +188,9 @@
                 // ACTIVITY_STOPPED.
                 && event.mEventType != Event.ACTIVITY_DESTROYED
                 // FLUSH_TO_DISK is a private event.
-                && event.mEventType != Event.FLUSH_TO_DISK) {
+                && event.mEventType != Event.FLUSH_TO_DISK
+                // DEVICE_SHUTDOWN is added to event list after reboot.
+                && event.mEventType != Event.DEVICE_SHUTDOWN) {
             currentDailyStats.addEvent(event);
         }
 
@@ -393,10 +408,6 @@
                     @Override
                     public void combine(IntervalStats stats, boolean mutable,
                             List<Event> accumulatedResult) {
-                        if (stats.events == null) {
-                            return;
-                        }
-
                         final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                         final int size = stats.events.size();
                         for (int i = startIndex; i < size; i++) {
@@ -434,10 +445,6 @@
         names.add(packageName);
         final List<Event> results = queryStats(INTERVAL_DAILY,
                 beginTime, endTime, (stats, mutable, accumulatedResult) -> {
-                    if (stats.events == null) {
-                        return;
-                    }
-
                     final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                     final int size = stats.events.size();
                     for (int i = startIndex; i < size; i++) {
@@ -696,10 +703,6 @@
                     @Override
                     public void combine(IntervalStats stats, boolean mutable,
                             List<Event> accumulatedResult) {
-                        if (stats.events == null) {
-                            return;
-                        }
-
                         final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                         final int size = stats.events.size();
                         for (int i = startIndex; i < size; i++) {
@@ -925,10 +928,12 @@
                 return "SCREEN_INTERACTIVE";
             case Event.SCREEN_NON_INTERACTIVE:
                 return "SCREEN_NON_INTERACTIVE";
-            case UsageEvents.Event.KEYGUARD_SHOWN:
+            case Event.KEYGUARD_SHOWN:
                 return "KEYGUARD_SHOWN";
-            case UsageEvents.Event.KEYGUARD_HIDDEN:
+            case Event.KEYGUARD_HIDDEN:
                 return "KEYGUARD_HIDDEN";
+            case Event.DEVICE_SHUTDOWN:
+                return "DEVICE_SHUTDOWN";
             default:
                 return "UNKNOWN_TYPE_" + eventType;
         }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 294b750..f1e2281 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1744,8 +1744,8 @@
                     mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
                             USB_GADGET_HAL_DEATH_COOKIE);
                     mCurrentFunctions = UsbManager.FUNCTION_NONE;
-                    mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
                     mCurrentUsbFunctionsRequested = true;
+                    mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
                 }
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 904d55e..00c7548 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -418,12 +418,10 @@
                         parser.getRawDescriptors());
 
                 // Stats collection
-                if (parser.hasAudioInterface()) {
-                    StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
-                            newDevice.getProductId(), parser.hasAudioInterface(),
-                            parser.hasHIDInterface(), parser.hasStorageInterface(),
-                            StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
-                }
+                StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
+                        newDevice.getProductId(), parser.hasAudioInterface(),
+                        parser.hasHIDInterface(), parser.hasStorageInterface(),
+                        StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
             }
         }
 
@@ -455,14 +453,12 @@
                 if (current != null) {
                     UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
                             current.mDescriptors);
-                    if (parser.hasAudioInterface()) {
                         // Stats collection
-                        StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
-                                device.getProductId(), parser.hasAudioInterface(),
-                                parser.hasHIDInterface(),  parser.hasStorageInterface(),
-                                StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
-                                System.currentTimeMillis() - current.mTimestamp);
-                    }
+                    StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
+                            device.getProductId(), parser.hasAudioInterface(),
+                            parser.hasHIDInterface(),  parser.hasStorageInterface(),
+                            StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
+                            System.currentTimeMillis() - current.mTimestamp);
                 }
             } else {
                 Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 38efc74..3d7cbb5 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -657,9 +657,8 @@
             return;
         }
 
-        if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
-            model.setStopped();
-        }
+        model.setStopped();
+
         try {
             callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event);
         } catch (DeadObjectException e) {
@@ -802,9 +801,7 @@
             return;
         }
 
-        if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
-            modelData.setStopped();
-        }
+        modelData.setStopped();
 
         try {
             modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index bbf3d45..613c4ff 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -371,14 +371,15 @@
         }
 
         private boolean shouldEnableService(Context context) {
-            // VoiceInteractionService should not be enabled on any low RAM devices
-            // or devices that have not declared the recognition feature, unless the
-            // device's configuration has explicitly set the config flag for a fixed
+            // VoiceInteractionService should not be enabled on devices that have not declared the
+            // recognition feature (including low-ram devices where notLowRam="true" takes effect),
+            // unless the device's configuration has explicitly set the config flag for a fixed
             // voice interaction service.
-            return (!ActivityManager.isLowRamDeviceStatic()
-                            && context.getPackageManager().hasSystemFeature(
-                                    PackageManager.FEATURE_VOICE_RECOGNIZERS)) ||
-                    getForceVoiceInteractionServicePackage(context.getResources()) != null;
+            if (getForceVoiceInteractionServicePackage(context.getResources()) != null) {
+                return true;
+            }
+            return context.getPackageManager()
+                    .hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS);
         }
 
         private String getForceVoiceInteractionServicePackage(Resources res) {
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
index b3b0900..59a80fb 100644
--- a/startop/iorap/Android.bp
+++ b/startop/iorap/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 java_library_static {
-  name: "libiorap-java",
+  name: "services.startop.iorap",
 
   aidl: {
     include_dirs: [
@@ -21,6 +21,8 @@
     ],
   },
 
+  libs: ["services.core"],
+
   srcs: [
       ":iorap-aidl",
       "**/*.java",
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
new file mode 100644
index 0000000..c2e4581
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -0,0 +1,368 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+// TODO: fix this. either move this class into system server or add a dependency on
+// these wm classes to libiorap-java and libiorap-java-tests (somehow).
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br />
+ * @see com.android.server.wm.ActivityMetricsLaunchObserver
+ *      ActivityMetricsLaunchObserver for the possible event states.
+ * @hide
+ */
+public abstract class AppLaunchEvent implements Parcelable {
+    @LongDef
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SequenceId {}
+
+    public final @SequenceId
+    long sequenceId;
+
+    protected AppLaunchEvent(@SequenceId long sequenceId) {
+        this.sequenceId = sequenceId;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof AppLaunchEvent) {
+            return equals((AppLaunchEvent) other);
+        }
+        return false;
+    }
+
+    protected boolean equals(AppLaunchEvent other) {
+        return sequenceId == other.sequenceId;
+    }
+
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() +
+                "{" + "sequenceId=" + Long.toString(sequenceId) +
+                toStringBody() + "}";
+    }
+
+    protected String toStringBody() { return ""; };
+
+    // List of possible variants:
+
+    public static final class IntentStarted extends AppLaunchEvent {
+        @NonNull
+        public final Intent intent;
+
+        public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+            super(sequenceId);
+            this.intent = intent;
+
+            Objects.requireNonNull(intent, "intent");
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof IntentStarted) {
+                return intent.equals(((IntentStarted)other).intent) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", intent=" + intent.toString();
+        }
+
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+            super.writeToParcelImpl(p, flags);
+            intent.writeToParcel(p, flags);
+        }
+
+        IntentStarted(Parcel p) {
+            super(p);
+            intent = Intent.CREATOR.createFromParcel(p);
+        }
+    }
+
+    public static final class IntentFailed extends AppLaunchEvent {
+        public IntentFailed(@SequenceId long sequenceId) {
+            super(sequenceId);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof IntentFailed) {
+                return super.equals(other);
+            }
+            return false;
+        }
+
+        IntentFailed(Parcel p) {
+            super(p);
+        }
+    }
+
+    public static abstract class BaseWithActivityRecordData extends AppLaunchEvent {
+        public final @NonNull
+        @ActivityRecordProto byte[] activityRecordSnapshot;
+
+        protected BaseWithActivityRecordData(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId);
+            activityRecordSnapshot = snapshot;
+
+            Objects.requireNonNull(snapshot, "snapshot");
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof BaseWithActivityRecordData) {
+                return activityRecordSnapshot.equals(
+                        ((BaseWithActivityRecordData)other).activityRecordSnapshot) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", " + activityRecordSnapshot.toString();
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+        }
+
+        BaseWithActivityRecordData(Parcel p) {
+            super(p);
+            activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+        }
+    }
+
+    public static final class ActivityLaunched extends BaseWithActivityRecordData {
+        public final @ActivityMetricsLaunchObserver.Temperature
+        int temperature;
+
+        public ActivityLaunched(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot, 
+                @ActivityMetricsLaunchObserver.Temperature int temperature) {
+            super(sequenceId, snapshot);
+            this.temperature = temperature;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunched) {
+                return temperature == ((ActivityLaunched)other).temperature &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", temperature=" + Integer.toString(temperature);
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           p.writeInt(temperature);
+        }
+
+        ActivityLaunched(Parcel p) {
+            super(p);
+            temperature = p.readInt();
+        }
+    }
+
+    public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+        public ActivityLaunchFinished(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId, snapshot);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunched) {
+                return super.equals(other);
+            }
+            return false;
+        }
+    }
+
+     public static class ActivityLaunchCancelled extends AppLaunchEvent {
+        public final @Nullable
+        @ActivityRecordProto byte[] activityRecordSnapshot;
+
+        public ActivityLaunchCancelled(@SequenceId long sequenceId,
+                @Nullable @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId);
+            activityRecordSnapshot = snapshot;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunchCancelled) {
+                return Objects.equals(activityRecordSnapshot,
+                        ((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", " + activityRecordSnapshot.toString();
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           if (activityRecordSnapshot != null) {
+               p.writeBoolean(true);
+               ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+           } else {
+               p.writeBoolean(false);
+           }
+        }
+
+        ActivityLaunchCancelled(Parcel p) {
+            super(p);
+            if (p.readBoolean()) {
+                activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+            } else {
+                activityRecordSnapshot = null;
+            }
+        }
+    }
+
+    @Override
+    public @ContentsFlags int describeContents() { return 0; }
+
+    @Override
+    public void writeToParcel(Parcel p, @WriteFlags int flags) {
+        p.writeInt(getTypeIndex());
+
+        writeToParcelImpl(p, flags);
+    }
+
+
+    public static Creator<AppLaunchEvent> CREATOR =
+            new Creator<AppLaunchEvent>() {
+        @Override
+        public AppLaunchEvent createFromParcel(Parcel source) {
+            int typeIndex = source.readInt();
+
+            Class<?> kls = getClassFromTypeIndex(typeIndex);
+            if (kls == null) {
+                throw new IllegalArgumentException("Invalid type index: " + typeIndex);
+            }
+
+            try {
+                return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source);
+            } catch (InstantiationException e) {
+                throw new AssertionError(e);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError(e);
+            } catch (InvocationTargetException e) {
+                throw new AssertionError(e);
+            } catch (NoSuchMethodException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public AppLaunchEvent[] newArray(int size) {
+            return new AppLaunchEvent[0];
+        }
+    };
+
+    protected void writeToParcelImpl(Parcel p, int flags) {
+        p.writeLong(sequenceId);
+    }
+
+    protected AppLaunchEvent(Parcel p) {
+        sequenceId = p.readLong();
+    }
+
+    private int getTypeIndex() {
+        for (int i = 0; i < sTypes.length; ++i) {
+            if (sTypes[i].equals(this.getClass())) {
+                return i;
+            }
+        }
+        throw new AssertionError("sTypes did not include this type: " + this.getClass());
+    }
+
+    private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) {
+        if (typeIndex >= 0 && typeIndex < sTypes.length) {
+            return sTypes[typeIndex];
+        }
+        return null;
+    }
+
+    // Index position matters: It is used to encode the specific type in parceling.
+    // Keep up-to-date with C++ side.
+    private static Class<?>[] sTypes = new Class[] {
+            IntentStarted.class,
+            IntentFailed.class,
+            ActivityLaunched.class,
+            ActivityLaunchFinished.class,
+            ActivityLaunchCancelled.class,
+    };
+
+    // TODO: move to @ActivityRecordProto byte[] once we have unit tests.
+    public static class ActivityRecordProtoParcelable {
+        public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot,
+                int flags) {
+            p.writeByteArray(activityRecordSnapshot);
+        }
+
+        public static @ActivityRecordProto byte[] create(Parcel p) {
+            byte[] data = p.createByteArray();
+
+            return data;
+        }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
new file mode 100644
index 0000000..7fcad36
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -0,0 +1,216 @@
+/*
+ * 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.google.android.startop.iorap;
+// TODO: rename to com.android.server.startop.iorap
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+/**
+ * System-server-local proxy into the {@code IIorap} native service.
+ */
+public class IorapForwardingService extends SystemService {
+
+    public static final boolean DEBUG = true; // TODO: read from a getprop?
+    public static final String TAG = "IorapForwardingService";
+
+    private IIorap mIorapRemote;
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public IorapForwardingService(Context context) {
+       super(context);
+    }
+
+    //<editor-fold desc="Providers">
+    /*
+     * Providers for external dependencies:
+     * - These are marked as protected to allow tests to inject different values via mocks.
+     */
+
+    @VisibleForTesting
+    protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() {
+        ActivityTaskManagerInternal amtInternal =
+                LocalServices.getService(ActivityTaskManagerInternal.class);
+        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+                amtInternal.getLaunchObserverRegistry();
+        return launchObserverRegistry;
+    }
+
+    @VisibleForTesting
+    protected IIorap provideIorapRemote() {
+        try {
+            return IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
+        } catch (ServiceManager.ServiceNotFoundException e) {
+            // TODO: how do we handle service being missing?
+            throw new AssertionError(e);
+        }
+    }
+
+    //</editor-fold>
+
+    @Override
+    public void onStart() {
+        if (DEBUG) {
+            Log.v(TAG, "onStart");
+        }
+
+        // Connect to the native binder service.
+        mIorapRemote = provideIorapRemote();
+        invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) );
+
+        // Listen to App Launch Sequence events from ActivityTaskManager,
+        // and forward them to the native binder service.
+        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+                provideLaunchObserverRegistry();
+        launchObserverRegistry.registerLaunchObserver(new AppLaunchObserver());
+    }
+
+    private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
+        // We add a synthetic sequence ID here to make it easier to differentiate new
+        // launch sequences on the native side.
+        private @AppLaunchEvent.SequenceId long mSequenceId = -1;
+
+        @Override
+        public void onIntentStarted(@NonNull Intent intent) {
+            // #onIntentStarted [is the only transition that] initiates a new launch sequence.
+            ++mSequenceId;
+
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
+                        mSequenceId, intent));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+            );
+        }
+
+        @Override
+        public void onIntentFailed() {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.IntentFailed(mSequenceId))
+            );
+        }
+
+        @Override
+        public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
+                @Temperature int temperature) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)",
+                        mSequenceId, activity, temperature));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                            new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature))
+            );
+        }
+
+        @Override
+        public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)",
+                        mSequenceId, activity));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                            new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId,
+                                    activity)));
+        }
+
+        @Override
+        public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
+                        mSequenceId, activity));
+            }
+
+            invokeRemote(() ->
+                mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, activity))
+            );
+        }
+    }
+
+    private class RemoteTaskListener extends ITaskListener.Stub {
+        @Override
+        public void onProgress(RequestId requestId, TaskResult result) throws RemoteException {
+            if (DEBUG) {
+                Log.v(TAG,
+                        String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result));
+            }
+
+            // TODO: implement rest.
+        }
+
+        @Override
+        public void onComplete(RequestId requestId, TaskResult result) throws RemoteException {
+            if (DEBUG) {
+                Log.v(TAG,
+                        String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result));
+            }
+
+            // TODO: implement rest.
+        }
+    }
+
+    private interface RemoteRunnable {
+        void run() throws RemoteException;
+    }
+
+    private static void invokeRemote(RemoteRunnable r) {
+       try {
+           r.run();
+       } catch (RemoteException e) {
+           // TODO: what do we do with exceptions?
+           throw new AssertionError("not implemented", e);
+       }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
index 2c79319..adb3a91 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -71,7 +71,7 @@
 
     @Override
     public String toString() {
-        return String.format("{requestId: %ld}", requestId);
+        return String.format("{requestId: %d}", requestId);
     }
 
     @Override
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
index 7605784..5ac4a46 100644
--- a/startop/iorap/tests/Android.bp
+++ b/startop/iorap/tests/Android.bp
@@ -18,8 +18,15 @@
     srcs: ["src/**/*.kt"],
 
     static_libs: [
-      // non-test dependencies
-      "libiorap-java",
+      // Non-test dependencies
+
+      // library under test
+      "services.startop.iorap",
+      // need the system_server code to be on the classpath,
+      "services.core",
+
+      // Test Dependencies
+
       // test android dependencies
       "platform-test-annotations",
       "android-support-test",
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 91cec554..7dc83c3 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -22,26 +22,46 @@
     shared_libs: [
         "libbase",
         "libdexfile",
+        "libz",
         "slicer",
     ],
     static_libs: [
         "libtinyxml2",
+        "liblog",
+        "libutils",
+        "libziparchive",
     ],
+    cppflags: ["-std=c++17"],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroidfw",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libandroidfw",
+            ],
+        },
+    },
 }
 
-cc_library_host_static {
+cc_library_static {
     name: "libviewcompiler",
     defaults: ["viewcompiler_defaults"],
     srcs: [
+        "apk_layout_compiler.cc",
         "dex_builder.cc",
+        "dex_layout_compiler.cc",
         "java_lang_builder.cc",
         "tinyxml_layout_parser.cc",
         "util.cc",
         "layout_validation.cc",
     ],
+    host_supported: true,
 }
 
-cc_binary_host {
+cc_binary {
     name: "viewcompiler",
     defaults: ["viewcompiler_defaults"],
     srcs: [
@@ -51,6 +71,7 @@
         "libgflags",
         "libviewcompiler",
     ],
+    host_supported: true
 }
 
 cc_test_host {
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
new file mode 100644
index 0000000..09cdbd5
--- /dev/null
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -0,0 +1,174 @@
+/*
+ * 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 "apk_layout_compiler.h"
+#include "dex_layout_compiler.h"
+#include "java_lang_builder.h"
+#include "layout_validation.h"
+#include "util.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+#include <iostream>
+#include <locale>
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+using android::ResXMLParser;
+using android::base::StringPrintf;
+
+class ResXmlVisitorAdapter {
+ public:
+  ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {}
+
+  template <typename Visitor>
+  void Accept(Visitor* visitor) {
+    size_t depth{0};
+    do {
+      switch (parser_->next()) {
+        case ResXMLParser::START_DOCUMENT:
+          depth++;
+          visitor->VisitStartDocument();
+          break;
+        case ResXMLParser::END_DOCUMENT:
+          depth--;
+          visitor->VisitEndDocument();
+          break;
+        case ResXMLParser::START_TAG: {
+          depth++;
+          size_t name_length = 0;
+          const char16_t* name = parser_->getElementName(&name_length);
+          visitor->VisitStartTag(std::u16string{name, name_length});
+          break;
+        }
+        case ResXMLParser::END_TAG:
+          depth--;
+          visitor->VisitEndTag();
+          break;
+        default:;
+      }
+    } while (depth > 0 || parser_->getEventType() == ResXMLParser::FIRST_CHUNK_CODE);
+  }
+
+ private:
+  ResXMLParser* parser_;
+};
+
+bool CanCompileLayout(ResXMLParser* parser) {
+  ResXmlVisitorAdapter adapter{parser};
+  LayoutValidationVisitor visitor;
+  adapter.Accept(&visitor);
+
+  return visitor.can_compile();
+}
+
+namespace {
+void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
+                             CompilationTarget target, std::ostream& target_out) {
+  android::AssetManager2 resources;
+  resources.SetApkAssets({assets.get()});
+
+  std::string package_name;
+
+  // TODO: handle multiple packages better
+  bool first = true;
+  for (const auto& package : assets->GetLoadedArsc()->GetPackages()) {
+    CHECK(first);
+    package_name = package->GetPackageName();
+    first = false;
+  }
+
+  dex::DexBuilder dex_file;
+  dex::ClassBuilder compiled_view{
+      dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))};
+  std::vector<dex::MethodBuilder> methods;
+
+  assets->ForEachFile("res/", [&](const android::StringPiece& s, android::FileType) {
+    if (s == "layout") {
+      auto path = StringPrintf("res/%s/", s.to_string().c_str());
+      assets->ForEachFile(path, [&](const android::StringPiece& layout_file, android::FileType) {
+        auto layout_path = StringPrintf("%s%s", path.c_str(), layout_file.to_string().c_str());
+        android::ApkAssetsCookie cookie = android::kInvalidCookie;
+        auto asset = resources.OpenNonAsset(layout_path, android::Asset::ACCESS_RANDOM, &cookie);
+        CHECK(asset);
+        CHECK(android::kInvalidCookie != cookie);
+        const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie);
+        CHECK(nullptr != dynamic_ref_table);
+        android::ResXMLTree xml_tree{dynamic_ref_table};
+        xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true),
+                       asset->getLength(),
+                       /*copy_data=*/true);
+        android::ResXMLParser parser{xml_tree};
+        parser.restart();
+        if (CanCompileLayout(&parser)) {
+          parser.restart();
+          const std::string layout_name = startop::util::FindLayoutNameFromFilename(layout_path);
+          ResXmlVisitorAdapter adapter{&parser};
+          switch (target) {
+            case CompilationTarget::kDex: {
+              methods.push_back(compiled_view.CreateMethod(
+                  layout_name,
+                  dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
+                                 dex::TypeDescriptor::FromClassname("android.content.Context"),
+                                 dex::TypeDescriptor::Int()}));
+              DexViewBuilder builder(&methods.back());
+              builder.Start();
+              LayoutCompilerVisitor visitor{&builder};
+              adapter.Accept(&visitor);
+              builder.Finish();
+              methods.back().Encode();
+              break;
+            }
+            case CompilationTarget::kJavaLanguage: {
+              JavaLangViewBuilder builder{package_name, layout_name, target_out};
+              builder.Start();
+              LayoutCompilerVisitor visitor{&builder};
+              adapter.Accept(&visitor);
+              builder.Finish();
+              break;
+            }
+          }
+        }
+      });
+    }
+  });
+
+  if (target == CompilationTarget::kDex) {
+    slicer::MemView image{dex_file.CreateImage()};
+    target_out.write(image.ptr<const char>(), image.size());
+  }
+}
+}  // namespace
+
+void CompileApkLayouts(const std::string& filename, CompilationTarget target,
+                       std::ostream& target_out) {
+  auto assets = android::ApkAssets::Load(filename);
+  CompileApkAssetsLayouts(assets, target, target_out);
+}
+
+void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
+                         std::ostream& target_out) {
+  constexpr const char* friendly_name{"viewcompiler assets"};
+  auto assets = android::ApkAssets::LoadFromFd(
+      std::move(fd), friendly_name, /*system=*/false, /*force_shared_lib=*/false);
+  CompileApkAssetsLayouts(assets, target, target_out);
+}
+
+}  // namespace startop
diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h
new file mode 100644
index 0000000..03bd545
--- /dev/null
+++ b/startop/view_compiler/apk_layout_compiler.h
@@ -0,0 +1,35 @@
+/*
+ * 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 APK_LAYOUT_COMPILER_H_
+#define APK_LAYOUT_COMPILER_H_
+
+#include <string>
+
+#include "android-base/unique_fd.h"
+
+namespace startop {
+
+enum class CompilationTarget { kJavaLanguage, kDex };
+
+void CompileApkLayouts(const std::string& filename, CompilationTarget target,
+                       std::ostream& target_out);
+void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
+                         std::ostream& target_out);
+
+}  // namespace startop
+
+#endif  // APK_LAYOUT_COMPILER_H_
\ No newline at end of file
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index 4449ea0..d4f38ed 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -14,16 +14,30 @@
 // limitations under the License.
 //
 
+genrule {
+    name: "generate_compiled_layout",
+    tools: [":viewcompiler"],
+    cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
+    srcs: ["res/layout/layout1.xml"],
+    out: [
+        "layout1.dex",
+    ],
+}
+
 android_test {
     name: "dex-builder-test",
-    srcs: ["src/android/startop/test/DexBuilderTest.java"],
+    srcs: [
+        "src/android/startop/test/DexBuilderTest.java",
+        "src/android/startop/test/LayoutCompilerTest.java",
+    ],
     sdk_version: "current",
-    data: [":generate_dex_testcases"],
+    data: [":generate_dex_testcases", ":generate_compiled_layout"],
     static_libs: [
         "android-support-test",
         "guava",
     ],
     manifest: "AndroidManifest.xml",
+    resource_dirs: ["res"],
     test_config: "AndroidTest.xml",
     test_suites: ["general-tests"],
 }
diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml
index 6f90cf3..68d8fdc 100644
--- a/startop/view_compiler/dex_builder_test/AndroidTest.xml
+++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml
@@ -25,6 +25,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" />
         <option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" />
+        <option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
new file mode 100644
index 0000000..0f9375c
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+   android:layout_width="match_parent"
+   android:layout_height="match_parent"
+   android:paddingLeft="16dp"
+   android:paddingRight="16dp"
+   android:orientation="vertical"
+   android:gravity="center">
+
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+ </LinearLayout>
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
new file mode 100644
index 0000000..ce3ce83
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.startop.test;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.view.View;
+import com.google.common.io.ByteStreams;
+import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import org.junit.Assert;
+import org.junit.Test;
+
+// Adding tests here requires changes in several other places. See README.md in
+// the view_compiler directory for more information.
+public class LayoutCompilerTest {
+    static ClassLoader loadDexFile(String filename) throws Exception {
+        return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
+                ClassLoader.getSystemClassLoader());
+    }
+
+    @Test
+    public void loadAndInflaterLayout1() throws Exception {
+        ClassLoader dex_file = loadDexFile("layout1.dex");
+        Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
+        Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class);
+        Context context = InstrumentationRegistry.getTargetContext();
+        layout1.invoke(null, context, R.layout.layout1);
+    }
+}
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
new file mode 100644
index 0000000..c68793d
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -0,0 +1,226 @@
+/*
+ * 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_layout_compiler.h"
+#include "layout_validation.h"
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+using android::base::StringPrintf;
+
+void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
+  if (0 == name.compare(u"merge")) {
+    message_ = "Merge tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"include")) {
+    message_ = "Include tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"view")) {
+    message_ = "View tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"fragment")) {
+    message_ = "Fragment tags are not supported";
+    can_compile_ = false;
+  }
+}
+
+DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
+    : method_{method},
+      context_{dex::Value::Parameter(0)},
+      resid_{dex::Value::Parameter(1)},
+      inflater_{method->MakeRegister()},
+      xml_{method->MakeRegister()},
+      attrs_{method->MakeRegister()},
+      classname_tmp_{method->MakeRegister()},
+      xml_next_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next",
+          dex::Prototype{dex::TypeDescriptor::Int()})},
+      try_create_view_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView",
+          dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
+                         dex::TypeDescriptor::FromClassname("android.view.View"),
+                         dex::TypeDescriptor::FromClassname("java.lang.String"),
+                         dex::TypeDescriptor::FromClassname("android.content.Context"),
+                         dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+      generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams",
+          dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
+                         dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+      add_view_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView",
+          dex::Prototype{
+              dex::TypeDescriptor::Void(),
+              dex::TypeDescriptor::FromClassname("android.view.View"),
+              dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})},
+      // The register stack starts with one register, which will be null for the root view.
+      register_stack_{{method->MakeRegister()}} {}
+
+void DexViewBuilder::Start() {
+  dex::DexBuilder* const dex = method_->dex_file();
+
+  // LayoutInflater inflater = LayoutInflater.from(context);
+  auto layout_inflater_from = dex->GetOrDeclareMethod(
+      dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+      "from",
+      dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+                     dex::TypeDescriptor::FromClassname("android.content.Context")});
+  method_->AddInstruction(
+      dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_));
+
+  // Resources res = context.getResources();
+  auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
+  auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
+  auto get_resources =
+      dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type});
+  method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_));
+
+  // XmlResourceParser xml = res.getLayout(resid);
+  auto xml_resource_parser_type =
+      dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
+  auto get_layout =
+      dex->GetOrDeclareMethod(resources_type,
+                              "getLayout",
+                              dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()});
+  method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_));
+
+  // AttributeSet attrs = Xml.asAttributeSet(xml);
+  auto as_attribute_set = dex->GetOrDeclareMethod(
+      dex::TypeDescriptor::FromClassname("android.util.Xml"),
+      "asAttributeSet",
+      dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"),
+                     dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
+  method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_));
+
+  // xml.next(); // start document
+  method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+}
+
+void DexViewBuilder::Finish() {}
+
+namespace {
+std::string ResolveName(const std::string& name) {
+  if (name == "View") return "android.view.View";
+  if (name == "ViewGroup") return "android.view.ViewGroup";
+  if (name.find(".") == std::string::npos) {
+    return StringPrintf("android.widget.%s", name.c_str());
+  }
+  return name;
+}
+}  // namespace
+
+void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
+  bool const is_root_view = view_stack_.empty();
+
+  // xml.next(); // start tag
+  method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+
+  dex::Value view = AcquireRegister();
+  // try to create the view using the factories
+  method_->BuildConstString(classname_tmp_,
+                            name);  // TODO: the need to fully qualify the classname
+  if (is_root_view) {
+    dex::Value null = AcquireRegister();
+    method_->BuildConst4(null, 0);
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_));
+    ReleaseRegister();
+  } else {
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_));
+  }
+  auto label = method_->MakeLabel();
+  // branch if not null
+  method_->AddInstruction(
+      dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
+
+  // If null, create the class directly.
+  method_->BuildNew(view,
+                    dex::TypeDescriptor::FromClassname(ResolveName(name)),
+                    dex::Prototype{dex::TypeDescriptor::Void(),
+                                   dex::TypeDescriptor::FromClassname("android.content.Context"),
+                                   dex::TypeDescriptor::FromClassname("android.util.AttributeSet")},
+                    context_,
+                    attrs_);
+
+  method_->AddInstruction(
+      dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label));
+
+  if (is_viewgroup) {
+    // Cast to a ViewGroup so we can add children later.
+    const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(
+        dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor());
+    method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index)));
+  }
+
+  if (!is_root_view) {
+    // layout_params = parent.generateLayoutParams(attrs);
+    dex::Value layout_params{AcquireRegister()};
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
+    view_stack_.push_back({view, layout_params});
+  } else {
+    view_stack_.push_back({view, {}});
+  }
+}
+
+void DexViewBuilder::FinishView() {
+  if (view_stack_.size() == 1) {
+    method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
+  } else {
+    // parent.add(view, layout_params)
+    method_->AddInstruction(dex::Instruction::InvokeVirtual(
+        add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
+    // xml.next(); // end tag
+    method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+  }
+  PopViewStack();
+}
+
+dex::Value DexViewBuilder::AcquireRegister() {
+  top_register_++;
+  if (register_stack_.size() == top_register_) {
+    register_stack_.push_back(method_->MakeRegister());
+  }
+  return register_stack_[top_register_];
+}
+
+void DexViewBuilder::ReleaseRegister() { top_register_--; }
+
+dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
+dex::Value DexViewBuilder::GetCurrentLayoutParams() const {
+  return view_stack_.back().layout_params.value();
+}
+dex::Value DexViewBuilder::GetParentView() const {
+  return view_stack_[view_stack_.size() - 2].view;
+}
+
+void DexViewBuilder::PopViewStack() {
+  const auto& top = view_stack_.back();
+  // release the layout params if we have them
+  if (top.layout_params.has_value()) {
+    ReleaseRegister();
+  }
+  // Unconditionally release the view register.
+  ReleaseRegister();
+  view_stack_.pop_back();
+}
+
+}  // namespace startop
\ No newline at end of file
diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h
new file mode 100644
index 0000000..170a1a6
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.h
@@ -0,0 +1,118 @@
+/*
+ * 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_LAYOUT_COMPILER_H_
+#define DEX_LAYOUT_COMPILER_H_
+
+#include "dex_builder.h"
+
+#include <codecvt>
+#include <locale>
+#include <string>
+#include <vector>
+
+namespace startop {
+
+// This visitor does the actual view compilation, using a supplied builder.
+template <typename Builder>
+class LayoutCompilerVisitor {
+ public:
+  explicit LayoutCompilerVisitor(Builder* builder) : builder_{builder} {}
+
+  void VisitStartDocument() { builder_->Start(); }
+  void VisitEndDocument() { builder_->Finish(); }
+  void VisitStartTag(const std::u16string& name) {
+    parent_stack_.push_back(ViewEntry{
+        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(name), {}});
+  }
+  void VisitEndTag() {
+    auto entry = parent_stack_.back();
+    parent_stack_.pop_back();
+
+    if (parent_stack_.empty()) {
+      GenerateCode(entry);
+    } else {
+      parent_stack_.back().children.push_back(entry);
+    }
+  }
+
+ private:
+  struct ViewEntry {
+    std::string name;
+    std::vector<ViewEntry> children;
+  };
+
+  void GenerateCode(const ViewEntry& view) {
+    builder_->StartView(view.name, !view.children.empty());
+    for (const auto& child : view.children) {
+      GenerateCode(child);
+    }
+    builder_->FinishView();
+  }
+
+  Builder* builder_;
+
+  std::vector<ViewEntry> parent_stack_;
+};
+
+class DexViewBuilder {
+ public:
+  DexViewBuilder(dex::MethodBuilder* method);
+
+  void Start();
+  void Finish();
+  void StartView(const std::string& name, bool is_viewgroup);
+  void FinishView();
+
+ private:
+  // Accessors for the stack of views that are under construction.
+  dex::Value AcquireRegister();
+  void ReleaseRegister();
+  dex::Value GetCurrentView() const;
+  dex::Value GetCurrentLayoutParams() const;
+  dex::Value GetParentView() const;
+  void PopViewStack();
+
+  dex::MethodBuilder* method_;
+
+  // Registers used for code generation
+  dex::Value const context_;
+  dex::Value const resid_;
+  const dex::Value inflater_;
+  const dex::Value xml_;
+  const dex::Value attrs_;
+  const dex::Value classname_tmp_;
+
+  const dex::MethodDeclData xml_next_;
+  const dex::MethodDeclData try_create_view_;
+  const dex::MethodDeclData generate_layout_params_;
+  const dex::MethodDeclData add_view_;
+
+  // used for keeping track of which registers are in use
+  size_t top_register_{0};
+  std::vector<dex::Value> register_stack_;
+
+  // Keep track of the views currently in progress.
+  struct ViewEntry {
+    dex::Value view;
+    std::optional<dex::Value> layout_params;
+  };
+  std::vector<ViewEntry> view_stack_;
+};
+
+}  // namespace startop
+
+#endif  // DEX_LAYOUT_COMPILER_H_
diff --git a/startop/view_compiler/java_lang_builder.cc b/startop/view_compiler/java_lang_builder.cc
index 0b8754f..920caee 100644
--- a/startop/view_compiler/java_lang_builder.cc
+++ b/startop/view_compiler/java_lang_builder.cc
@@ -67,7 +67,7 @@
           "}\n";     // end CompiledView
 }
 
-void JavaLangViewBuilder::StartView(const string& class_name) {
+void JavaLangViewBuilder::StartView(const string& class_name, bool /*is_viewgroup*/) {
   const string view_var = MakeVar("view");
   const string layout_var = MakeVar("layout");
   std::string parent = "null";
diff --git a/startop/view_compiler/java_lang_builder.h b/startop/view_compiler/java_lang_builder.h
index c8d20b2..69356d3 100644
--- a/startop/view_compiler/java_lang_builder.h
+++ b/startop/view_compiler/java_lang_builder.h
@@ -35,7 +35,7 @@
   void Finish() const;
 
   // Begin creating a view (i.e. process the opening tag)
-  void StartView(const std::string& class_name);
+  void StartView(const std::string& class_name, bool is_viewgroup);
   // Finish a view, after all of its child nodes have been processed.
   void FinishView();
 
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 609bcf3..11ecde2 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -16,8 +16,12 @@
 
 #include "gflags/gflags.h"
 
+#include "android-base/stringprintf.h"
+#include "apk_layout_compiler.h"
 #include "dex_builder.h"
+#include "dex_layout_compiler.h"
 #include "java_lang_builder.h"
+#include "layout_validation.h"
 #include "tinyxml_layout_parser.h"
 #include "util.h"
 
@@ -32,49 +36,67 @@
 namespace {
 
 using namespace tinyxml2;
+using android::base::StringPrintf;
+using startop::dex::ClassBuilder;
+using startop::dex::DexBuilder;
+using startop::dex::MethodBuilder;
+using startop::dex::Prototype;
+using startop::dex::TypeDescriptor;
 using namespace startop::util;
 using std::string;
 
 constexpr char kStdoutFilename[]{"stdout"};
 
+DEFINE_bool(apk, false, "Compile layouts in an APK");
 DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
+DEFINE_int32(infd, -1, "Read input from the given file descriptor");
 DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
 DEFINE_string(package, "", "The package name for the generated class (required)");
 
-class ViewCompilerXmlVisitor : public XMLVisitor {
+template <typename Visitor>
+class XmlVisitorAdapter : public XMLVisitor {
  public:
-  explicit ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
+  explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
 
   bool VisitEnter(const XMLDocument& /*doc*/) override {
-    builder_->Start();
+    visitor_->VisitStartDocument();
     return true;
   }
 
   bool VisitExit(const XMLDocument& /*doc*/) override {
-    builder_->Finish();
+    visitor_->VisitEndDocument();
     return true;
   }
 
   bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
-    builder_->StartView(element.Name());
+    visitor_->VisitStartTag(
+        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
+            element.Name()));
     return true;
   }
 
   bool VisitExit(const XMLElement& /*element*/) override {
-    builder_->FinishView();
+    visitor_->VisitEndTag();
     return true;
   }
 
  private:
-  JavaLangViewBuilder* builder_;
+  Visitor* visitor_;
 };
 
+template <typename Builder>
+void CompileLayout(XMLDocument* xml, Builder* builder) {
+  startop::LayoutCompilerVisitor visitor{builder};
+  XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
+  xml->Accept(&adapter);
+}
+
 }  // end namespace
 
 int main(int argc, char** argv) {
   constexpr size_t kProgramName = 0;
   constexpr size_t kFileNameParam = 1;
-  constexpr size_t kNumRequiredArgs = 2;
+  constexpr size_t kNumRequiredArgs = 1;
 
   gflags::SetUsageMessage(
       "Compile XML layout files into equivalent Java language code\n"
@@ -83,21 +105,37 @@
   gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
 
   gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
-  if (argc != kNumRequiredArgs || cmd.is_default) {
+  if (argc < kNumRequiredArgs || cmd.is_default) {
     gflags::ShowUsageWithFlags(argv[kProgramName]);
     return 1;
   }
 
-  if (FLAGS_dex) {
-    startop::dex::WriteTestDexFile("test.dex");
+  const bool is_stdout = FLAGS_out == kStdoutFilename;
+
+  std::ofstream outfile;
+  if (!is_stdout) {
+    outfile.open(FLAGS_out);
+  }
+
+  if (FLAGS_apk) {
+    const startop::CompilationTarget target =
+        FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage;
+    if (FLAGS_infd >= 0) {
+      startop::CompileApkLayoutsFd(
+          android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile);
+    } else {
+      if (argc < 2) {
+        gflags::ShowUsageWithFlags(argv[kProgramName]);
+        return 1;
+      }
+      const char* const filename = argv[kFileNameParam];
+      startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile);
+    }
     return 0;
   }
 
   const char* const filename = argv[kFileNameParam];
-  const string layout_name = FindLayoutNameFromFilename(filename);
-
-  // We want to generate Java language code to inflate exactly this layout. This means
-  // generating code to walk the resource XML too.
+  const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
 
   XMLDocument xml;
   xml.LoadFile(filename);
@@ -108,15 +146,27 @@
     return 1;
   }
 
-  std::ofstream outfile;
-  if (FLAGS_out != kStdoutFilename) {
-    outfile.open(FLAGS_out);
+  if (FLAGS_dex) {
+    DexBuilder dex_file;
+    string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str());
+    ClassBuilder compiled_view{dex_file.MakeClass(class_name)};
+    MethodBuilder method{compiled_view.CreateMethod(
+        layout_name,
+        Prototype{TypeDescriptor::FromClassname("android.view.View"),
+                  TypeDescriptor::FromClassname("android.content.Context"),
+                  TypeDescriptor::Int()})};
+    startop::DexViewBuilder builder{&method};
+    CompileLayout(&xml, &builder);
+    method.Encode();
+
+    slicer::MemView image{dex_file.CreateImage()};
+
+    (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size());
+  } else {
+    // Generate Java language output.
+    JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile};
+
+    CompileLayout(&xml, &builder);
   }
-  JavaLangViewBuilder builder{
-      FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile};
-
-  ViewCompilerXmlVisitor visitor{&builder};
-  xml.Accept(&visitor);
-
   return 0;
 }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2820836..dcaa499 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -239,6 +239,30 @@
             "android.telecom.event.HANDOVER_FAILED";
 
     public static class Details {
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(
+                prefix = { "DIRECTION_" },
+                value = {DIRECTION_UNKNOWN, DIRECTION_INCOMING, DIRECTION_OUTGOING})
+        public @interface CallDirection {}
+
+        /**
+         * Indicates that the call is neither and incoming nor an outgoing call.  This can be the
+         * case for calls reported directly by a {@link ConnectionService} in special cases such as
+         * call handovers.
+         */
+        public static final int DIRECTION_UNKNOWN = -1;
+
+        /**
+         * Indicates that the call is an incoming call.
+         */
+        public static final int DIRECTION_INCOMING = 0;
+
+        /**
+         * Indicates that the call is an outgoing call.
+         */
+        public static final int DIRECTION_OUTGOING = 1;
+
 
         /** Call can currently be put on hold or unheld. */
         public static final int CAPABILITY_HOLD = 0x00000001;
@@ -519,6 +543,7 @@
         private final Bundle mIntentExtras;
         private final long mCreationTimeMillis;
         private final CallIdentification mCallIdentification;
+        private final @CallDirection int mCallDirection;
 
         /**
          * Whether the supplied capabilities  supports the specified capability.
@@ -838,6 +863,14 @@
             return mCallIdentification;
         }
 
+        /**
+         * Indicates whether the call is an incoming or outgoing call.
+         * @return The call's direction.
+         */
+        public @CallDirection int getCallDirection() {
+            return mCallDirection;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -859,7 +892,8 @@
                         areBundlesEqual(mExtras, d.mExtras) &&
                         areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
                         Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
-                        Objects.equals(mCallIdentification, d.mCallIdentification);
+                        Objects.equals(mCallIdentification, d.mCallIdentification) &&
+                        Objects.equals(mCallDirection, d.mCallDirection);
             }
             return false;
         }
@@ -881,7 +915,8 @@
                             mExtras,
                             mIntentExtras,
                             mCreationTimeMillis,
-                            mCallIdentification);
+                            mCallIdentification,
+                            mCallDirection);
         }
 
         /** {@hide} */
@@ -902,7 +937,8 @@
                 Bundle extras,
                 Bundle intentExtras,
                 long creationTimeMillis,
-                CallIdentification callIdentification) {
+                CallIdentification callIdentification,
+                int callDirection) {
             mTelecomCallId = telecomCallId;
             mHandle = handle;
             mHandlePresentation = handlePresentation;
@@ -920,6 +956,7 @@
             mIntentExtras = intentExtras;
             mCreationTimeMillis = creationTimeMillis;
             mCallIdentification = callIdentification;
+            mCallDirection = callDirection;
         }
 
         /** {@hide} */
@@ -941,7 +978,8 @@
                     parcelableCall.getExtras(),
                     parcelableCall.getIntentExtras(),
                     parcelableCall.getCreationTimeMillis(),
-                    parcelableCall.getCallIdentification());
+                    parcelableCall.getCallIdentification(),
+                    parcelableCall.getCallDirection());
         }
 
         @Override
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
index 97af06c..87834fd 100644
--- a/telecomm/java/android/telecom/CallIdentification.java
+++ b/telecomm/java/android/telecom/CallIdentification.java
@@ -250,8 +250,8 @@
         mDetails = details;
         mPhoto = photo;
         mNuisanceConfidence = nuisanceConfidence;
-        mCallScreeningAppName = callScreeningPackageName;
-        mCallScreeningPackageName = callScreeningAppName;
+        mCallScreeningAppName = callScreeningAppName;
+        mCallScreeningPackageName = callScreeningPackageName;
     }
 
     private String mName;
@@ -430,4 +430,22 @@
         return Objects.hash(mName, mDescription, mDetails, mPhoto, mNuisanceConfidence,
                 mCallScreeningAppName, mCallScreeningPackageName);
     }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[CallId mName=");
+        sb.append(Log.pii(mName));
+        sb.append(", mDesc=");
+        sb.append(mDescription);
+        sb.append(", mDet=");
+        sb.append(mDetails);
+        sb.append(", conf=");
+        sb.append(mNuisanceConfidence);
+        sb.append(", appName=");
+        sb.append(mCallScreeningAppName);
+        sb.append(", pkgName=");
+        sb.append(mCallScreeningPackageName);
+        return sb.toString();
+    }
 }
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index b906d0b..3299117 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.app.Service;
 import android.content.Intent;
@@ -27,8 +28,8 @@
 import android.os.RemoteException;
 
 import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallRedirectionService;
 import com.android.internal.telecom.ICallRedirectionAdapter;
+import com.android.internal.telecom.ICallRedirectionService;
 
 /**
  * This service can be implemented to interact between Telecom and its implementor
@@ -62,22 +63,35 @@
 
     /**
      * Telecom calls this method to inform the implemented {@link CallRedirectionService} of
-     * a new outgoing call which is being placed.
+     * a new outgoing call which is being placed. Telecom does not request to redirect emergency
+     * calls and does not request to redirect calls with gateway information.
      *
-     * The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
-     * {@link #redirectCall(Uri, PhoneAccountHandle)}, and {@link #cancelCall()} only from here.
+     * <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from
+     * the implemented {@link CallRedirectionService} set by users.
      *
-     * @param handle the phone number dialed by the user
-     * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+     * <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
+     * {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only
+     * from here.
+     *
+     * @param handle the phone number dialed by the user, represented in E.164 format if possible
+     * @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+     * @param allowInteractiveResponse a boolean to tell if the implemented
+     *                                 {@link CallRedirectionService} should allow interactive
+     *                                 responses with users. Will be {@code false} if, for example
+     *                                 the device is in car mode and the user would not be able to
+     *                                 interact with their device.
      */
-    public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount);
+    public abstract void onPlaceCall(@NonNull Uri handle,
+                                     @NonNull PhoneAccountHandle initialPhoneAccount,
+                                     boolean allowInteractiveResponse);
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes
-     * are required to the outgoing call, and that the call should be placed as-is.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * no changes are required to the outgoing call, and that the call should be placed as-is.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      */
     public final void placeCallUnmodified() {
@@ -89,29 +103,39 @@
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes
-     * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing
+     * call. Telecom will cancel the call if the implemented {@link CallRedirectionService}
+     * replies Telecom a handle for an emergency number.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      * @param handle the new phone number to dial
      * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
      *                           If {@code null}, no change will be made to the
      *                           {@link PhoneAccountHandle} used to place the call.
+     * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog
+     *                     if the confirmFirst is true, and if the redirection request of this
+     *                     response was sent with a true flag of allowInteractiveResponse via
+     *                     {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}
      */
-    public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
+    public final void redirectCall(@NonNull Uri handle,
+                                   @NonNull PhoneAccountHandle targetPhoneAccount,
+                                   boolean confirmFirst) {
         try {
-            mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount);
+            mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount, confirmFirst);
         } catch (RemoteException e) {
         }
     }
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing
-     * call should be canceled entirely.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * an outgoing call should be canceled entirely.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      */
     public final void cancelCall() {
@@ -137,7 +161,8 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
-                        onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3);
+                        onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3,
+                                (boolean) args.arg4);
                     } finally {
                         args.recycle();
                     }
@@ -152,15 +177,20 @@
          * Telecom calls this method to inform the CallRedirectionService of a new outgoing call
          * which is about to be placed.
          * @param handle the phone number dialed by the user
-         * @param targetPhoneAccount the URI of the number the user dialed
+         * @param initialPhoneAccount the URI of the number the user dialed
+         * @param allowInteractiveResponse a boolean to tell if the implemented
+         *                                 {@link CallRedirectionService} should allow interactive
+         *                                 responses with users.
          */
         @Override
-        public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
-                              PhoneAccountHandle targetPhoneAccount) {
+        public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle,
+                              @NonNull PhoneAccountHandle initialPhoneAccount,
+                              boolean allowInteractiveResponse) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = adapter;
             args.arg2 = handle;
-            args.arg3 = targetPhoneAccount;
+            args.arg3 = initialPhoneAccount;
+            args.arg4 = allowInteractiveResponse;
             mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
         }
     }
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index be96b3c..826ad82 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -21,6 +21,7 @@
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -33,8 +34,9 @@
 
 /**
  * This service can be implemented by the default dialer (see
- * {@link TelecomManager#getDefaultDialerPackage()}) to allow or disallow incoming calls before
- * they are shown to a user.
+ * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
+ * incoming calls before they are shown to a user.  This service can also provide
+ * {@link CallIdentification} information for calls.
  * <p>
  * Below is an example manifest registration for a {@code CallScreeningService}.
  * <pre>
@@ -56,6 +58,34 @@
  *     information about a {@link Call.Details call} which will be shown to the user in the
  *     Dialer app.</li>
  * </ol>
+ * <p>
+ * <h2>Becoming the {@link CallScreeningService}</h2>
+ * Telecom will bind to a single app chosen by the user which implements the
+ * {@link CallScreeningService} API when there are new incoming and outgoing calls.
+ * <p>
+ * The code snippet below illustrates how your app can request that it fills the call screening
+ * role.
+ * <pre>
+ * {@code
+ * private static final int REQUEST_ID = 1;
+ *
+ * public void requestRole() {
+ *     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
+ *     Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING_APP");
+ *     startActivityForResult(intent, REQUEST_ID);
+ * }
+ *
+ * &#64;Override
+ * public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ *     if (requestCode == REQUEST_ID) {
+ *         if (resultCode == android.app.Activity.RESULT_OK) {
+ *             // Your app is now the call screening app
+ *         } else {
+ *             // Your app is not the call screening app
+ *         }
+ *     }
+ * }
+ * </pre>
  */
 public abstract class CallScreeningService extends Service {
     /**
@@ -222,30 +252,46 @@
     }
 
     /**
-     * Called when a new incoming call is added.
-     * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
-     * should be called to allow or disallow the call.
+     * Called when a new incoming or outgoing call is added which is not in the user's contact list.
+     * <p>
+     * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
+     * calling
+     * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
+     * Your app can tell if a call is an incoming call by checking to see if
+     * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
+     * <p>
+     * For incoming or outgoing calls, the {@link CallScreeningService} can call
+     * {@link #provideCallIdentification(Call.Details, CallIdentification)} in order to provide
+     * {@link CallIdentification} for the call.
      * <p>
      * Note: The {@link Call.Details} instance provided to a call screening service will only have
      * the following properties set.  The rest of the {@link Call.Details} properties will be set to
      * their default value or {@code null}.
      * <ul>
-     *     <li>{@link Call.Details#getState()}</li>
+     *     <li>{@link Call.Details#getCallDirection()}</li>
      *     <li>{@link Call.Details#getConnectTimeMillis()}</li>
      *     <li>{@link Call.Details#getCreationTimeMillis()}</li>
      *     <li>{@link Call.Details#getHandle()}</li>
      *     <li>{@link Call.Details#getHandlePresentation()}</li>
      * </ul>
+     * <p>
+     * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
+     * is {@link PhoneAccount#SCHEME_TEL} are passed for call
+     * screening.  Further, only calls which are not in the user's contacts are passed for
+     * screening.  For outgoing calls, no post-dial digits are passed.
      *
-     * @param callDetails Information about a new incoming call, see {@link Call.Details}.
+     * @param callDetails Information about a new call, see {@link Call.Details}.
      */
     public abstract void onScreenCall(@NonNull Call.Details callDetails);
 
     /**
-     * Responds to the given call, either allowing it or disallowing it.
+     * Responds to the given incoming call, either allowing it or disallowing it.
      * <p>
      * The {@link CallScreeningService} calls this method to inform the system whether the call
      * should be silently blocked or not.
+     * <p>
+     * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
+     * {@link Call.Details#DIRECTION_INCOMING}.
      *
      * @param callDetails The call to allow.
      *                    <p>
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8425603..05d5a13 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -505,6 +505,14 @@
             "android.telecom.extra.ORIGINAL_CONNECTION_ID";
 
     /**
+     * Boolean connection extra key set on the extras passed to
+     * {@link Connection#sendConnectionEvent} which indicates that audio is present
+     * on the RTT call when the extra value is true.
+     */
+    public static final String EXTRA_IS_RTT_AUDIO_PRESENT =
+            "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
+
+    /**
      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
      * {@link #sendConnectionEvent(String, Bundle)}.
@@ -619,6 +627,13 @@
      */
     public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
 
+    /**
+     * Connection event used to inform an {@link InCallService} that the RTT audio indication
+     * has changed.
+     */
+    public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED =
+            "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
+
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
 
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 1aeeca7..f5f0af7 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -40,11 +40,30 @@
 import java.util.List;
 
 /**
- * This service is implemented by any app that wishes to provide the user-interface for managing
- * phone calls. Telecom binds to this service while there exists a live (active or incoming) call,
- * and uses it to notify the in-call app of any live and recently disconnected calls. An app must
- * first be set as the default phone app (See {@link TelecomManager#getDefaultDialerPackage()})
- * before the telecom service will bind to its {@code InCallService} implementation.
+ * This service is implemented by an app that wishes to provide functionality for managing
+ * phone calls.
+ * <p>
+ * There are three types of apps which Telecom can bind to when there exists a live (active or
+ * incoming) call:
+ * <ol>
+ *     <li>Default Dialer/Phone app - the default dialer/phone app is one which provides the
+ *     in-call user interface while the device is in a call.  A device is bundled with a system
+ *     provided default dialer/phone app.  The user may choose a single app to take over this role
+ *     from the system app.</li>
+ *     <li>Default Car-mode Dialer/Phone app - the default car-mode dialer/phone app is one which
+ *     provides the in-call user interface while the device is in a call and the device is in car
+ *     mode.  The user may choose a single app to fill this role.</li>
+ *     <li>Call Companion app - a call companion app is one which provides no user interface itself,
+ *     but exposes call information to another display surface, such as a wearable device.  The
+ *     user may choose multiple apps to fill this role.</li>
+ * </ol>
+ * <p>
+ * Apps which wish to fulfill one of the above roles use the {@link android.app.role.RoleManager}
+ * to request that they fill the desired role.
+ *
+ * <h2>Becoming the Default Phone App</h2>
+ * An app filling the role of the default phone app provides a user interface while the device is in
+ * a call, and the device is not in car mode.
  * <p>
  * Below is an example manifest registration for an {@code InCallService}. The meta-data
  * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
@@ -82,12 +101,34 @@
  * }
  * </pre>
  * <p>
- * When a user installs your application and runs it for the first time, you should prompt the user
- * to see if they would like your application to be the new default phone app.  See the
- * {@link TelecomManager#ACTION_CHANGE_DEFAULT_DIALER} intent documentation for more information on
- * how to do this.
+ * When a user installs your application and runs it for the first time, you should use the
+ * {@link android.app.role.RoleManager} to prompt the user to see if they would like your app to
+ * be the new default phone app.
+ * <p id="requestRole">
+ * The code below shows how your app can request to become the default phone/dialer app:
+ * <pre>
+ * {@code
+ * private static final int REQUEST_ID = 1;
+ *
+ * public void requestRole() {
+ *     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
+ *     Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
+ *     startActivityForResult(intent, REQUEST_ID);
+ * }
+ *
+ * &#64;Override
+ * public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ *     if (requestCode == REQUEST_ID) {
+ *         if (resultCode == android.app.Activity.RESULT_OK) {
+ *             // Your app is now the default dialer app
+ *         } else {
+ *             // Your app is not the default dialer app
+ *         }
+ *     }
+ * }
+ * </pre>
  * <p id="incomingCallNotification">
- * <h2>Showing the Incoming Call Notification</h2>
+ * <h3>Showing the Incoming Call Notification</h3>
  * When your app receives a new incoming call via {@link InCallService#onCallAdded(Call)}, it is
  * responsible for displaying an incoming call UI for the incoming call.  It should do this using
  * {@link android.app.NotificationManager} APIs to post a new incoming call notification.
@@ -121,7 +162,7 @@
  * heads-up notification if the user is actively using the phone.  When the user is not using the
  * phone, your full-screen incoming call UI is used instead.
  * For example:
- * <pre><code>
+ * <pre><code>{@code
  * // Create an intent which triggers your fullscreen incoming call user interface.
  * Intent intent = new Intent(Intent.ACTION_MAIN, null);
  * intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -151,7 +192,49 @@
  * NotificationManager notificationManager = mContext.getSystemService(
  *     NotificationManager.class);
  * notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
- * </code></pre>
+ * }</pre>
+ * <p>
+ * <h2>Becoming the Default Car-mode Phone App</h2>
+ * An app filling the role of the default car-mode dialer/phone app provides a user interface while
+ * the device is in a call, and in car mode.  See
+ * {@link android.app.UiModeManager#ACTION_ENTER_CAR_MODE} for more information about car mode.
+ * When the device is in car mode, Telecom binds to the default car-mode dialer/phone app instead
+ * of the usual dialer/phone app.
+ * <p>
+ * Similar to the requirements for becoming the default dialer/phone app, your app must declare a
+ * manifest entry for its {@link InCallService} implementation.  Your manifest entry should ensure
+ * the following conditions are met:
+ * <ul>
+ *     <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li>
+ *     <li>Set the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI} metadata to
+ *     {@code true}<li>
+ *     <li>Your app must request the permission
+ *     {@link android.Manifest.permission.CALL_COMPANION_APP}.</li>
+ * </ul>
+ * <p>
+ * Your app should request to fill the role {@code android.app.role.CAR_MODE_DIALER_APP} in order to
+ * become the default (see <a href="#requestRole">above</a> for how to request your app fills this
+ * role).
+ *
+ * <h2>Becoming a Call Companion App</h2>
+ * An app which fills the companion app role does not directly provide a user interface while the
+ * device is in a call.  Instead, it is typically used to relay information about calls to another
+ * display surface, such as a wearable device.
+ * <p>
+ * Similar to the requirements for becoming the default dialer/phone app, your app must declare a
+ * manifest entry for its {@link InCallService} implementation.  Your manifest entry should
+ * ensure the following conditions are met:
+ * <ul>
+ *     <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li>
+ *     <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI}
+ *     metadata.</li>
+ *     <li>Your app must request the permission
+ *     {@link android.Manifest.permission.CALL_COMPANION_APP}.</li>
+ * </ul>
+ * <p>
+ * Your app should request to fill the role {@code android.app.role.CALL_COMPANION_APP} in order to
+ * become a call companion app (see <a href="#requestRole">above</a> for how to request your app
+ * fills this role).
  */
 public abstract class InCallService extends Service {
 
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 911786e..f7dec83 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.telecom.Call.Details.CallDirection;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -64,6 +65,7 @@
     private final Bundle mExtras;
     private final long mCreationTimeMillis;
     private final CallIdentification mCallIdentification;
+    private final int mCallDirection;
 
     public ParcelableCall(
             String id,
@@ -92,7 +94,8 @@
             Bundle intentExtras,
             Bundle extras,
             long creationTimeMillis,
-            CallIdentification callIdentification) {
+            CallIdentification callIdentification,
+            int callDirection) {
         mId = id;
         mState = state;
         mDisconnectCause = disconnectCause;
@@ -120,6 +123,7 @@
         mExtras = extras;
         mCreationTimeMillis = creationTimeMillis;
         mCallIdentification = callIdentification;
+        mCallDirection = callDirection;
     }
 
     /** The unique ID of the call. */
@@ -318,6 +322,13 @@
         return mCallIdentification;
     }
 
+    /**
+     * Indicates whether the call is an incoming or outgoing call.
+     */
+    public @CallDirection int getCallDirection() {
+        return mCallDirection;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
@@ -356,6 +367,7 @@
             ParcelableRttCall rttCall = source.readParcelable(classLoader);
             long creationTimeMillis = source.readLong();
             CallIdentification callIdentification = source.readParcelable(classLoader);
+            int callDirection = source.readInt();
             return new ParcelableCall(
                     id,
                     state,
@@ -383,7 +395,8 @@
                     intentExtras,
                     extras,
                     creationTimeMillis,
-                    callIdentification);
+                    callIdentification,
+                    callDirection);
         }
 
         @Override
@@ -429,6 +442,7 @@
         destination.writeParcelable(mRttCall, 0);
         destination.writeLong(mCreationTimeMillis);
         destination.writeParcelable(mCallIdentification, 0);
+        destination.writeInt(mCallDirection);
     }
 
     @Override
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
index 46bf983..0a42a3f 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
@@ -31,5 +31,6 @@
 
     void placeCallUnmodified();
 
-    void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount);
+    void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount,
+            boolean confirmFirst);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
index d8d360b..c1bc440 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -30,5 +30,5 @@
  */
 oneway interface ICallRedirectionService {
     void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
-            in PhoneAccountHandle targetPhoneAccount);
+            in PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse);
 }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index c115a4b..15abdb7 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2019,6 +2019,12 @@
 
     /**
      * Helper functions for the "threads" table used by MMS and SMS.
+     *
+     * Thread IDs are determined by the participants in a conversation and can be used to match
+     * both SMS and MMS messages.
+     *
+     * To avoid issues where applications might cache a thread ID, the thread ID of a deleted thread
+     * must not be reused to point at a new thread.
      */
     public static final class Threads implements ThreadsColumns {
 
@@ -2072,14 +2078,10 @@
         }
 
         /**
-         * Given the recipients list and subject of an unsaved message,
-         * return its thread ID.  If the message starts a new thread,
-         * allocate a new thread ID.  Otherwise, use the appropriate
-         * existing thread ID.
-         *
-         * <p>Find the thread ID of the same set of recipients (in any order,
-         * without any additions). If one is found, return it. Otherwise,
-         * return a unique thread ID.</p>
+         * Given a set of recipients return its thread ID.
+         * <p>
+         * If a thread exists containing the provided participants, return its thread ID. Otherwise,
+         * this will create a new thread containing the provided participants and return its ID.
          */
         public static long getOrCreateThreadId(
                 Context context, Set<String> recipients) {
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index fe07370..4da79b3 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -110,6 +110,7 @@
     private AvailableNetworkInfo(Parcel in) {
         mSubId = in.readInt();
         mPriority = in.readInt();
+        mMccMncs = new ArrayList<>();
         in.readStringList(mMccMncs);
     }
 
diff --git a/telephony/java/android/telephony/CallAttributes.aidl b/telephony/java/android/telephony/CallAttributes.aidl
new file mode 100644
index 0000000..69127df
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.aidl
@@ -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.
+ */
+
+package android.telephony;
+
+parcelable CallAttributes;
+
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
new file mode 100644
index 0000000..2b99ce1
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -0,0 +1,151 @@
+/*
+ * 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.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyManager.NetworkType;
+
+import java.util.Objects;
+
+/**
+ * Contains information about a call's attributes as passed up from the HAL. If there are multiple
+ * ongoing calls, the CallAttributes will pertain to the call in the foreground.
+ * @hide
+ */
+@SystemApi
+public class CallAttributes implements Parcelable {
+    private PreciseCallState mPreciseCallState;
+    @NetworkType
+    private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
+    private CallQuality mCallQuality;
+
+
+    public CallAttributes(PreciseCallState state, @NetworkType int networkType,
+            CallQuality callQuality) {
+        this.mPreciseCallState = state;
+        this.mNetworkType = networkType;
+        this.mCallQuality = callQuality;
+    }
+
+    @Override
+    public String toString() {
+        return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType
+                + " mCallQuality=" + mCallQuality;
+    }
+
+    private CallAttributes(Parcel in) {
+        mPreciseCallState = (PreciseCallState) in.readValue(mPreciseCallState.getClass()
+                .getClassLoader());
+        mNetworkType = in.readInt();
+        mCallQuality = (CallQuality) in.readValue(mCallQuality.getClass().getClassLoader());
+    }
+
+    // getters
+    /**
+     * Returns the {@link PreciseCallState} of the call.
+     */
+    public PreciseCallState getPreciseCallState() {
+        return mPreciseCallState;
+    }
+
+    /**
+     * Returns the {@link TelephonyManager#NetworkType} of the call.
+     *
+     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+     * @see TelephonyManager#NETWORK_TYPE_GPRS
+     * @see TelephonyManager#NETWORK_TYPE_EDGE
+     * @see TelephonyManager#NETWORK_TYPE_UMTS
+     * @see TelephonyManager#NETWORK_TYPE_CDMA
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+     * @see TelephonyManager#NETWORK_TYPE_1xRTT
+     * @see TelephonyManager#NETWORK_TYPE_HSDPA
+     * @see TelephonyManager#NETWORK_TYPE_HSUPA
+     * @see TelephonyManager#NETWORK_TYPE_HSPA
+     * @see TelephonyManager#NETWORK_TYPE_IDEN
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+     * @see TelephonyManager#NETWORK_TYPE_LTE
+     * @see TelephonyManager#NETWORK_TYPE_EHRPD
+     * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * @see TelephonyManager#NETWORK_TYPE_GSM
+     * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA
+     * @see TelephonyManager#NETWORK_TYPE_IWLAN
+     * @see TelephonyManager#NETWORK_TYPE_LTE_CA
+     * @see TelephonyManager#NETWORK_TYPE_NR
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Returns the {#link CallQuality} of the call.
+     */
+    public CallQuality getCallQuality() {
+        return mCallQuality;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof CallAttributes) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallAttributes s = (CallAttributes) o;
+
+        return (mPreciseCallState == s.mPreciseCallState
+                && mNetworkType == s.mNetworkType
+                && mCallQuality == s.mCallQuality);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @Parcelable.ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+        mPreciseCallState.writeToParcel(dest, flags);
+        dest.writeInt(mNetworkType);
+        mCallQuality.writeToParcel(dest, flags);
+    }
+
+    public static final Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() {
+        public CallAttributes createFromParcel(Parcel in) {
+            return new CallAttributes(in);
+        }
+
+        public CallAttributes[] newArray(int size) {
+            return new CallAttributes[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/CallQuality.aidl b/telephony/java/android/telephony/CallQuality.aidl
new file mode 100644
index 0000000..f54355f
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.aidl
@@ -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.
+ */
+
+package android.telephony;
+
+parcelable CallQuality;
+
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
new file mode 100644
index 0000000..b27f6b4
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -0,0 +1,341 @@
+/*
+ * 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.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Parcelable object to handle call quality.
+ * <p>
+ * Currently this supports IMS calls.
+ * <p>
+ * It provides the call quality level, duration, and additional information related to RTP packets,
+ * jitter and delay.
+ * <p>
+ * If there are multiple active calls, the CallQuality will pertain to the call in the foreground.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CallQuality implements Parcelable {
+
+    // Constants representing the call quality level (see #CallQuality);
+    public static final int CALL_QUALITY_EXCELLENT = 0;
+    public static final int CALL_QUALITY_GOOD = 1;
+    public static final int CALL_QUALITY_FAIR = 2;
+    public static final int CALL_QUALITY_POOR = 3;
+    public static final int CALL_QUALITY_BAD = 4;
+    public static final int CALL_QUALITY_NOT_AVAILABLE = 5;
+
+    /**
+     * Call quality
+     * @hide
+     */
+    @IntDef(prefix = { "CALL_QUALITY_" }, value = {
+            CALL_QUALITY_EXCELLENT,
+            CALL_QUALITY_GOOD,
+            CALL_QUALITY_FAIR,
+            CALL_QUALITY_POOR,
+            CALL_QUALITY_BAD,
+            CALL_QUALITY_NOT_AVAILABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallQualityLevel {}
+
+    @CallQualityLevel
+    private int mDownlinkCallQualityLevel;
+    @CallQualityLevel
+    private int mUplinkCallQualityLevel;
+    private int mCallDuration;
+    private int mNumRtpPacketsTransmitted;
+    private int mNumRtpPacketsReceived;
+    private int mNumRtpPacketsTransmittedLost;
+    private int mNumRtpPacketsNotReceived;
+    private int mAverageRelativeJitter;
+    private int mMaxRelativeJitter;
+    private int mAverageRoundTripTime;
+    private int mCodecType;
+
+    /** @hide **/
+    public CallQuality(Parcel in) {
+        mDownlinkCallQualityLevel = in.readInt();
+        mUplinkCallQualityLevel = in.readInt();
+        mCallDuration = in.readInt();
+        mNumRtpPacketsTransmitted = in.readInt();
+        mNumRtpPacketsReceived = in.readInt();
+        mNumRtpPacketsTransmittedLost = in.readInt();
+        mNumRtpPacketsNotReceived = in.readInt();
+        mAverageRelativeJitter = in.readInt();
+        mMaxRelativeJitter = in.readInt();
+        mAverageRoundTripTime = in.readInt();
+        mCodecType = in.readInt();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param callQualityLevel the call quality level (see #CallQualityLevel)
+     * @param callDuration the call duration in milliseconds
+     * @param numRtpPacketsTransmitted RTP packets sent to network
+     * @param numRtpPacketsReceived RTP packets received from network
+     * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+     * transmitted
+     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved
+     * @param averageRelativeJitter average relative jitter in milliseconds
+     * @param maxRelativeJitter maximum relative jitter in milliseconds
+     * @param averageRoundTripTime average round trip delay in milliseconds
+     * @param codecType the codec type
+     */
+    public CallQuality(
+            @CallQualityLevel int downlinkCallQualityLevel,
+            @CallQualityLevel int uplinkCallQualityLevel,
+            int callDuration,
+            int numRtpPacketsTransmitted,
+            int numRtpPacketsReceived,
+            int numRtpPacketsTransmittedLost,
+            int numRtpPacketsNotReceived,
+            int averageRelativeJitter,
+            int maxRelativeJitter,
+            int averageRoundTripTime,
+            int codecType) {
+        this.mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+        this.mUplinkCallQualityLevel = uplinkCallQualityLevel;
+        this.mCallDuration = callDuration;
+        this.mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+        this.mNumRtpPacketsReceived = numRtpPacketsReceived;
+        this.mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+        this.mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+        this.mAverageRelativeJitter = averageRelativeJitter;
+        this.mMaxRelativeJitter = maxRelativeJitter;
+        this.mAverageRoundTripTime = averageRoundTripTime;
+        this.mCodecType = codecType;
+    }
+
+    // getters
+    /**
+     * Returns the downlink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getDownlinkCallQualityLevel() {
+        return mDownlinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the uplink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getUplinkCallQualityLevel() {
+        return mUplinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the duration of the call, in milliseconds.
+     */
+    public int getCallDuration() {
+        return mCallDuration;
+    }
+
+    /**
+     * Returns the total number of RTP packets transmitted by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsTransmitted() {
+        return mNumRtpPacketsTransmitted;
+    }
+
+    /**
+     * Returns the total number of RTP packets received by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsReceived() {
+        return mNumRtpPacketsReceived;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by this device but were lost in the
+     * network before reaching the other party.
+     */
+    public int getNumRtpPacketsTransmittedLost() {
+        return mNumRtpPacketsTransmittedLost;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by the other party but were lost in the
+     * network before reaching this device.
+     */
+    public int getNumRtpPacketsNotReceived() {
+        return mNumRtpPacketsNotReceived;
+    }
+
+    /**
+     * Returns the average relative jitter in milliseconds. Jitter represents the amount of variance
+     * in interarrival time of packets, for example, if two packets are sent 2 milliseconds apart
+     * but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getAverageRelativeJitter() {
+        return mAverageRelativeJitter;
+    }
+
+    /**
+     * Returns the maximum relative jitter for a given ongoing call. Jitter represents the amount of
+     * variance in interarrival time of packets, for example, if two packets are sent 2 milliseconds
+     * apart but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getMaxRelativeJitter() {
+        return mMaxRelativeJitter;
+    }
+
+    /**
+     * Returns the average round trip time in milliseconds.
+     */
+    public int getAverageRoundTripTime() {
+        return mAverageRoundTripTime;
+    }
+
+    /**
+     * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
+     * {@link ImsStreamMediaProfile}.
+     *
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_NONE
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_QCELP13K
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_B
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_NW
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_EFR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_FR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_HR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711U
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G723
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711A
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G722
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711AB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G729
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_NB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_SWB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_FB
+     */
+    public int getCodecType() {
+        return mCodecType;
+    }
+
+    // Parcelable things
+    @Override
+    public String toString() {
+        return "CallQuality: {downlinkCallQualityLevel=" + mDownlinkCallQualityLevel
+                + " uplinkCallQualityLevel=" + mUplinkCallQualityLevel
+                + " callDuration=" + mCallDuration
+                + " numRtpPacketsTransmitted=" + mNumRtpPacketsTransmitted
+                + " numRtpPacketsReceived=" + mNumRtpPacketsReceived
+                + " numRtpPacketsTransmittedLost=" + mNumRtpPacketsTransmittedLost
+                + " numRtpPacketsNotReceived=" + mNumRtpPacketsNotReceived
+                + " averageRelativeJitter=" + mAverageRelativeJitter
+                + " maxRelativeJitter=" + mMaxRelativeJitter
+                + " averageRoundTripTime=" + mAverageRoundTripTime
+                + " codecType=" + mCodecType
+                + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mDownlinkCallQualityLevel,
+                mUplinkCallQualityLevel,
+                mCallDuration,
+                mNumRtpPacketsTransmitted,
+                mNumRtpPacketsReceived,
+                mNumRtpPacketsTransmittedLost,
+                mNumRtpPacketsNotReceived,
+                mAverageRelativeJitter,
+                mMaxRelativeJitter,
+                mAverageRoundTripTime,
+                mCodecType);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallQuality s = (CallQuality) o;
+
+        return (mDownlinkCallQualityLevel == s.mDownlinkCallQualityLevel
+                && mUplinkCallQualityLevel == s.mUplinkCallQualityLevel
+                && mCallDuration == s.mCallDuration
+                && mNumRtpPacketsTransmitted == s.mNumRtpPacketsTransmitted
+                && mNumRtpPacketsReceived == s.mNumRtpPacketsReceived
+                && mNumRtpPacketsTransmittedLost == s.mNumRtpPacketsTransmittedLost
+                && mNumRtpPacketsNotReceived == s.mNumRtpPacketsNotReceived
+                && mAverageRelativeJitter == s.mAverageRelativeJitter
+                && mMaxRelativeJitter == s.mMaxRelativeJitter
+                && mAverageRoundTripTime == s.mAverageRoundTripTime
+                && mCodecType == s.mCodecType);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @Parcelable.ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+        dest.writeInt(mDownlinkCallQualityLevel);
+        dest.writeInt(mUplinkCallQualityLevel);
+        dest.writeInt(mCallDuration);
+        dest.writeInt(mNumRtpPacketsTransmitted);
+        dest.writeInt(mNumRtpPacketsReceived);
+        dest.writeInt(mNumRtpPacketsTransmittedLost);
+        dest.writeInt(mNumRtpPacketsNotReceived);
+        dest.writeInt(mAverageRelativeJitter);
+        dest.writeInt(mMaxRelativeJitter);
+        dest.writeInt(mAverageRoundTripTime);
+        dest.writeInt(mCodecType);
+    }
+
+    public static final Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
+        public CallQuality createFromParcel(Parcel in) {
+            return new CallQuality(in);
+        }
+
+        public CallQuality[] newArray(int size) {
+            return new CallQuality[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a9a128b..312b318 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2231,7 +2231,7 @@
      * e.g.) To use RSCP by default, set the value to "rscp". The signal strength level will
      * then be determined by #KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY
      * <p>
-     * Currently this only supports the value "rscp"
+     * Currently this supports the value "rscp" and "rssi".
      * @hide
      */
     // FIXME: this key and related keys must not be exposed without a consistent philosophy for
@@ -2875,7 +2875,7 @@
                         -95, /* SIGNAL_STRENGTH_GOOD */
                         -85  /* SIGNAL_STRENGTH_GREAT */
                 });
-        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
+        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
         sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL, false);
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.aidl b/telephony/java/android/telephony/CarrierRestrictionRules.aidl
new file mode 100644
index 0000000..8b1f0b9
--- /dev/null
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/** @hide */
+package android.telephony;
+
+parcelable CarrierRestrictionRules;
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
new file mode 100644
index 0000000..37847ae
--- /dev/null
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -0,0 +1,277 @@
+/*
+ * 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.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains the list of carrier restrictions.
+ * Allowed list: it indicates the list of carriers that are allowed.
+ * Excluded list: it indicates the list of carriers that are excluded.
+ * Default carrier restriction: it indicates the default behavior and the priority between the two
+ * lists:
+ *  - not allowed: the device only allows usage of carriers that are present in the allowed list
+ *    and not present in the excluded list. This implies that if a carrier is not present in either
+ *    list, it is not allowed.
+ *  - allowed: the device allows all carriers, except those present in the excluded list and not
+ *    present in the allowed list. This implies that if a carrier is not present in either list,
+ *    it is allowed.
+ * MultiSim policy: it indicates the behavior in case of devices with two or more SIM cards.
+ *  - MULTISIM_POLICY_NONE: the same configuration is applied to all SIM slots independently. This
+ *    is the default value if none is set.
+ *  - MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT: it indicates that any SIM card can be used
+ *    as far as one SIM card matching the configuration is present in the device.
+ *
+ * Both lists support the character '?' as wild character. For example, an entry indicating
+ * MCC=310 and MNC=??? will match all networks with MCC=310.
+ *
+ * Example 1: Allowed list contains MCC and MNC of operator A. Excluded list contains operator B,
+ *            which has same MCC and MNC, but also GID1 value. The priority allowed list is set
+ *            to true. Only SIM cards of operator A are allowed, but not those of B or any other
+ *            operator.
+ * Example 2: Allowed list contains MCC and MNC of operator A. Excluded list contains an entry
+ *            with same MCC, and '???' as MNC. The priority allowed list is set to false.
+ *            SIM cards of operator A and all SIM cards with a different MCC value are allowed.
+ *            SIM cards of operators with same MCC value and different MNC are not allowed.
+ * @hide
+ */
+@SystemApi
+public final class CarrierRestrictionRules implements Parcelable {
+    /**
+     * The device only allows usage of carriers that are present in the allowed list and not
+     * present in the excluded list. This implies that if a carrier is not present in either list,
+     * it is not allowed.
+     */
+    public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0;
+
+    /**
+     * The device allows all carriers, except those present in the excluded list and not present
+     * in the allowed list. This implies that if a carrier is not present in either list, it is
+     * allowed.
+     */
+    public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1;
+
+    /** The same configuration is applied to all SIM slots independently. */
+    public static final int MULTISIM_POLICY_NONE = 0;
+
+    /** Any SIM card can be used as far as one SIM card matching the configuration is present. */
+    public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "MULTISIM_POLICY_",
+            value = {MULTISIM_POLICY_NONE, MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT})
+    public @interface MultiSimPolicy {}
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CARRIER_RESTRICTION_DEFAULT_",
+            value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED})
+    public @interface CarrierRestrictionDefault {}
+
+    private List<CarrierIdentifier> mAllowedCarriers;
+    private List<CarrierIdentifier> mExcludedCarriers;
+    @CarrierRestrictionDefault
+    private int mCarrierRestrictionDefault;
+    @MultiSimPolicy
+    private int mMultiSimPolicy;
+
+    private CarrierRestrictionRules() {
+        mAllowedCarriers = new ArrayList<CarrierIdentifier>();
+        mExcludedCarriers = new ArrayList<CarrierIdentifier>();
+        mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED;
+        mMultiSimPolicy = MULTISIM_POLICY_NONE;
+    }
+
+    private CarrierRestrictionRules(Parcel in) {
+        mAllowedCarriers = new ArrayList<CarrierIdentifier>();
+        mExcludedCarriers = new ArrayList<CarrierIdentifier>();
+
+        in.readTypedList(mAllowedCarriers, CarrierIdentifier.CREATOR);
+        in.readTypedList(mExcludedCarriers, CarrierIdentifier.CREATOR);
+        mCarrierRestrictionDefault = in.readInt();
+        mMultiSimPolicy = in.readInt();
+    }
+
+    /**
+     * Creates a new builder for this class
+     * @hide
+     */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Indicates if all carriers are allowed
+     */
+    public boolean isAllCarriersAllowed() {
+        return (mAllowedCarriers.isEmpty() && mExcludedCarriers.isEmpty()
+                && mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_ALLOWED);
+    }
+
+    /**
+     * Retrieves list of allowed carriers
+     *
+     * @return the list of allowed carriers
+     */
+    public @NonNull List<CarrierIdentifier> getAllowedCarriers() {
+        return mAllowedCarriers;
+    }
+
+    /**
+     * Retrieves list of excluded carriers
+     *
+     * @return the list of excluded carriers
+     */
+    public @NonNull List<CarrierIdentifier> getExcludedCarriers() {
+        return mExcludedCarriers;
+    }
+
+    /**
+     * Retrieves the default behavior of carrier restrictions
+     */
+    public @CarrierRestrictionDefault int getDefaultCarrierRestriction() {
+        return mCarrierRestrictionDefault;
+    }
+
+    /**
+     * @return The policy used for multi-SIM devices
+     */
+    public @MultiSimPolicy int getMultiSimPolicy() {
+        return mMultiSimPolicy;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeTypedList(mAllowedCarriers);
+        out.writeTypedList(mExcludedCarriers);
+        out.writeInt(mCarrierRestrictionDefault);
+        out.writeInt(mMultiSimPolicy);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     */
+    public static final Creator<CarrierRestrictionRules> CREATOR =
+            new Creator<CarrierRestrictionRules>() {
+        @Override
+        public CarrierRestrictionRules createFromParcel(Parcel in) {
+            return new CarrierRestrictionRules(in);
+        }
+
+        @Override
+        public CarrierRestrictionRules[] newArray(int size) {
+            return new CarrierRestrictionRules[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        return "CarrierRestrictionRules(allowed:" + mAllowedCarriers + ", excluded:"
+                + mExcludedCarriers + ", default:" + mCarrierRestrictionDefault
+                + ", multisim policy:" + mMultiSimPolicy + ")";
+    }
+
+    /**
+     * Builder for a {@link CarrierRestrictionRules}.
+     */
+    public static class Builder {
+        private final CarrierRestrictionRules mRules;
+
+        /** {@hide} */
+        public Builder() {
+            mRules = new CarrierRestrictionRules();
+        }
+
+        /** build command */
+        public CarrierRestrictionRules build() {
+            return mRules;
+        }
+
+        /**
+         * Indicate that all carriers are allowed.
+         */
+        public Builder setAllCarriersAllowed() {
+            mRules.mAllowedCarriers.clear();
+            mRules.mExcludedCarriers.clear();
+            mRules.mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_ALLOWED;
+            return this;
+        }
+
+        /**
+         * Set list of allowed carriers.
+         *
+         * @param allowedCarriers list of allowed carriers
+         */
+        public Builder setAllowedCarriers(List<CarrierIdentifier> allowedCarriers) {
+            mRules.mAllowedCarriers = new ArrayList<CarrierIdentifier>(allowedCarriers);
+            return this;
+        }
+
+        /**
+         * Set list of excluded carriers.
+         *
+         * @param excludedCarriers list of excluded carriers
+         */
+        public Builder setExcludedCarriers(List<CarrierIdentifier> excludedCarriers) {
+            mRules.mExcludedCarriers = new ArrayList<CarrierIdentifier>(excludedCarriers);
+            return this;
+        }
+
+        /**
+         * Set the default behavior of the carrier restrictions
+         *
+         * @param carrierRestrictionDefault prioritized carrier list
+         */
+        public Builder setDefaultCarrierRestriction(
+                @CarrierRestrictionDefault int carrierRestrictionDefault) {
+            mRules.mCarrierRestrictionDefault = carrierRestrictionDefault;
+            return this;
+        }
+
+        /**
+         * Set the policy to be used for multi-SIM devices
+         *
+         * @param multiSimPolicy multi SIM policy
+         */
+        public Builder setMultiSimPolicy(@MultiSimPolicy int multiSimPolicy) {
+            mRules.mMultiSimPolicy = multiSimPolicy;
+            return this;
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/CellConfigLte.java b/telephony/java/android/telephony/CellConfigLte.java
index 35769f0..eafbfbc 100644
--- a/telephony/java/android/telephony/CellConfigLte.java
+++ b/telephony/java/android/telephony/CellConfigLte.java
@@ -34,6 +34,11 @@
     }
 
     /** @hide */
+    public CellConfigLte(android.hardware.radio.V1_4.CellConfigLte cellConfig) {
+        mIsEndcAvailable = cellConfig.isEndcAvailable;
+    }
+
+    /** @hide */
     public CellConfigLte(boolean isEndcAvailable) {
         mIsEndcAvailable = isEndcAvailable;
     }
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index b761bd7..8ce5c54 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -19,8 +19,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
+import android.hardware.radio.V1_4.CellInfo.Info;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -318,6 +320,13 @@
     }
 
     /** @hide */
+    protected CellInfo(android.hardware.radio.V1_4.CellInfo ci) {
+        this.mRegistered = ci.isRegistered;
+        this.mTimeStamp = SystemClock.elapsedRealtimeNanos();
+        this.mCellConnectionStatus = ci.connectionStatus;
+    }
+
+    /** @hide */
     public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) {
         if (ci == null) return null;
         switch(ci.cellInfoType) {
@@ -342,4 +351,17 @@
             default: return null;
         }
     }
+
+    /** @hide */
+    public static CellInfo create(android.hardware.radio.V1_4.CellInfo ci) {
+        if (ci == null) return null;
+        switch (ci.info.getDiscriminator()) {
+            case Info.hidl_discriminator.gsm: return new CellInfoGsm(ci);
+            case Info.hidl_discriminator.cdma: return new CellInfoCdma(ci);
+            case Info.hidl_discriminator.lte: return new CellInfoLte(ci);
+            case Info.hidl_discriminator.wcdma: return new CellInfoWcdma(ci);
+            case Info.hidl_discriminator.tdscdma: return new CellInfoTdscdma(ci);
+            default: return null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index c9f07da..4440108 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -67,6 +67,15 @@
             new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
     }
 
+    /** @hide */
+    public CellInfoCdma(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoCdma cic = ci.info.cdma();
+        mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+        mCellSignalStrengthCdma =
+                new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+    }
+
     @Override
     public CellIdentityCdma getCellIdentity() {
         return mCellIdentityCdma;
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index ad16dfa..248adfc 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -63,6 +63,14 @@
         mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
     }
 
+    /** @hide */
+    public CellInfoGsm(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoGsm cig = ci.info.gsm();
+        mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
+    }
+
     @Override
     public CellIdentityGsm getCellIdentity() {
         return mCellIdentityGsm;
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 7593831..8e8ce8a 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -70,6 +70,15 @@
         mCellConfig = new CellConfigLte();
     }
 
+    /** @hide */
+    public CellInfoLte(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_4.CellInfoLte cil = ci.info.lte();
+        mCellIdentityLte = new CellIdentityLte(cil.base.cellIdentityLte);
+        mCellSignalStrengthLte = new CellSignalStrengthLte(cil.base.signalStrengthLte);
+        mCellConfig = new CellConfigLte(cil.cellConfig);
+    }
+
     @Override
     public CellIdentityLte getCellIdentity() {
         if (DBG) log("getCellIdentity: " + mCellIdentityLte);
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index a8c49b7..2ab38fb 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -64,6 +64,14 @@
         mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
     }
 
+    /** @hide */
+    public CellInfoTdscdma(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoTdscdma cit = ci.info.tdscdma();
+        mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+    }
+
     @Override public CellIdentityTdscdma getCellIdentity() {
         return mCellIdentityTdscdma;
     }
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index a427e80..65e0470 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -63,6 +63,14 @@
         mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
     }
 
+    /** @hide */
+    public CellInfoWcdma(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoWcdma ciw = ci.info.wcdma();
+        mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+    }
+
     @Override
     public CellIdentityWcdma getCellIdentity() {
         return mCellIdentityWcdma;
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index f2c14a1..e6182ed 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -42,6 +42,9 @@
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
 
     /** @hide */
+    protected static final int NUM_SIGNAL_STRENGTH_THRESHOLDS = NUM_SIGNAL_STRENGTH_BINS - 1;
+
+    /** @hide */
     public static final String[] SIGNAL_STRENGTH_NAMES = {
         "none", "poor", "moderate", "good", "great"
     };
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 893dbe3..61c6b48 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -30,7 +30,7 @@
 public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
 
     private static final String LOG_TAG = "CellSignalStrengthLte";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
 
     /**
      * Indicates the unknown or undetectable RSSI value in ASU.
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 88f6fbc..0760407 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.telephony.Rlog;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -41,8 +42,18 @@
     private static final int WCDMA_RSSI_POOR = -107;
     private static final int WCDMA_RSSI_MIN = -113;
 
-    private static final int WCDMA_RSCP_MIN = -120;
+    private static final int[] sRssiThresholds = new int[]{
+            WCDMA_RSSI_POOR, WCDMA_RSSI_MODERATE, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT};
+
     private static final int WCDMA_RSCP_MAX = -24;
+    private static final int WCDMA_RSCP_GREAT = -85;
+    private static final int WCDMA_RSCP_GOOD = -95;
+    private static final int WCDMA_RSCP_MODERATE = -105;
+    private static final int WCDMA_RSCP_POOR = -115;
+    private static final int WCDMA_RSCP_MIN = -120;
+
+    private static final int[] sRscpThresholds = new int[] {
+            WCDMA_RSCP_POOR, WCDMA_RSCP_MODERATE, WCDMA_RSCP_GOOD, WCDMA_RSCP_GREAT};
 
     // TODO: Because these are used as values in CarrierConfig, they should be exposed somehow.
     /** @hide */
@@ -54,6 +65,9 @@
     /** @hide */
     public static final String LEVEL_CALCULATION_METHOD_RSCP = "rscp";
 
+    // Default to RSSI for backwards compatibility with older devices
+    private static final String sLevelCalculationMethod = LEVEL_CALCULATION_METHOD_RSSI;
+
     private int mRssi; // in dBm [-113, 51] or CellInfo.UNAVAILABLE if unknown
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
                                // CellInfo.UNAVAILABLE if unknown
@@ -121,10 +135,6 @@
         mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
-    private static final String sLevelCalculationMethod = LEVEL_CALCULATION_METHOD_RSSI;
-    private static final int[] sThresholds = new int[]{
-            WCDMA_RSSI_POOR, WCDMA_RSSI_GOOD, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT};
-
     /**
      * Retrieve an abstract level value for the overall signal strength.
      *
@@ -140,41 +150,46 @@
     @Override
     public void updateLevel(PersistableBundle cc, ServiceState ss) {
         String calcMethod;
-        int[] thresholds;
+        int[] rscpThresholds;
 
         if (cc == null) {
             calcMethod = sLevelCalculationMethod;
-            thresholds = sThresholds;
+            rscpThresholds = sRscpThresholds;
         } else {
             // TODO: abstract this entire thing into a series of functions
             calcMethod = cc.getString(
                     CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING,
                     sLevelCalculationMethod);
-            thresholds = cc.getIntArray(
+            if (TextUtils.isEmpty(calcMethod)) calcMethod = sLevelCalculationMethod;
+            rscpThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
-            if (thresholds == null) thresholds = sThresholds;
+            if (rscpThresholds == null || rscpThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) {
+                rscpThresholds = sRscpThresholds;
+            }
         }
 
-        int level = thresholds.length;
+        int level = NUM_SIGNAL_STRENGTH_THRESHOLDS;
         switch (calcMethod) {
             case LEVEL_CALCULATION_METHOD_RSCP:
                 if (mRscp < WCDMA_RSCP_MIN || mRscp > WCDMA_RSCP_MAX) {
                     mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                     return;
                 }
-                while (level > 0 && mRscp < thresholds[level - 1]) level--;
+                while (level > 0 && mRscp < rscpThresholds[level - 1]) level--;
                 mLevel = level;
                 return;
+            default:
+                loge("Invalid Level Calculation Method for CellSignalStrengthWcdma = "
+                        + calcMethod);
+                /** fall through */
             case LEVEL_CALCULATION_METHOD_RSSI:
                 if (mRssi < WCDMA_RSSI_MIN || mRssi > WCDMA_RSSI_MAX) {
                     mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                     return;
                 }
-                while (level > 0 && mRssi < thresholds[level - 1]) level--;
+                while (level > 0 && mRssi < sRssiThresholds[level - 1]) level--;
                 mLevel = level;
                 return;
-            default:
-                mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         }
     }
 
@@ -204,7 +219,7 @@
     }
 
     /**
-     * Get the signal strength as dBm
+     * Get the RSSI as dBm
      *
      * @hide
      */
@@ -214,12 +229,32 @@
 
     /**
      * Get the RSCP as dBm
+     *
      * @hide
      */
     public int getRscp() {
         return mRscp;
     }
 
+    /**
+     * Get the Ec/No as dB
+     *
+     * @hide
+     */
+    public int getEcNo() {
+        return mEcNo;
+    }
+
+    /**
+     * Return the Bit Error Rate
+     *
+     * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE.
+     * @hide
+     */
+    public int getBitErrorRate() {
+        return mBitErrorRate;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mRssi, mBitErrorRate, mRscp, mEcNo, mLevel);
@@ -304,9 +339,16 @@
     };
 
     /**
-     * log
+     * log warning
      */
     private static void log(String s) {
         Rlog.w(LOG_TAG, s);
     }
+
+    /**
+     * log error
+     */
+    private static void loge(String s) {
+        Rlog.e(LOG_TAG, s);
+    }
 }
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c53b37d..26ec6de 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.PersistableBundle;
 
@@ -34,6 +35,7 @@
  * Returned as the reason for a data connection failure as defined by modem and some local errors.
  * @hide
  */
+@SystemApi
 public final class DataFailCause {
     /** There is no failure */
     public static final int NONE = 0;
@@ -101,8 +103,8 @@
     public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
     /** Multiple connections to a same PDN is not allowed. */
     public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
-    /** Packet Data Protocol (PDP) */
-    public static final int MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41;
+    /** Max number of Packet Data Protocol (PDP) context reached. */
+    public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 0x41;
     /** Unsupported APN in current public land mobile network (PLMN). */
     public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42;
     /** Invalid transaction id. */
@@ -165,22 +167,36 @@
 
     // Local errors generated by Vendor RIL
     // specified in ril.h
+    /** Data fail due to registration failure. */
     public static final int REGISTRATION_FAIL = -1;
+    /** Data fail due to GPRS registration failure. */
     public static final int GPRS_REGISTRATION_FAIL = -2;
+    /** Data call drop due to network/modem disconnect. */
     public static final int SIGNAL_LOST = -3;                        /* no retry */
+    /**
+     * Preferred technology has changed, must retry with parameters appropriate for new technology.
+     */
     public static final int PREF_RADIO_TECH_CHANGED = -4;
+    /** data call was disconnected because radio was resetting, powered off. */
     public static final int RADIO_POWER_OFF = -5;                    /* no retry */
+    /** Data call was disconnected by modem because tethered. */
     public static final int TETHERED_CALL_ACTIVE = -6;               /* no retry */
+    /** Data call fail due to unspecific errors. */
     public static final int ERROR_UNSPECIFIED = 0xFFFF;
 
     // Errors generated by the Framework
     // specified here
+    /** Unknown data failure cause. */
     public static final int UNKNOWN = 0x10000;
+    /** Data fail due to radio not unavailable. */
     public static final int RADIO_NOT_AVAILABLE = 0x10001;                   /* no retry */
+    /** @hide */
     public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002;        /* no retry */
+    /** @hide */
     public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
+    /** Data connection was lost. */
     public static final int LOST_CONNECTION = 0x10004;
-    /** Data was reset by framework. */
+    /** @hide */
     public static final int RESET_BY_FRAMEWORK = 0x10005;
 
     /** @hide */
@@ -216,7 +232,7 @@
             ESM_INFO_NOT_RECEIVED,
             PDN_CONN_DOES_NOT_EXIST,
             MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
-            MAX_ACTIVE_PDP_CONTEXT_REACHED,
+            ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
             UNSUPPORTED_APN_IN_CURRENT_PLMN,
             INVALID_TRANSACTION_ID,
             MESSAGE_INCORRECT_SEMANTIC,
@@ -308,8 +324,8 @@
         sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
         sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
                 "MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
-        sFailCauseMap.put(MAX_ACTIVE_PDP_CONTEXT_REACHED,
-                "MAX_ACTIVE_PDP_CONTEXT_REACHED");
+        sFailCauseMap.put(ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
+                "ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED");
         sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
                 "UNSUPPORTED_APN_IN_CURRENT_PLMN");
         sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID");
@@ -369,6 +385,9 @@
         sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
     }
 
+    private DataFailCause() {
+    }
+
     /**
      * Map of subId -> set of data call setup permanent failure for the carrier.
      */
@@ -382,6 +401,8 @@
      * @param cause data disconnect cause
      * @param subId subscription index
      * @return true if the fail cause code needs platform to trigger a modem restart.
+     *
+     * @hide
      */
     public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause,
                                                 int subId) {
@@ -410,6 +431,7 @@
         return false;
     }
 
+    /** @hide */
     public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause,
                                              int subId) {
         synchronized (sPermanentFailureCache) {
@@ -469,6 +491,7 @@
         }
     }
 
+    /** @hide */
     public static boolean isEventLoggable(@FailCause int dataFailCause) {
         return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
                 || (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
@@ -488,11 +511,13 @@
                 || (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER);
     }
 
+    /** @hide */
     public static String toString(@FailCause int dataFailCause) {
         int cause = getFailCause(dataFailCause);
         return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause);
     }
 
+    /** @hide */
     public static int getFailCause(@FailCause int failCause) {
         if (sFailCauseMap.containsKey(failCause)) {
             return failCause;
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
index 5d809d0..d6a8065 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationStates.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -44,13 +44,19 @@
      */
     public final boolean isEnDcAvailable;
 
+    /**
+     * Provides network support info for LTE VoPS and LTE Emergency bearer support
+     */
+    public final LteVopsSupportInfo lteVopsSupportInfo;
+
     DataSpecificRegistrationStates(
             int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
-            boolean isEnDcAvailable) {
+            boolean isEnDcAvailable, LteVopsSupportInfo lteVops) {
         this.maxDataCalls = maxDataCalls;
         this.isDcNrRestricted = isDcNrRestricted;
         this.isNrAvailable = isNrAvailable;
         this.isEnDcAvailable = isEnDcAvailable;
+        this.lteVopsSupportInfo = lteVops;
     }
 
     private DataSpecificRegistrationStates(Parcel source) {
@@ -58,6 +64,7 @@
         isDcNrRestricted = source.readBoolean();
         isNrAvailable = source.readBoolean();
         isEnDcAvailable = source.readBoolean();
+        lteVopsSupportInfo = LteVopsSupportInfo.CREATOR.createFromParcel(source);
     }
 
     @Override
@@ -66,6 +73,7 @@
         dest.writeBoolean(isDcNrRestricted);
         dest.writeBoolean(isNrAvailable);
         dest.writeBoolean(isEnDcAvailable);
+        lteVopsSupportInfo.writeToParcel(dest, flags);
     }
 
     @Override
@@ -81,13 +89,15 @@
                 .append(" isDcNrRestricted = " + isDcNrRestricted)
                 .append(" isNrAvailable = " + isNrAvailable)
                 .append(" isEnDcAvailable = " + isEnDcAvailable)
+                .append(lteVopsSupportInfo.toString())
                 .append(" }")
                 .toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable);
+        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable,
+            lteVopsSupportInfo);
     }
 
     @Override
@@ -100,7 +110,8 @@
         return this.maxDataCalls == other.maxDataCalls
                 && this.isDcNrRestricted == other.isDcNrRestricted
                 && this.isNrAvailable == other.isNrAvailable
-                && this.isEnDcAvailable == other.isEnDcAvailable;
+                && this.isEnDcAvailable == other.isEnDcAvailable
+                && this.lteVopsSupportInfo.equals(other.lteVopsSupportInfo);
     }
 
     public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
@@ -115,4 +126,4 @@
                     return new DataSpecificRegistrationStates[size];
                 }
             };
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/ICellInfoCallback.aidl b/telephony/java/android/telephony/ICellInfoCallback.aidl
index 7fb62682..ee3c1b1 100644
--- a/telephony/java/android/telephony/ICellInfoCallback.aidl
+++ b/telephony/java/android/telephony/ICellInfoCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.os.ParcelableException;
 import android.telephony.CellInfo;
 
 import java.util.List;
@@ -27,4 +28,5 @@
 oneway interface ICellInfoCallback
 {
     void onCellInfo(in List<CellInfo> state);
+    void onError(in int errorCode, in ParcelableException detail);
 }
diff --git a/telephony/java/android/telephony/LteVopsSupportInfo.aidl b/telephony/java/android/telephony/LteVopsSupportInfo.aidl
new file mode 100644
index 0000000..5984598
--- /dev/null
+++ b/telephony/java/android/telephony/LteVopsSupportInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony;
+
+parcelable LteVopsSupportInfo;
diff --git a/telephony/java/android/telephony/LteVopsSupportInfo.java b/telephony/java/android/telephony/LteVopsSupportInfo.java
new file mode 100644
index 0000000..0ae85c0
--- /dev/null
+++ b/telephony/java/android/telephony/LteVopsSupportInfo.java
@@ -0,0 +1,137 @@
+/*
+ * 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.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class stores information related to LTE network VoPS support
+ * @hide
+ */
+@SystemApi
+public final class LteVopsSupportInfo implements Parcelable {
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {LTE_STATUS_NOT_AVAILABLE, LTE_STATUS_SUPPORTED,
+                    LTE_STATUS_NOT_SUPPORTED}, prefix = "LTE_STATUS_")
+    public @interface LteVopsStatus {}
+    /**
+     * Indicates information not available from modem.
+     */
+    public static final int LTE_STATUS_NOT_AVAILABLE = 1;
+
+    /**
+     * Indicates network support the feature.
+     */
+    public static final int LTE_STATUS_SUPPORTED = 2;
+
+    /**
+     * Indicates network does not support the feature.
+     */
+    public static final int LTE_STATUS_NOT_SUPPORTED = 3;
+
+    @LteVopsStatus
+    private final int mVopsSupport;
+    @LteVopsStatus
+    private final int mEmcBearerSupport;
+
+    public LteVopsSupportInfo(@LteVopsStatus int vops, @LteVopsStatus int emergency) {
+        mVopsSupport = vops;
+        mEmcBearerSupport = emergency;
+    }
+
+    /**
+     * Provides the LTE VoPS support capability as described in:
+     * 3GPP 24.301 EPS network feature support -> IMS VoPS
+     */
+    public @LteVopsStatus int getVopsSupport() {
+        return mVopsSupport;
+    }
+
+    /**
+     * Provides the LTE Emergency bearer support capability as described in:
+     *    3GPP 24.301 EPS network feature support -> EMC BS
+     *    25.331 LTE RRC SIB1 : ims-EmergencySupport-r9
+     */
+    public @LteVopsStatus int getEmcBearerSupport() {
+        return mEmcBearerSupport;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mVopsSupport);
+        out.writeInt(mEmcBearerSupport);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof LteVopsSupportInfo)) {
+            return false;
+        }
+        if (this == o) return true;
+        LteVopsSupportInfo other = (LteVopsSupportInfo) o;
+        return mVopsSupport == other.mVopsSupport
+            && mEmcBearerSupport == other.mEmcBearerSupport;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVopsSupport, mEmcBearerSupport);
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return ("LteVopsSupportInfo : "
+                + " mVopsSupport = " + mVopsSupport
+                + " mEmcBearerSupport = " + mEmcBearerSupport);
+    }
+
+    public static final Creator<LteVopsSupportInfo> CREATOR =
+            new Creator<LteVopsSupportInfo>() {
+        @Override
+        public LteVopsSupportInfo createFromParcel(Parcel in) {
+            return new LteVopsSupportInfo(in);
+        }
+
+        @Override
+        public LteVopsSupportInfo[] newArray(int size) {
+            return new LteVopsSupportInfo[size];
+        }
+    };
+
+    private LteVopsSupportInfo(Parcel in) {
+        mVopsSupport = in.readInt();
+        mEmcBearerSupport = in.readInt();
+    }
+}
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index b00665e..ceb76b5 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -219,12 +219,13 @@
     public NetworkRegistrationState(int domain, int transportType, int regState,
             int accessNetworkTechnology, int rejectCause, boolean emergencyOnly,
             int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls,
-            boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable) {
+            boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable,
+            LteVopsSupportInfo lteVopsSupportInfo) {
         this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
                 availableServices, cellIdentity);
 
         mDataSpecificStates = new DataSpecificRegistrationStates(
-                maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable);
+                maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo);
         updateNrStatus(mDataSpecificStates);
     }
 
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 9317aa7..9fee593 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -184,14 +184,17 @@
     public static final int LISTEN_PRECISE_CALL_STATE                       = 0x00000800;
 
     /**
-     * Listen for precise changes and fails on the data connection (cellular).
+     * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
+     *
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
      * READ_PRECISE_PHONE_STATE}
      *
      * @see #onPreciseDataConnectionStateChanged
+     *
      * @hide
      */
+    @SystemApi
     public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE            = 0x00001000;
 
     /**
@@ -293,7 +296,7 @@
 
     /**
      *  Listen for changes to preferred data subId.
-     *  See {@link SubscriptionManager#setPreferredData(int)}
+     *  See {@link SubscriptionManager#setPreferredDataSubId(int)}
      *  for more details.
      *
      *  @see #onPreferredDataSubIdChanged
@@ -332,6 +335,18 @@
     @SystemApi
     public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
 
+    /**
+     * Listen for changes to the call attributes of a currently active call.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     *
+     * @see #onCallAttributesChanged
+     * @hide
+     */
+    @SystemApi
+    public static final int LISTEN_CALL_ATTRIBUTES_CHANGED                 = 0x04000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -564,10 +579,11 @@
 
     /**
      * Callback invoked when data connection state changes with precise information.
+     * @param dataConnectionState {@link PreciseDataConnectionState}
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void onPreciseDataConnectionStateChanged(
             PreciseDataConnectionState dataConnectionState) {
         // default implementation empty
@@ -679,6 +695,17 @@
     }
 
     /**
+     * Callback invoked when the call attributes changes. Requires
+     * the READ_PRIVILEGED_PHONE_STATE permission.
+     * @param callAttributes the call attributes
+     * @hide
+     */
+    @SystemApi
+    public void onCallAttributesChanged(CallAttributes callAttributes) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when modem radio power state changes. Requires
      * the READ_PRIVILEGED_PHONE_STATE permission.
      * @param state the modem radio power state
@@ -778,9 +805,11 @@
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
 
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(
-                            () -> psl.onDataConnectionStateChanged(state, networkType)));
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> {
+                        psl.onDataConnectionStateChanged(state, networkType);
+                        psl.onDataConnectionStateChanged(state);
+                    }));
         }
 
         public void onDataActivity(int direction) {
@@ -937,6 +966,14 @@
                     () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
         }
 
+        public void onCallAttributesChanged(CallAttributes callAttributes) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
+        }
+
         public void onPreferredDataSubIdChanged(int subId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index b258f52..57a1826 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -16,11 +16,14 @@
 
 package android.telephony;
 
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.net.LinkProperties;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.TelephonyManager;
-import android.net.LinkProperties;
+import android.telephony.data.ApnSetting;
+
+import java.util.Objects;
 
 /**
  * Contains precise data connection state.
@@ -30,24 +33,23 @@
  * <ul>
  *   <li>Data connection state.
  *   <li>Network type of the connection.
- *   <li>APN type.
+ *   <li>APN types.
  *   <li>APN.
- *   <li>Data connection change reason.
  *   <li>The properties of the network link.
  *   <li>Data connection fail cause.
  * </ul>
  *
  * @hide
  */
-public class PreciseDataConnectionState implements Parcelable {
+@SystemApi
+public final class PreciseDataConnectionState implements Parcelable {
 
-    private int mState = TelephonyManager.DATA_UNKNOWN;
-    private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-    private String mAPNType = "";
+    private @TelephonyManager.DataState int mState = TelephonyManager.DATA_UNKNOWN;
+    private @TelephonyManager.NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    private @DataFailCause.FailCause int mFailCause = DataFailCause.NONE;
+    private @ApnSetting.ApnType int mAPNTypes = ApnSetting.TYPE_NONE;
     private String mAPN = "";
-    private String mReason = "";
     private LinkProperties mLinkProperties = null;
-    private String mFailCause = "";
 
     /**
      * Constructor
@@ -55,14 +57,15 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public PreciseDataConnectionState(int state, int networkType,
-            String apnType, String apn, String reason,
-            LinkProperties linkProperties, String failCause) {
+    public PreciseDataConnectionState(@TelephonyManager.DataState int state,
+                                      @TelephonyManager.NetworkType int networkType,
+                                      @ApnSetting.ApnType int apnTypes, String apn,
+                                      LinkProperties linkProperties,
+                                      @DataFailCause.FailCause int failCause) {
         mState = state;
         mNetworkType = networkType;
-        mAPNType = apnType;
+        mAPNTypes = apnTypes;
         mAPN = apn;
-        mReason = reason;
         mLinkProperties = linkProperties;
         mFailCause = failCause;
     }
@@ -77,82 +80,52 @@
 
     /**
      * Construct a PreciseDataConnectionState object from the given parcel.
+     *
+     * @hide
      */
     private PreciseDataConnectionState(Parcel in) {
         mState = in.readInt();
         mNetworkType = in.readInt();
-        mAPNType = in.readString();
+        mAPNTypes = in.readInt();
         mAPN = in.readString();
-        mReason = in.readString();
         mLinkProperties = (LinkProperties)in.readParcelable(null);
-        mFailCause = in.readString();
+        mFailCause = in.readInt();
     }
 
     /**
-     * Get data connection state
-     *
-     * @see TelephonyManager#DATA_UNKNOWN
-     * @see TelephonyManager#DATA_DISCONNECTED
-     * @see TelephonyManager#DATA_CONNECTING
-     * @see TelephonyManager#DATA_CONNECTED
-     * @see TelephonyManager#DATA_SUSPENDED
+     * Returns the state of data connection that supported the apn types returned by
+     * {@link #getDataConnectionApnTypeBitMask()}
      */
-    @UnsupportedAppUsage
-    public int getDataConnectionState() {
+    public @TelephonyManager.DataState int getDataConnectionState() {
         return mState;
     }
 
     /**
-     * Get data connection network type
-     *
-     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
-     * @see TelephonyManager#NETWORK_TYPE_GPRS
-     * @see TelephonyManager#NETWORK_TYPE_EDGE
-     * @see TelephonyManager#NETWORK_TYPE_UMTS
-     * @see TelephonyManager#NETWORK_TYPE_CDMA
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
-     * @see TelephonyManager#NETWORK_TYPE_1xRTT
-     * @see TelephonyManager#NETWORK_TYPE_HSDPA
-     * @see TelephonyManager#NETWORK_TYPE_HSUPA
-     * @see TelephonyManager#NETWORK_TYPE_HSPA
-     * @see TelephonyManager#NETWORK_TYPE_IDEN
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
-     * @see TelephonyManager#NETWORK_TYPE_LTE
-     * @see TelephonyManager#NETWORK_TYPE_EHRPD
-     * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * Returns the network type associated with this data connection.
+     * @hide
      */
-    @UnsupportedAppUsage
-    public int getDataConnectionNetworkType() {
+    public @TelephonyManager.NetworkType int getDataConnectionNetworkType() {
         return mNetworkType;
     }
 
     /**
-     * Get data connection APN type
+     * Returns the data connection APN types supported by this connection and triggers
+     * {@link PreciseDataConnectionState} change.
      */
-    @UnsupportedAppUsage
-    public String getDataConnectionAPNType() {
-        return mAPNType;
+    public @ApnSetting.ApnType int getDataConnectionApnTypeBitMask() {
+        return mAPNTypes;
     }
 
     /**
-     * Get data connection APN.
+     * Returns APN {@link ApnSetting} of this data connection.
      */
-    @UnsupportedAppUsage
-    public String getDataConnectionAPN() {
+    public String getDataConnectionApn() {
         return mAPN;
     }
 
     /**
-     * Get data connection change reason.
-     */
-    @UnsupportedAppUsage
-    public String getDataConnectionChangeReason() {
-        return mReason;
-    }
-
-    /**
-     * Get the properties of the network link.
+     * Get the properties of the network link {@link LinkProperties}.
+     * @hide
      */
     @UnsupportedAppUsage
     public LinkProperties getDataConnectionLinkProperties() {
@@ -160,10 +133,9 @@
     }
 
     /**
-     * Get data connection fail cause, in case there was a failure.
+     * Returns data connection fail cause, in case there was a failure.
      */
-    @UnsupportedAppUsage
-    public String getDataConnectionFailCause() {
+    public @DataFailCause.FailCause int getDataConnectionFailCause() {
         return mFailCause;
     }
 
@@ -176,11 +148,10 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mState);
         out.writeInt(mNetworkType);
-        out.writeString(mAPNType);
+        out.writeInt(mAPNTypes);
         out.writeString(mAPN);
-        out.writeString(mReason);
         out.writeParcelable(mLinkProperties, flags);
-        out.writeString(mFailCause);
+        out.writeInt(mFailCause);
     }
 
     public static final Parcelable.Creator<PreciseDataConnectionState> CREATOR
@@ -197,72 +168,23 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mState;
-        result = prime * result + mNetworkType;
-        result = prime * result + ((mAPNType == null) ? 0 : mAPNType.hashCode());
-        result = prime * result + ((mAPN == null) ? 0 : mAPN.hashCode());
-        result = prime * result + ((mReason == null) ? 0 : mReason.hashCode());
-        result = prime * result + ((mLinkProperties == null) ? 0 : mLinkProperties.hashCode());
-        result = prime * result + ((mFailCause == null) ? 0 : mFailCause.hashCode());
-        return result;
+        return Objects.hash(mState, mNetworkType, mAPNTypes, mAPN, mLinkProperties,
+                mFailCause);
     }
 
     @Override
     public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
+
+        if (!(obj instanceof PreciseDataConnectionState)) {
             return false;
         }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
+
         PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
-        if (mAPN == null) {
-            if (other.mAPN != null) {
-                return false;
-            }
-        } else if (!mAPN.equals(other.mAPN)) {
-            return false;
-        }
-        if (mAPNType == null) {
-            if (other.mAPNType != null) {
-                return false;
-            }
-        } else if (!mAPNType.equals(other.mAPNType)) {
-            return false;
-        }
-        if (mFailCause == null) {
-            if (other.mFailCause != null) {
-                return false;
-            }
-        } else if (!mFailCause.equals(other.mFailCause)) {
-            return false;
-        }
-        if (mLinkProperties == null) {
-            if (other.mLinkProperties != null) {
-                return false;
-            }
-        } else if (!mLinkProperties.equals(other.mLinkProperties)) {
-            return false;
-        }
-        if (mNetworkType != other.mNetworkType) {
-            return false;
-        }
-        if (mReason == null) {
-            if (other.mReason != null) {
-                return false;
-            }
-        } else if (!mReason.equals(other.mReason)) {
-            return false;
-        }
-        if (mState != other.mState) {
-            return false;
-        }
-        return true;
+        return Objects.equals(mAPN, other.mAPN) && mAPNTypes == other.mAPNTypes
+                && mFailCause == other.mFailCause
+                && Objects.equals(mLinkProperties, other.mLinkProperties)
+                && mNetworkType == other.mNetworkType
+                && mState == other.mState;
     }
 
     @Override
@@ -271,11 +193,10 @@
 
         sb.append("Data Connection state: " + mState);
         sb.append(", Network type: " + mNetworkType);
-        sb.append(", APN type: " + mAPNType);
+        sb.append(", APN types: " + ApnSetting.getApnTypesStringFromBitmask(mAPNTypes));
         sb.append(", APN: " + mAPN);
-        sb.append(", Change reason: " + mReason);
         sb.append(", Link properties: " + mLinkProperties);
-        sb.append(", Fail cause: " + mFailCause);
+        sb.append(", Fail cause: " + DataFailCause.toString(mFailCause));
 
         return sb.toString();
     }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 9fc1b6f..bf9bf9a 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -170,7 +170,8 @@
                     RIL_RADIO_TECHNOLOGY_GSM,
                     RIL_RADIO_TECHNOLOGY_TD_SCDMA,
                     RIL_RADIO_TECHNOLOGY_IWLAN,
-                    RIL_RADIO_TECHNOLOGY_LTE_CA})
+                    RIL_RADIO_TECHNOLOGY_LTE_CA,
+                    RIL_RADIO_TECHNOLOGY_NR})
     public @interface RilRadioTechnology {}
     /**
      * Available radio technologies for GSM, UMTS and CDMA.
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index ef185c5..ad3ca6d 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -24,6 +24,8 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -84,11 +86,6 @@
     CellSignalStrengthTdscdma mTdscdma;
     CellSignalStrengthLte mLte;
 
-    /** Parameters from the framework */
-    @UnsupportedAppUsage
-    private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
-                                // signal strength level
-
     /**
      * Create a new SignalStrength from a intent notifier Bundle
      *
@@ -138,7 +135,6 @@
         mWcdma = wcdma;
         mTdscdma = tdscdma;
         mLte = lte;
-        mLteRsrpBoost = 0;
     }
 
     /**
@@ -178,9 +174,37 @@
         return mLte;
     }
 
+    /**
+     * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+     *
+     * Use this API to access underlying
+     * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+     * granular information about the SignalStrength report. Only valid (non-empty)
+     * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+     * and the list may contain more than one instance of a CellSignalStrength type.
+     *
+     * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+     *
+     * @see android.telephony#CellSignalStrength
+     * @see android.telephony#CellSignalStrengthNr
+     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony#CellSignalStrengthTdscdma
+     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony#CellSignalStrengthGsm
+     */
+    public @NonNull List<CellSignalStrength> getCellSignalStrengths() {
+        List<CellSignalStrength> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
+        if (mLte.isValid()) cssList.add(mLte);
+        if (mCdma.isValid()) cssList.add(mCdma);
+        if (mTdscdma.isValid()) cssList.add(mTdscdma);
+        if (mWcdma.isValid()) cssList.add(mWcdma);
+        if (mGsm.isValid()) cssList.add(mGsm);
+        return cssList;
+    }
+
     /** @hide */
     public void updateLevel(PersistableBundle cc, ServiceState ss) {
-        mLteRsrpBoost = ss.getLteEarfcnRsrpBoost();
         mCdma.updateLevel(cc, ss);
         mGsm.updateLevel(cc, ss);
         mWcdma.updateLevel(cc, ss);
@@ -210,7 +234,6 @@
         mWcdma = new CellSignalStrengthWcdma(s.mWcdma);
         mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma);
         mLte = new CellSignalStrengthLte(s.mLte);
-        mLteRsrpBoost = s.mLteRsrpBoost;
     }
 
     /**
@@ -227,7 +250,6 @@
         mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader());
         mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader());
         mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
-        mLteRsrpBoost = in.readInt();
     }
 
     /**
@@ -239,8 +261,6 @@
         out.writeParcelable(mWcdma, flags);
         out.writeParcelable(mTdscdma, flags);
         out.writeParcelable(mLte, flags);
-
-        out.writeInt(mLteRsrpBoost);
     }
 
     /**
@@ -353,11 +373,6 @@
         return mLte.getCqi();
     }
 
-    /** @hide */
-    public int getLteRsrpBoost() {
-        return mLteRsrpBoost;
-    }
-
     /**
      * Retrieve an abstract level value for the overall signal strength.
      *
@@ -369,7 +384,7 @@
     public int getLevel() {
         int level = getPrimary().getLevel();
         if (level < SIGNAL_STRENGTH_NONE_OR_UNKNOWN || level > SIGNAL_STRENGTH_GREAT) {
-            log("Invalid Level " + level + ", this=" + this);
+            loge("Invalid Level " + level + ", this=" + this);
             return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         }
         return getPrimary().getLevel();
@@ -585,7 +600,7 @@
      */
     @Override
     public int hashCode() {
-        return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte, mLteRsrpBoost);
+        return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte);
     }
 
     /**
@@ -601,8 +616,7 @@
             && mGsm.equals(s.mGsm)
             && mWcdma.equals(s.mWcdma)
             && mTdscdma.equals(s.mTdscdma)
-            && mLte.equals(s.mLte)
-            && mLteRsrpBoost == s.mLteRsrpBoost;
+            && mLte.equals(s.mLte);
     }
 
     /**
@@ -616,7 +630,6 @@
             .append(",mWcdma=").append(mWcdma)
             .append(",mTdscdma=").append(mTdscdma)
             .append(",mLte=").append(mLte)
-            .append(",mLteRsrpBoost=").append(mLteRsrpBoost)
             .append(",primary=").append(getPrimary().getClass().getSimpleName())
             .append("}")
             .toString();
@@ -635,8 +648,6 @@
         mWcdma = m.getParcelable("Wcdma");
         mTdscdma = m.getParcelable("Tdscdma");
         mLte = m.getParcelable("Lte");
-
-        mLteRsrpBoost = m.getInt("LteRsrpBoost");
     }
 
     /**
@@ -652,14 +663,19 @@
         m.putParcelable("Wcdma", mWcdma);
         m.putParcelable("Tdscdma", mTdscdma);
         m.putParcelable("Lte", mLte);
-
-        m.putInt("LteRsrpBoost", mLteRsrpBoost);
     }
 
     /**
-     * log
+     * log warning
      */
     private static void log(String s) {
         Rlog.w(LOG_TAG, s);
     }
+
+    /**
+     * log error
+     */
+    private static void loge(String s) {
+        Rlog.e(LOG_TAG, s);
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a1e8b19..51d5ab1 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -174,6 +174,16 @@
     private boolean mIsGroupDisabled = false;
 
     /**
+     * Profile class, PROFILE_CLASS_TESTING, PROFILE_CLASS_OPERATIONAL
+     * PROFILE_CLASS_PROVISIONING, or PROFILE_CLASS_UNSET.
+     * A profile on the eUICC can be defined as test, operational, provisioning, or unset.
+     * The profile class will be populated from the profile metadata if present. Otherwise,
+     * the profile class defaults to unset if there is no profile metadata or the subscription
+     * is not on an eUICC ({@link #isEmbedded} returns false).
+     */
+    private int mProfileClass;
+
+    /**
      * @hide
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
@@ -182,7 +192,8 @@
             @Nullable UiccAccessRule[] accessRules, String cardString) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                 roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
-                false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID);
+                false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID,
+                SubscriptionManager.PROFILE_CLASS_DEFAULT);
     }
 
     /**
@@ -192,10 +203,10 @@
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
             @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
-            @Nullable String groupUUID, boolean isMetered, int carrierId) {
+            @Nullable String groupUUID, boolean isMetered, int carrierId, int profileClass) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                 roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
-                isOpportunistic, groupUUID, isMetered, false, carrierId);
+                isOpportunistic, groupUUID, isMetered, false, carrierId, profileClass);
     }
 
     /**
@@ -206,7 +217,7 @@
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
             @Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
             boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered,
-            boolean isGroupDisabled, int carrierid) {
+            boolean isGroupDisabled, int carrierid, int profileClass) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -229,6 +240,7 @@
         this.mIsMetered = isMetered;
         this.mIsGroupDisabled = isGroupDisabled;
         this.mCarrierId = carrierid;
+        this.mProfileClass = profileClass;
     }
 
 
@@ -466,6 +478,15 @@
     }
 
     /**
+     * @return the profile class of this subscription.
+     * @hide
+     */
+    @SystemApi
+    public @SubscriptionManager.ProfileClass int getProfileClass() {
+        return this.mProfileClass;
+    }
+
+    /**
      * Checks whether the app with the given context is authorized to manage this subscription
      * according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
      * returns true).
@@ -590,11 +611,12 @@
             boolean isMetered = source.readBoolean();
             boolean isGroupDisabled = source.readBoolean();
             int carrierid = source.readInt();
+            int profileClass = source.readInt();
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
                     isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID,
-                    isMetered, isGroupDisabled, carrierid);
+                    isMetered, isGroupDisabled, carrierid, profileClass);
         }
 
         @Override
@@ -627,6 +649,7 @@
         dest.writeBoolean(mIsMetered);
         dest.writeBoolean(mIsGroupDisabled);
         dest.writeInt(mCarrierId);
+        dest.writeInt(mProfileClass);
     }
 
     @Override
@@ -662,7 +685,8 @@
                 + " accessRules " + Arrays.toString(mAccessRules)
                 + " cardString=" + cardStringToPrint + " cardId=" + mCardId
                 + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
-                + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
+                + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled
+                + " profileClass=" + mProfileClass + "}";
     }
 
     @Override
@@ -670,7 +694,7 @@
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
                 mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
                 mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
-                mIsGroupDisabled, mCarrierId);
+                mIsGroupDisabled, mCarrierId, mProfileClass);
     }
 
     @Override
@@ -705,6 +729,7 @@
                 && Objects.equals(mCardId, toCompare.mCardId)
                 && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
                 && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
-                && Arrays.equals(mAccessRules, toCompare.mAccessRules);
+                && Arrays.equals(mAccessRules, toCompare.mAccessRules)
+                && mProfileClass == toCompare.mProfileClass;
     }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2c712a1..9fa4c3c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -22,6 +22,7 @@
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -62,6 +63,8 @@
 import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.PhoneConstants;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -599,6 +602,73 @@
      * @hide
      */
     public static final String IS_METERED = "is_metered";
+
+    /**
+     * TelephonyProvider column name for the profile class of a subscription
+     * Only present if {@link #IS_EMBEDDED} is 1.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String PROFILE_CLASS = "profile_class";
+
+    /**
+     * Profile class of the subscription
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PROFILE_CLASS_" }, value = {
+            PROFILE_CLASS_TESTING,
+            PROFILE_CLASS_PROVISIONING,
+            PROFILE_CLASS_OPERATIONAL,
+            PROFILE_CLASS_UNSET,
+            PROFILE_CLASS_DEFAULT
+    })
+    public @interface ProfileClass {}
+
+    /**
+     * A testing profile can be pre-loaded or downloaded onto
+     * the eUICC and provides connectivity to test equipment
+     * for the purpose of testing the device and the eUICC. It
+     * is not intended to store any operator credentials.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_TESTING = 0;
+
+    /**
+     * A provisioning profile is pre-loaded onto the eUICC and
+     * provides connectivity to a mobile network solely for the
+     * purpose of provisioning profiles.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_PROVISIONING = 1;
+
+    /**
+     * An operational profile can be pre-loaded or downloaded
+     * onto the eUICC and provides services provided by the
+     * operator.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_OPERATIONAL = 2;
+
+    /**
+     * The profile class is unset. This occurs when profile class
+     * info is not available. The subscription either has no profile
+     * metadata or the profile metadata did not encode profile class.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_UNSET = -1;
+
+    /**
+     * Default profile class
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
+
     /**
      * Broadcast Action: The user has changed one of the default subs related to
      * data, phone calls, or sms</p>
@@ -1102,17 +1172,33 @@
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-        List<SubscriptionInfo> result = null;
+        return getActiveSubscriptionInfoList(false);
+    }
+
+    /**
+     * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
+     * is true, it will filter out the hidden subscriptions.
+     *
+     * @hide
+     */
+    public List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+        List<SubscriptionInfo> activeList = null;
 
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
+                activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
             }
         } catch (RemoteException ex) {
             // ignore it
         }
-        return result;
+
+        if (!userVisibleOnly || activeList == null) {
+            return activeList;
+        } else {
+            return activeList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
+                    .collect(Collectors.toList());
+        }
     }
 
     /**
@@ -2367,10 +2453,39 @@
      *
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setPreferredData(int subId) {
-        if (VDBG) logd("[setPreferredData]+ subId:" + subId);
-        setSubscriptionPropertyHelper(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                "setPreferredData", (iSub)-> iSub.setPreferredData(subId));
+    public void setPreferredDataSubscriptionId(int subId) {
+        if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
+        setSubscriptionPropertyHelper(DEFAULT_SUBSCRIPTION_ID, "setPreferredDataSubscriptionId",
+                (iSub)-> iSub.setPreferredDataSubscriptionId(subId));
+    }
+
+    /**
+     * Get 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 AlternativeNetworkService or carrier apps to switch primary and CBRS
+     * subscription dynamically in multi-SIM devices.
+     *
+     * @return preferred subscription id for cellular data. {@link DEFAULT_SUBSCRIPTION_ID} if
+     * there's no prefered subscription.
+     *
+     * @hide
+     *
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getPreferredDataSubscriptionId() {
+        int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                preferredSubId = iSub.getPreferredDataSubscriptionId();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return preferredSubId;
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4598afa..8053353 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -76,8 +76,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ITelecomService;
 import com.android.internal.telephony.CellNetworkScanResult;
-import com.android.internal.telephony.IAns;
 import com.android.internal.telephony.INumberVerificationCallback;
+import com.android.internal.telephony.IOns;
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
@@ -772,7 +772,6 @@
      * The {@link #EXTRA_DATA_NETWORK_TYPE} extra indicates the connection network type.
      * The {@link #EXTRA_DATA_APN_TYPE} extra indicates the APN type.
      * The {@link #EXTRA_DATA_APN} extra indicates the APN.
-     * The {@link #EXTRA_DATA_CHANGE_REASON} extra indicates the connection change reason.
      * The {@link #EXTRA_DATA_IFACE_PROPERTIES} extra indicates the connection interface.
      * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
      *
@@ -783,7 +782,6 @@
      * @see #EXTRA_DATA_NETWORK_TYPE
      * @see #EXTRA_DATA_APN_TYPE
      * @see #EXTRA_DATA_APN
-     * @see #EXTRA_DATA_CHANGE_REASON
      * @see #EXTRA_DATA_IFACE
      * @see #EXTRA_DATA_FAILURE_CAUSE
      * @hide
@@ -873,18 +871,6 @@
 
     /**
      * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an String representation of the change reason.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_CHANGE_REASON = PhoneConstants.STATE_CHANGE_REASON_KEY;
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
      * for an String representation of the data interface.
      *
      * <p class="note">
@@ -4599,9 +4585,18 @@
       }
     }
 
-    /** Data connection state: Unknown.  Used before we know the state.
-     * @hide
-     */
+    /** @hide */
+    @IntDef(prefix = {"DATA_"}, value = {
+            DATA_UNKNOWN,
+            DATA_DISCONNECTED,
+            DATA_CONNECTING,
+            DATA_CONNECTED,
+            DATA_SUSPENDED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataState{}
+
+    /** Data connection state: Unknown.  Used before we know the state. */
     public static final int DATA_UNKNOWN        = -1;
     /** Data connection state: Disconnected. IP traffic not available. */
     public static final int DATA_DISCONNECTED   = 0;
@@ -4656,8 +4651,8 @@
         return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
     }
 
-    private IAns getIAns() {
-        return IAns.Stub.asInterface(ServiceManager.getService("ians"));
+    private IOns getIOns() {
+        return IOns.Stub.asInterface(ServiceManager.getService("ions"));
     }
 
     //
@@ -4903,19 +4898,53 @@
     /** Callback for providing asynchronous {@link CellInfo} on request */
     public abstract static class CellInfoCallback {
         /**
-         * Response to
+         * Success response to
          * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
          *
-         * <p>Invoked when there is a response to
+         * Invoked when there is a response to
          * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}
          * to provide a list of {@link CellInfo}. If no {@link CellInfo} is available then an empty
-         * list will be provided. If an error occurs, null will be provided.
+         * list will be provided. If an error occurs, null will be provided unless the onError
+         * callback is overridden.
          *
          * @param cellInfo a list of {@link CellInfo}, an empty list, or null.
          *
          * {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
          */
-        public abstract void onCellInfo(List<CellInfo> cellInfo);
+        public abstract void onCellInfo(@NonNull List<CellInfo> cellInfo);
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"ERROR_"}, value = {ERROR_TIMEOUT, ERROR_MODEM_ERROR})
+        public @interface CellInfoCallbackError {}
+
+        /**
+         * The system timed out waiting for a response from the Radio.
+         */
+        public static final int ERROR_TIMEOUT = 1;
+
+        /**
+         * The modem returned a failure.
+         */
+        public static final int ERROR_MODEM_ERROR = 2;
+
+        /**
+         * Error response to
+         * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+         *
+         * Invoked when an error condition prevents updated {@link CellInfo} from being fetched
+         * and returned from the modem. Callers of requestCellInfoUpdate() should override this
+         * function to receive detailed status information in the event of an error. By default,
+         * this function will invoke onCellInfo() with null.
+         *
+         * @param errorCode an error code indicating the type of failure.
+         * @param detail a Throwable object with additional detail regarding the failure if
+         *     available, otherwise null.
+         */
+        public void onError(@CellInfoCallbackError int errorCode, @Nullable Throwable detail) {
+            // By default, simply invoke the success callback with an empty list.
+            onCellInfo(new ArrayList<CellInfo>());
+        }
     };
 
     /**
@@ -4942,6 +4971,12 @@
                             Binder.withCleanCallingIdentity(() ->
                                     executor.execute(() -> callback.onCellInfo(cellInfo)));
                         }
+
+                        public void onError(int errorCode, android.os.ParcelableException detail) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onError(
+                                            errorCode, detail.getCause())));
+                        }
                     }, getOpPackageName());
 
         } catch (RemoteException ex) {
@@ -4976,6 +5011,12 @@
                             Binder.withCleanCallingIdentity(() ->
                                     executor.execute(() -> callback.onCellInfo(cellInfo)));
                         }
+
+                        public void onError(int errorCode, android.os.ParcelableException detail) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onError(
+                                            errorCode, detail.getCause())));
+                        }
                     }, getOpPackageName(), workSource);
         } catch (RemoteException ex) {
         }
@@ -5863,9 +5904,14 @@
 
     /**
      * Returns the IMS Service Table (IST) that was loaded from the ISIM.
+     *
+     * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table.
+     *
      * @return IMS Service Table or null if not present or not loaded
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getIsimIst() {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -9009,6 +9055,8 @@
      * <p>This method works only on devices with {@link
      * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
      *
+     * @deprecated use setCarrierRestrictionRules instead
+     *
      * @return The number of carriers set successfully. Should be length of
      * carrierList on success; -1 if carrierList null or on error.
      * @hide
@@ -9016,44 +9064,144 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) {
+        // Execute the method setCarrierRestrictionRules with an empty excluded list and
+        // indicating priority for the allowed list.
+        CarrierRestrictionRules carrierRestrictionRules = CarrierRestrictionRules.newBuilder()
+                .setAllowedCarriers(carriers)
+                .setDefaultCarrierRestriction(
+                    CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)
+                .build();
+
+        int result = setCarrierRestrictionRules(carrierRestrictionRules);
+
+        // Convert boolean result into int, as required by this method.
+        if (result == SET_CARRIER_RESTRICTION_SUCCESS) {
+            return carriers.size();
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * The carrier restrictions were successfully set.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0;
+
+    /**
+     * The carrier restrictions were not set due to lack of support in the modem. This can happen
+     * if the modem does not support setting the carrier restrictions or if the configuration
+     * passed in the {@code setCarrierRestrictionRules} is not supported by the modem.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1;
+
+    /**
+     * The setting of carrier restrictions failed.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_CARRIER_RESTRICTION_ERROR = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SET_CARRIER_RESTRICTION_"},
+            value = {
+                    SET_CARRIER_RESTRICTION_SUCCESS,
+                    SET_CARRIER_RESTRICTION_NOT_SUPPORTED,
+                    SET_CARRIER_RESTRICTION_ERROR
+            })
+    public @interface SetCarrierRestrictionResult {}
+
+    /**
+     * Set the allowed carrier list and the excluded carrier list indicating the priority between
+     * the two lists.
+     * Requires system privileges.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     *
+     * <p>This method works only on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
+     * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success.
+     * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the
+     * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SetCarrierRestrictionResult
+    public int setCarrierRestrictionRules(@NonNull CarrierRestrictionRules rules) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.setAllowedCarriers(slotIndex, carriers);
+                return service.setAllowedCarriers(rules);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
         } catch (NullPointerException e) {
             Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
         }
-        return -1;
+        return SET_CARRIER_RESTRICTION_ERROR;
     }
 
     /**
      * Get the allowed carrier list for slotIndex.
-     * Require system privileges. In the future we may add this to carrier APIs.
+     * Requires system privileges.
      *
      * <p>This method returns valid data on devices with {@link
      * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
      *
+     * @deprecated Apps should use {@link getCarriersRestrictionRules} to retrieve the list of
+     * allowed and excliuded carriers, as the result of this API is valid only when the excluded
+     * list is empty. This API could return an empty list, even if some restrictions are present.
+     *
      * @return List of {@link android.telephony.CarrierIdentifier}; empty list
      * means all carriers are allowed.
      * @hide
      */
+    @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
+        CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules();
+        if (carrierRestrictionRule != null) {
+            return carrierRestrictionRule.getAllowedCarriers();
+        }
+        return new ArrayList<CarrierIdentifier>(0);
+    }
+
+    /**
+     * Get the allowed carrier list and the excluded carrier list indicating the priority between
+     * the two lists.
+     * Require system privileges. In the future we may add this to carrier APIs.
+     *
+     * <p>This method returns valid data on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
+     * @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the
+     * excluded carrier list with the priority between the two lists. Returns {@code null}
+     * in case of error.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @Nullable
+    public CarrierRestrictionRules getCarrierRestrictionRules() {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.getAllowedCarriers(slotIndex);
+                return service.getAllowedCarriers();
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
         } catch (NullPointerException e) {
             Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
         }
-        return new ArrayList<CarrierIdentifier>(0);
+        return null;
     }
 
     /**
@@ -9456,10 +9604,10 @@
     }
 
     /**
-     * Enable or disable AlternativeNetworkService.
+     * Enable or disable OpportunisticNetworkService.
      *
      * This method should be called to enable or disable
-     * AlternativeNetwork service on the device.
+     * OpportunisticNetwork service on the device.
      *
      * <p>
      * Requires Permission:
@@ -9470,25 +9618,25 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public boolean setAlternativeNetworkState(boolean enable) {
+    public boolean setOpportunisticNetworkState(boolean enable) {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         boolean ret = false;
         try {
-            IAns iAlternativeNetworkService = getIAns();
-            if (iAlternativeNetworkService != null) {
-                ret = iAlternativeNetworkService.setEnable(enable, pkgForDebug);
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null) {
+                ret = iOpportunisticNetworkService.setEnable(enable, pkgForDebug);
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "enableAlternativeNetwork RemoteException", ex);
+            Rlog.e(TAG, "enableOpportunisticNetwork RemoteException", ex);
         }
 
         return ret;
     }
 
     /**
-     * is AlternativeNetworkService enabled
+     * is OpportunisticNetworkService enabled
      *
-     * This method should be called to determine if the AlternativeNetworkService is
+     * This method should be called to determine if the OpportunisticNetworkService is
      * enabled
      *
      * <p>
@@ -9497,17 +9645,17 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public boolean isAlternativeNetworkEnabled() {
+    public boolean isOpportunisticNetworkEnabled() {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         boolean isEnabled = false;
 
         try {
-            IAns iAlternativeNetworkService = getIAns();
-            if (iAlternativeNetworkService != null) {
-                isEnabled = iAlternativeNetworkService.isEnabled(pkgForDebug);
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null) {
+                isEnabled = iOpportunisticNetworkService.isEnabled(pkgForDebug);
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "enableAlternativeNetwork RemoteException", ex);
+            Rlog.e(TAG, "enableOpportunisticNetwork RemoteException", ex);
         }
 
         return isEnabled;
@@ -9862,12 +10010,13 @@
     public boolean setPreferredOpportunisticDataSubscription(int subId) {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         try {
-            IAns iAlternativeNetworkService = getIAns();
-            if (iAlternativeNetworkService != null) {
-                return iAlternativeNetworkService.setPreferredData(subId, pkgForDebug);
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null) {
+                return iOpportunisticNetworkService
+                        .setPreferredDataSubscriptionId(subId, pkgForDebug);
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "setPreferredData RemoteException", ex);
+            Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex);
         }
         return false;
     }
@@ -9886,12 +10035,12 @@
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         try {
-            IAns iAlternativeNetworkService = getIAns();
-            if (iAlternativeNetworkService != null) {
-                subId = iAlternativeNetworkService.getPreferredData(pkgForDebug);
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null) {
+                subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(pkgForDebug);
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "getPreferredData RemoteException", ex);
+            Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex);
         }
         return subId;
     }
@@ -9899,8 +10048,8 @@
     /**
      * Update availability of a list of networks in the current location.
      *
-     * This api should be called to inform AlternativeNetwork Service about the availability
-     * of a network at the current location. This information will be used by AlternativeNetwork
+     * This api should be called to inform OpportunisticNetwork Service about the availability
+     * of a network at the current location. This information will be used by OpportunisticNetwork
      * service to decide to attach to the network opportunistically. If an empty list is passed,
      * it is assumed that no network is available.
      * Requires that the calling app has carrier privileges on both primary and
@@ -9915,9 +10064,9 @@
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         boolean ret = false;
         try {
-            IAns iAlternativeNetworkService = getIAns();
-            if (iAlternativeNetworkService != null) {
-                ret = iAlternativeNetworkService.updateAvailableNetworks(availableNetworks,
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null && availableNetworks != null) {
+                ret = iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks,
                         pkgForDebug);
             }
         } catch (RemoteException ex) {
@@ -9925,4 +10074,33 @@
         }
         return ret;
     }
+
+    /**
+     * Enable or disable a logical modem stack. When a logical modem is disabled, the corresponding
+     * SIM will still be visible to the user but its mapping modem will not have any radio activity.
+     * For example, we will disable a modem when user or system believes the corresponding SIM
+     * is temporarily not needed (e.g. out of coverage), and will enable it back on when needed.
+     *
+     * Requires that the calling app has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * @param slotIndex which corresponding modem will operate on.
+     * @param enable whether to enable or disable the modem stack.
+     * @return whether the operation is successful.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean enableModemForSlot(int slotIndex, boolean enable) {
+        boolean ret = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ret = telephony.enableModemForSlot(slotIndex, enable);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "enableModem RemoteException", ex);
+        }
+        return ret;
+    }
 }
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index fe062d5..e68256d 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -27,8 +27,10 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -175,6 +177,12 @@
      * Bit-field which indicates the number is from the platform-maintained database.
      */
     public static final int EMERGENCY_NUMBER_SOURCE_DATABASE =  1 << 4;
+    /**
+     * Bit-field which indicates the number is from test mode.
+     *
+     * @hide
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_TEST =  1 << 5;
     /** Bit-field which indicates the number is from the modem config. */
     public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
             EmergencyNumberSource.MODEM_CONFIG;
@@ -198,21 +206,56 @@
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
     }
 
+    /**
+     * Indicated the framework does not know whether an emergency call should be placed using
+     * emergency or normal call routing. This means the underlying radio or IMS implementation is
+     * free to determine for itself how to route the call.
+     */
+    public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0;
+    /**
+     * Indicates the radio or IMS implementation must handle the call through emergency routing.
+     */
+    public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1;
+    /**
+     * Indicates the radio or IMS implementation must handle the call through normal call routing.
+     */
+    public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2;
+
+    /**
+     * The routing to tell how to handle the call for the corresponding emergency number.
+     *
+     * @hide
+     */
+    @IntDef(flag = false, prefix = { "EMERGENCY_CALL_ROUTING_" }, value = {
+            EMERGENCY_CALL_ROUTING_UNKNOWN,
+            EMERGENCY_CALL_ROUTING_EMERGENCY,
+            EMERGENCY_CALL_ROUTING_NORMAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyCallRouting {}
+
+
     private final String mNumber;
     private final String mCountryIso;
     private final String mMnc;
     private final int mEmergencyServiceCategoryBitmask;
+    private final List<String> mEmergencyUrns;
     private final int mEmergencyNumberSourceBitmask;
+    private final int mEmergencyCallRouting;
 
     /** @hide */
-    public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
-                           @NonNull String mnc, int emergencyServiceCategories,
-                           int emergencyNumberSources) {
+    public EmergencyNumber(@NonNull String number, @NonNull String countryIso, @NonNull String mnc,
+                           @EmergencyServiceCategories int emergencyServiceCategories,
+                           @NonNull List<String> emergencyUrns,
+                           @EmergencyNumberSources int emergencyNumberSources,
+                           @EmergencyCallRouting int emergencyCallRouting) {
         this.mNumber = number;
         this.mCountryIso = countryIso;
         this.mMnc = mnc;
         this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
+        this.mEmergencyUrns = emergencyUrns;
         this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
+        this.mEmergencyCallRouting = emergencyCallRouting;
     }
 
     /** @hide */
@@ -221,9 +264,36 @@
         mCountryIso = source.readString();
         mMnc = source.readString();
         mEmergencyServiceCategoryBitmask = source.readInt();
+        mEmergencyUrns = source.createStringArrayList();
         mEmergencyNumberSourceBitmask = source.readInt();
+        mEmergencyCallRouting = source.readInt();
     }
 
+    @Override
+    /** @hide */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mNumber);
+        dest.writeString(mCountryIso);
+        dest.writeString(mMnc);
+        dest.writeInt(mEmergencyServiceCategoryBitmask);
+        dest.writeStringList(mEmergencyUrns);
+        dest.writeInt(mEmergencyNumberSourceBitmask);
+        dest.writeInt(mEmergencyCallRouting);
+    }
+
+    public static final Parcelable.Creator<EmergencyNumber> CREATOR =
+            new Parcelable.Creator<EmergencyNumber>() {
+                @Override
+                public EmergencyNumber createFromParcel(Parcel in) {
+                    return new EmergencyNumber(in);
+                }
+
+                @Override
+                public EmergencyNumber[] newArray(int size) {
+                    return new EmergencyNumber[size];
+                }
+            };
+
     /**
      * Get the dialing number of the emergency number.
      *
@@ -264,6 +334,21 @@
     }
 
     /**
+     * Returns the bitmask of emergency service categories of the emergency number for
+     * internal dialing.
+     *
+     * @return bitmask of the emergency service categories
+     *
+     * @hide
+     */
+    public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmaskInternalDial() {
+        if (mEmergencyNumberSourceBitmask == EMERGENCY_NUMBER_SOURCE_DATABASE) {
+            return EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+        }
+        return mEmergencyServiceCategoryBitmask;
+    }
+
+    /**
      * Returns the emergency service categories of the emergency number.
      *
      * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only
@@ -287,6 +372,22 @@
     }
 
     /**
+     * Returns the list of emergency Uniform Resources Names (URN) of the emergency number.
+     *
+     * For example, {@code urn:service:sos} is the generic URN for contacting emergency services
+     * of all type.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            RFC 5031
+     *
+     * @return list of emergency Uniform Resources Names (URN) or an empty list if the emergency
+     *         number does not have a specified emergency Uniform Resource Name.
+     */
+    public @NonNull List<String> getEmergencyUrns() {
+        return mEmergencyUrns;
+    }
+
+    /**
      * Checks if the emergency service category is unspecified for the emergency number
      * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}.
      *
@@ -352,14 +453,17 @@
         return (mEmergencyNumberSourceBitmask & sources) == sources;
     }
 
-    @Override
-    /** @hide */
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mNumber);
-        dest.writeString(mCountryIso);
-        dest.writeString(mMnc);
-        dest.writeInt(mEmergencyServiceCategoryBitmask);
-        dest.writeInt(mEmergencyNumberSourceBitmask);
+    /**
+     * Returns the emergency call routing information.
+     *
+     * <p>Some regions require some emergency numbers which are not routed using typical emergency
+     * call processing, but are instead placed as regular phone calls. The emergency call routing
+     * field provides information about how an emergency call will be routed when it is placed.
+     *
+     * @return the emergency call routing requirement
+     */
+    public @EmergencyCallRouting int getEmergencyCallRouting() {
+        return mEmergencyCallRouting;
     }
 
     @Override
@@ -373,7 +477,9 @@
         return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
                 + "|Mnc-" + mMnc
                 + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
-                + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+                + "|Urns-" + mEmergencyUrns
+                + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask)
+                + "|Routing-" + Integer.toBinaryString(mEmergencyCallRouting);
     }
 
     @Override
@@ -381,7 +487,20 @@
         if (!EmergencyNumber.class.isInstance(o)) {
             return false;
         }
-        return (o == this || toString().equals(o.toString()));
+        EmergencyNumber other = (EmergencyNumber) o;
+        return mNumber.equals(other.mNumber)
+                && mCountryIso.equals(other.mCountryIso)
+                && mMnc.equals(other.mMnc)
+                && mEmergencyServiceCategoryBitmask == other.mEmergencyServiceCategoryBitmask
+                && mEmergencyUrns.equals(other.mEmergencyUrns)
+                && mEmergencyNumberSourceBitmask == other.mEmergencyNumberSourceBitmask
+                && mEmergencyCallRouting == other.mEmergencyCallRouting;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNumber, mCountryIso, mMnc, mEmergencyServiceCategoryBitmask,
+                mEmergencyUrns, mEmergencyNumberSourceBitmask, mEmergencyCallRouting);
     }
 
     /**
@@ -462,12 +581,12 @@
                 continue;
             }
             for (int j = i + 1; j < emergencyNumberList.size(); j++) {
-                if (isSameEmergencyNumber(
+                if (areSameEmergencyNumbers(
                         emergencyNumberList.get(i), emergencyNumberList.get(j))) {
                     Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: "
                             + emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j));
                     // Set the merged emergency number in the current position
-                    emergencyNumberList.set(i, mergeNumbers(
+                    emergencyNumberList.set(i, mergeSameEmergencyNumbers(
                             emergencyNumberList.get(i), emergencyNumberList.get(j)));
                     // Mark the emergency number has been merged
                     mergedEmergencyNumber.add(emergencyNumberList.get(j));
@@ -480,14 +599,15 @@
                 emergencyNumberList.remove(i--);
             }
         }
+        Collections.sort(emergencyNumberList);
     }
 
     /**
      * Check if two emergency numbers are the same.
      *
      * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
-     * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
-     * for the same EmergencyNumber.
+     * 'categories', and 'routing' fields. Multiple Emergency Number Sources should be
+     * merged into one bitfield for the same EmergencyNumber.
      *
      * @param first first EmergencyNumber to compare
      * @param second second EmergencyNumber to compare
@@ -495,8 +615,8 @@
      *
      * @hide
      */
-    public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first,
-                                                @NonNull EmergencyNumber second) {
+    public static boolean areSameEmergencyNumbers(@NonNull EmergencyNumber first,
+                                                  @NonNull EmergencyNumber second) {
         if (!first.getNumber().equals(second.getNumber())) {
             return false;
         }
@@ -510,11 +630,24 @@
                 != second.getEmergencyServiceCategoryBitmask()) {
             return false;
         }
+        if (first.getEmergencyUrns().equals(second.getEmergencyUrns())) {
+            return false;
+        }
+        if (first.getEmergencyCallRouting() != second.getEmergencyCallRouting()) {
+            return false;
+        }
+        // Never merge two numbers if one of them is from test mode but the other one is not;
+        // This supports to remove a number from the test mode.
+        if (first.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)
+                ^ second.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)) {
+            return false;
+        }
         return true;
     }
 
     /**
-     * Get a merged EmergencyNumber for two numbers if they are the same.
+     * Get a merged EmergencyNumber from two same emergency numbers. Two emergency numbers are
+     * the same if {@link #areSameEmergencyNumbers} returns {@code true}.
      *
      * @param first first EmergencyNumber to compare
      * @param second second EmergencyNumber to compare
@@ -522,27 +655,25 @@
      *
      * @hide
      */
-    public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first,
-                                         @NonNull EmergencyNumber second) {
-        if (isSameEmergencyNumber(first, second)) {
+    public static EmergencyNumber mergeSameEmergencyNumbers(@NonNull EmergencyNumber first,
+                                                            @NonNull EmergencyNumber second) {
+        if (areSameEmergencyNumbers(first, second)) {
             return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
                     first.getEmergencyServiceCategoryBitmask(),
+                    first.getEmergencyUrns(),
                     first.getEmergencyNumberSourceBitmask()
-                            | second.getEmergencyNumberSourceBitmask());
+                            | second.getEmergencyNumberSourceBitmask(),
+                    first.getEmergencyCallRouting());
         }
         return null;
     }
 
-    public static final Parcelable.Creator<EmergencyNumber> CREATOR =
-            new Parcelable.Creator<EmergencyNumber>() {
-        @Override
-        public EmergencyNumber createFromParcel(Parcel in) {
-            return new EmergencyNumber(in);
-        }
-
-        @Override
-        public EmergencyNumber[] newArray(int size) {
-            return new EmergencyNumber[size];
-        }
-    };
+    /**
+     * Validate Emergency Number address that only allows '0'-'9', '*', or '#'
+     *
+     * @hide
+     */
+    public static boolean validateEmergencyNumberAddress(String address) {
+        return address.matches("[0-9*#]+");
+    }
 }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 6326cc6..0e5c71d 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -30,6 +30,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.euicc.IEuiccController;
 
@@ -40,7 +41,11 @@
  * EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs.
  *
  * <p>You do not instantiate this class directly; instead, you retrieve an instance through
- * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}.
+ * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}. This instance will be
+ * created using the default eUICC.
+ *
+ * <p>On a device with multiple eUICCs, you may want to create multiple EuiccManagers. To do this
+ * you can call {@link #createForCardId}.
  *
  * <p>See {@link #isEnabled} before attempting to use these APIs.
  */
@@ -318,10 +323,29 @@
     public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
 
     private final Context mContext;
+    private final int mCardId;
 
     /** @hide */
     public EuiccManager(Context context) {
         mContext = context;
+        TelephonyManager tm = (TelephonyManager)
+                context.getSystemService(Context.TELEPHONY_SERVICE);
+        mCardId = tm.getCardIdForDefaultEuicc();
+    }
+
+    /** @hide */
+    private EuiccManager(Context context, int cardId) {
+        mContext = context;
+        mCardId = cardId;
+    }
+
+    /**
+     * Create a new EuiccManager object pinned to the given card ID.
+     *
+     * @return an EuiccManager that uses the given card ID for all calls.
+     */
+    public EuiccManager createForCardId(int cardId) {
+        return new EuiccManager(mContext, cardId);
     }
 
     /**
@@ -337,14 +361,15 @@
     public boolean isEnabled() {
         // In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
         // restrictions.
-        return getIEuiccController() != null;
+        return getIEuiccController() != null && mCardId != TelephonyManager.INVALID_CARD_ID;
     }
 
     /**
      * Returns the EID identifying the eUICC hardware.
      *
      * <p>Requires that the calling app has carrier privileges on the active subscription on the
-     * eUICC.
+     * current eUICC. A calling app with carrier privileges for one eUICC may not necessarily have
+     * access to the EID of another eUICC.
      *
      * @return the EID. May be null if {@link #isEnabled()} is false or the eUICC is not ready.
      */
@@ -354,7 +379,7 @@
             return null;
         }
         try {
-            return getIEuiccController().getEid();
+            return getIEuiccController().getEid(mCardId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -377,7 +402,7 @@
             return EUICC_OTA_STATUS_UNAVAILABLE;
         }
         try {
-            return getIEuiccController().getOtaStatus();
+            return getIEuiccController().getOtaStatus(mCardId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -387,10 +412,10 @@
      * Attempt to download the given {@link DownloadableSubscription}.
      *
      * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
-     * or the calling app must be authorized to manage both the currently-active subscription and
-     * the subscription to be downloaded according to the subscription metadata. Without the former,
-     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
-     * intent to prompt the user to accept the download.
+     * or the calling app must be authorized to manage both the currently-active subscription on the
+     * current eUICC and the subscription to be downloaded according to the subscription metadata.
+     * Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
+     * returned in the callback intent to prompt the user to accept the download.
      *
      * @param subscription the subscription to download.
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
@@ -404,7 +429,7 @@
             return;
         }
         try {
-            getIEuiccController().downloadSubscription(subscription, switchAfterDownload,
+            getIEuiccController().downloadSubscription(mCardId, subscription, switchAfterDownload,
                     mContext.getOpPackageName(), null /* resolvedBundle */, callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -471,7 +496,7 @@
             return;
         }
         try {
-            getIEuiccController().continueOperation(resolutionIntent, resolutionExtras);
+            getIEuiccController().continueOperation(mCardId, resolutionIntent, resolutionExtras);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -503,8 +528,8 @@
             return;
         }
         try {
-            getIEuiccController().getDownloadableSubscriptionMetadata(
-                    subscription, mContext.getOpPackageName(), callbackIntent);
+            getIEuiccController().getDownloadableSubscriptionMetadata(mCardId, subscription,
+                    mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -533,7 +558,7 @@
             return;
         }
         try {
-            getIEuiccController().getDefaultDownloadableSubscriptionList(
+            getIEuiccController().getDefaultDownloadableSubscriptionList(mCardId,
                     mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -552,7 +577,7 @@
             return null;
         }
         try {
-            return getIEuiccController().getEuiccInfo();
+            return getIEuiccController().getEuiccInfo(mCardId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -578,7 +603,7 @@
             return;
         }
         try {
-            getIEuiccController().deleteSubscription(
+            getIEuiccController().deleteSubscription(mCardId,
                     subscriptionId, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -606,7 +631,7 @@
             return;
         }
         try {
-            getIEuiccController().switchToSubscription(
+            getIEuiccController().switchToSubscription(mCardId,
                     subscriptionId, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -616,14 +641,13 @@
     /**
      * Update the nickname for the given subscription.
      *
-     * <p>Requires that the calling app has the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
-     * internal system use only.
+     * <p>Requires that the calling app has carrier privileges according to the metadata of the
+     * profile to be updated, or the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param subscriptionId the ID of the subscription to update.
      * @param nickname the new nickname to apply.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
-     * @hide
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
@@ -633,8 +657,8 @@
             return;
         }
         try {
-            getIEuiccController().updateSubscriptionNickname(
-                    subscriptionId, nickname, callbackIntent);
+            getIEuiccController().updateSubscriptionNickname(mCardId,
+                    subscriptionId, nickname, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -657,7 +681,7 @@
             return;
         }
         try {
-            getIEuiccController().eraseSubscriptions(callbackIntent);
+            getIEuiccController().eraseSubscriptions(mCardId, callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -687,7 +711,7 @@
             return;
         }
         try {
-            getIEuiccController().retainSubscriptionsForFactoryReset(callbackIntent);
+            getIEuiccController().retainSubscriptionsForFactoryReset(mCardId, callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index cb6fcd7..59167b7 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -24,13 +24,17 @@
 import android.os.Parcelable;
 import android.telecom.VideoProfile;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
 import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneConstants;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Parcelable object to handle IMS call profile.
@@ -321,6 +325,32 @@
             EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
 
     /**
+     * The emergency Uniform Resource Names (URN), only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    private List<String> mEmergencyUrns = new ArrayList<>();
+
+    /**
+     * The emergency call routing, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is any of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+     * </ol>
+     */
+    private @EmergencyCallRouting int mEmergencyCallRouting =
+            EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+
+    /** Indicates if the call is for testing purpose */
+    private boolean mEmergencyCallTesting = false;
+
+    /**
      * Extras associated with this {@link ImsCallProfile}.
      * <p>
      * Valid data types include:
@@ -503,10 +533,14 @@
 
     @Override
     public String toString() {
-        return "{ serviceType=" + mServiceType +
-                ", callType=" + mCallType +
-                ", restrictCause=" + mRestrictCause +
-                ", mediaProfile=" + mMediaProfile.toString() + " }";
+        return "{ serviceType=" + mServiceType
+                + ", callType=" + mCallType
+                + ", restrictCause=" + mRestrictCause
+                + ", mediaProfile=" + mMediaProfile.toString()
+                + ", emergencyServiceCategories=" + mEmergencyServiceCategories
+                + ", emergencyUrns=" + mEmergencyUrns
+                + ", emergencyCallRouting=" + mEmergencyCallRouting
+                + ", emergencyCallTesting=" + mEmergencyCallTesting + " }";
     }
 
     @Override
@@ -522,6 +556,9 @@
         out.writeBundle(filteredExtras);
         out.writeParcelable(mMediaProfile, 0);
         out.writeInt(mEmergencyServiceCategories);
+        out.writeStringList(mEmergencyUrns);
+        out.writeInt(mEmergencyCallRouting);
+        out.writeBoolean(mEmergencyCallTesting);
     }
 
     private void readFromParcel(Parcel in) {
@@ -530,6 +567,9 @@
         mCallExtras = in.readBundle();
         mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
         mEmergencyServiceCategories = in.readInt();
+        mEmergencyUrns = in.createStringArrayList();
+        mEmergencyCallRouting = in.readInt();
+        mEmergencyCallTesting = in.readBoolean();
     }
 
     public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -740,6 +780,24 @@
     }
 
     /**
+     * Set the emergency number information. The set value is valid
+     * only if {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     *
+     * @hide
+     */
+    public void setEmergencyCallInfo(EmergencyNumber num) {
+        setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
+        setEmergencyUrns(num.getEmergencyUrns());
+        setEmergencyCallRouting(num.getEmergencyCallRouting());
+        setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
+                == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+    }
+
+    /**
      * Set the emergency service categories. The set value is valid only if
      * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
      *
@@ -758,12 +816,50 @@
      * Reference: 3gpp 23.167, Section 6 - Functional description;
      *            3gpp 22.101, Section 10 - Emergency Calls.
      */
+    @VisibleForTesting
     public void setEmergencyServiceCategories(
             @EmergencyServiceCategories int emergencyServiceCategories) {
         mEmergencyServiceCategories = emergencyServiceCategories;
     }
 
     /**
+     * Set the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
+     * returns {@link #SERVICE_TYPE_EMERGENCY}.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    @VisibleForTesting
+    public void setEmergencyUrns(List<String> emergencyUrns) {
+        mEmergencyUrns = emergencyUrns;
+    }
+
+    /**
+     * Set the emergency call routing, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is any of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+     * </ol>
+     */
+    @VisibleForTesting
+    public void setEmergencyCallRouting(@EmergencyCallRouting int emergencyCallRouting) {
+        mEmergencyCallRouting = emergencyCallRouting;
+    }
+
+    /**
+     * Set if this is for testing emergency call, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}.
+     */
+    @VisibleForTesting
+    public void setEmergencyCallTesting(boolean isTesting) {
+        mEmergencyCallTesting = isTesting;
+    }
+
+    /**
      * Get the emergency service categories, only valid if {@link #getServiceType} returns
      * {@link #SERVICE_TYPE_EMERGENCY}
      *
@@ -787,4 +883,37 @@
     public @EmergencyServiceCategories int getEmergencyServiceCategories() {
         return mEmergencyServiceCategories;
     }
+
+    /**
+     * Get the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
+     * returns {@link #SERVICE_TYPE_EMERGENCY}.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    public List<String> getEmergencyUrns() {
+        return mEmergencyUrns;
+    }
+
+    /**
+     * Get the emergency call routing, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is any of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+     * </ol>
+     */
+    public @EmergencyCallRouting int getEmergencyCallRouting() {
+        return mEmergencyCallRouting;
+    }
+
+    /**
+     * Get if the emergency call is for testing purpose.
+     */
+    public boolean isEmergencyCallTesting() {
+        return mEmergencyCallTesting;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index df903cc2..397d5d9 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -443,6 +443,13 @@
         public void callSessionRttMessageReceived(String rttMessage) {
             // no-op
         }
+
+        /**
+         * While in call, there has been a change in RTT audio indicator.
+         */
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+            // no-op
+        }
     }
 
     private final IImsCallSession miSession;
@@ -1397,6 +1404,16 @@
                 mListener.callSessionRttMessageReceived(rttMessage);
             }
         }
+
+        /**
+         * While in call, there has been a change in RTT audio indicator.
+         */
+        @Override
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionRttAudioIndicatorChanged(profile);
+            }
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index a7f124a..a4696a3 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -599,5 +599,18 @@
             throw new RuntimeException(e);
         }
     }
+
+    /**
+     * While in call, there has been a change in RTT audio indicator.
+     *
+     * @param profile updated ImsStreamMediaProfile
+     */
+    public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+        try {
+            mListener.callSessionRttAudioIndicatorChanged(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
 
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 122626f..9414abd 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -92,7 +92,7 @@
     public static final int WIFI_MODE_WIFI_PREFERRED = 2;
 
     /**
-     * Callback class for receiving Registration callback events.
+     * Callback class for receiving IMS network Registration callback events.
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
      * @see #unregisterImsRegistrationCallback(RegistrationCallback)
      */
@@ -241,7 +241,8 @@
     }
 
     /**
-     * Receives IMS capability status updates from the ImsService.
+     * Receives IMS capability status updates from the ImsService. This information is also
+     * available via the {@link #isAvailable(int, int)} method below.
      *
      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
@@ -290,6 +291,8 @@
          * If unavailable, the feature is not able to support the unavailable capability at this
          * time.
          *
+         * This information can also be queried using the {@link #isAvailable(int, int)} API.
+         *
          * @param capabilities The new availability of the capabilities.
          */
         public void onCapabilitiesStatusChanged(
@@ -304,7 +307,7 @@
         /**@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) {
+        public final void setExecutor(Executor executor) {
             mBinder.setExecutor(executor);
         }
     }
@@ -342,8 +345,7 @@
      * 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 #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up
-     * after a subscription is removed.
+     * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
      *
      * When the callback is registered, it will initiate the callback c to be called with the
      * current registration state.
@@ -351,6 +353,12 @@
      * @param executor The executor the callback events should be run on.
      * @param c The {@link RegistrationCallback} to be added.
      * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+     * @throws IllegalArgumentException if the subscription associated with this callback is not
+     * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
+     * {@link CapabilityCallback} callback.
+     * @throws IllegalStateException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerImsRegistrationCallback(@CallbackExecutor Executor executor,
@@ -370,11 +378,17 @@
     }
 
     /**
-     * Removes an existing {@link RegistrationCallback}. Ensure to call this method when cleaning
-     * up to avoid memory leaks or when the subscription is removed.
+     * Removes an existing {@link RegistrationCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     *
      * @param c The {@link RegistrationCallback} to be removed.
      * @see SubscriptionManager.OnSubscriptionsChangedListener
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+     * @throws IllegalArgumentException if the subscription ID associated with this callback is
+     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
@@ -389,12 +403,14 @@
     }
 
     /**
-     * Registers a {@link CapabilityCallback} with the system, which will provide MmTel capability
-     * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
+     * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
+     * availability updates for the subscription specified in
+     * {@link #createForSubscriptionId(Context, int)}. The method {@link #isAvailable(int, int)}
+     * can also be used to query this information at any time.
+     *
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
      * subscription changed events and call
-     * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up after a
-     * subscription is removed.
+     * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
      *
      * When the callback is registered, it will initiate the callback c to be called with the
      * current capabilities.
@@ -402,9 +418,15 @@
      * @param executor The executor the callback events should be run on.
      * @param c The MmTel {@link CapabilityCallback} to be registered.
      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
+     * @throws IllegalArgumentException if the subscription associated with this callback is not
+     * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
+     * {@link CapabilityCallback} callback.
+     * @throws IllegalStateException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void registerMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+    public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull CapabilityCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -421,10 +443,15 @@
     }
 
     /**
-     * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
-     * up to avoid memory leaks.
+     * Removes an existing MmTel {@link CapabilityCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
      * @param c The MmTel {@link CapabilityCallback} to be removed.
      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
+     * @throws IllegalArgumentException if the subscription ID associated with this callback is
+     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
@@ -714,7 +741,7 @@
      * @see #setVoWiFiRoamingSetting(boolean)
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+    public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
         try {
             return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
         } catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index 52d72b5..837ef54 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -97,6 +97,9 @@
     // Rtt related information
     /** @hide */
     public int mRttMode;
+    // RTT Audio Speech Indicator
+    /** @hide */
+    public boolean mHasRttAudioSpeech = false;
 
     /** @hide */
     public ImsStreamMediaProfile(Parcel in) {
@@ -197,7 +200,8 @@
                 ", audioDirection=" + mAudioDirection +
                 ", videoQuality=" + mVideoQuality +
                 ", videoDirection=" + mVideoDirection +
-                ", rttMode=" + mRttMode + " }";
+                ", rttMode=" + mRttMode +
+                ", hasRttAudioSpeech=" + mHasRttAudioSpeech + " }";
     }
 
     @Override
@@ -212,6 +216,7 @@
         out.writeInt(mVideoQuality);
         out.writeInt(mVideoDirection);
         out.writeInt(mRttMode);
+        out.writeBoolean(mHasRttAudioSpeech);
     }
 
     private void readFromParcel(Parcel in) {
@@ -220,6 +225,7 @@
         mVideoQuality = in.readInt();
         mVideoDirection = in.readInt();
         mRttMode = in.readInt();
+        mHasRttAudioSpeech = in.readBoolean();
     }
 
     public static final Creator<ImsStreamMediaProfile> CREATOR =
@@ -250,6 +256,10 @@
         mRttMode = rttMode;
     }
 
+    public void setRttAudioSpeech(boolean audioOn) {
+        mHasRttAudioSpeech = audioOn;
+    }
+
     public int getAudioQuality() {
         return mAudioQuality;
     }
@@ -269,4 +279,8 @@
     public int getRttMode() {
         return mRttMode;
     }
+
+    public boolean getRttAudioSpeech() {
+        return mHasRttAudioSpeech;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 916e282..d37198a 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -133,33 +133,40 @@
     }
 
     /**
-     * Register a new {@link Callback} to listen to changes to changes in
-     * IMS provisioning. Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
-     * Subscription changed events and call
-     * {@link #unregisterProvisioningChangedCallback(Callback)} to clean up after a
-     * subscription is removed.
+     * Register a new {@link Callback} to listen to changes to changes in IMS provisioning.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed.
      * @param executor The {@link Executor} to call the callback methods on
      * @param callback The provisioning callbackto be registered.
      * @see #unregisterProvisioningChangedCallback(Callback)
      * @see SubscriptionManager.OnSubscriptionsChangedListener
+     * @throws IllegalArgumentException if the subscription associated with this callback is not
+     * active (SIM is not inserted, ESIM inactive) or the subscription is invalid.
+     * @throws IllegalStateException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerProvisioningChangedCallback(@CallbackExecutor Executor executor,
             @NonNull Callback callback) {
         callback.setExecutor(executor);
         try {
-            getITelephony().registerImsProvisioningChangedCallback(mSubId,
-                    callback.getBinder());
+            getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
-     * Unregister an existing {@link Callback}. Ensure to call this method when cleaning
-     * up to avoid memory leaks or when the subscription is removed.
+     * Unregister an existing {@link Callback}. When the subscription associated with this
+     * callback is removed (SIM removed, ESIM swap, etc...), this callback will automatically be
+     * removed. If this method is called for an inactive subscription, it will result in a no-op.
      * @param callback The existing {@link Callback} to be removed.
      * @see #registerProvisioningChangedCallback(Executor, Callback)
+     *
+     * @throws IllegalArgumentException if the subscription associated with this callback is
+     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
index 5122c7d..709b3aa 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -23,6 +23,10 @@
  * @hide - TODO(sahinc) make this public
  */
 public class Rcs1To1Thread extends RcsThread {
+    public Rcs1To1Thread(int threadId) {
+        super(threadId);
+    }
+
     public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() {
         @Override
         public Rcs1To1Thread createFromParcel(Parcel in) {
@@ -36,6 +40,7 @@
     };
 
     protected Rcs1To1Thread(Parcel in) {
+        super(in);
     }
 
     @Override
@@ -45,5 +50,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(RCS_1_TO_1_TYPE);
+        super.writeToParcel(dest, flags);
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
index 150c4d4..d954b2d 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -36,6 +36,7 @@
     };
 
     protected RcsGroupThread(Parcel in) {
+        super(in);
     }
 
     @Override
@@ -45,5 +46,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(RCS_GROUP_TYPE);
+        super.writeToParcel(dest, flags);
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index 4198c78..1bf6ffd 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -16,6 +16,8 @@
 
 package android.telephony.ims;
 
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.Rlog;
@@ -27,26 +29,93 @@
  * @hide - TODO make this public
  */
 public class RcsMessageStore {
-    private static final String TAG = "RcsMessageStore";
-    private static final boolean VDBG = false;
+    static final String TAG = "RcsMessageStore";
 
     /**
-     * Delete the RcsThread identified by the given threadId.
+     * Returns the first chunk of existing {@link RcsThread}s in the common storage.
+     * @param queryParameters Parameters to specify to return a subset of all RcsThreads.
+     *                        Passing a value of null will return all threads.
+     */
+    @WorkerThread
+    public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.getRcsThreads(queryParameters);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the next chunk of {@link RcsThread}s in the common storage.
+     * @param continuationToken A token to continue the query to get the next chunk. This is
+     *                          obtained through {@link RcsThreadQueryResult#nextChunkToken}.
+     */
+    @WorkerThread
+    public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.getRcsThreadsWithToken(continuationToken);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a new 1 to 1 thread with the given participant and persists it in the storage.
+     */
+    @WorkerThread
+    public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.createRcs1To1Thread(recipient);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Delete the {@link RcsThread} identified by the given threadId.
      * @param threadId threadId of the thread to be deleted.
      */
+    @WorkerThread
     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) {
-
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re);
         }
     }
 
-    private static void logd(String msg) {
-        Rlog.d(TAG, msg);
+    /**
+     * Creates a new participant and persists it in the storage.
+     * @param canonicalAddress The defining address (e.g. phone number) of the participant.
+     */
+    public RcsParticipant createRcsParticipant(String canonicalAddress) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.createRcsParticipant(canonicalAddress);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re);
+        }
+
+        return null;
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index 318dba3..f678ec7 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -15,14 +15,113 @@
  */
 package android.telephony.ims;
 
+import static android.telephony.ims.RcsMessageStore.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+import android.telephony.ims.aidl.IRcs;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
 
 /**
  * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
  * @hide - TODO(sahinc) make this public
  */
 public class RcsParticipant implements Parcelable {
+    // The row ID of this participant in the database
+    private int mId;
+    // The phone number of this participant
+    private String mCanonicalAddress;
+    // The RCS alias of this participant. This is different than the name of the contact in the
+    // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't
+    // require other users to add them as contacts and give them a name.
+    private String mAlias;
+
+    /**
+     * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+     * to create instances of participants. This is not meant to be part of the SDK.
+     *
+     * @hide
+     */
+    public RcsParticipant(int id, @NonNull String canonicalAddress) {
+        mId = id;
+        mCanonicalAddress = canonicalAddress;
+    }
+
+    /**
+     * @return Returns the canonical address (i.e. normalized phone number) for this participant
+     */
+    public String getCanonicalAddress() {
+        return mCanonicalAddress;
+    }
+
+    /**
+     * Sets the canonical address for this participant and updates it in storage.
+     * @param canonicalAddress the canonical address to update to.
+     */
+    @WorkerThread
+    public void setCanonicalAddress(@NonNull String canonicalAddress) {
+        Preconditions.checkNotNull(canonicalAddress);
+        if (canonicalAddress.equals(mCanonicalAddress)) {
+            return;
+        }
+
+        mCanonicalAddress = canonicalAddress;
+
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
+        }
+    }
+
+    /**
+     * @return Returns the alias for this participant. Alias is usually the real name of the person
+     * themselves.
+     */
+    public String getAlias() {
+        return mAlias;
+    }
+
+    /**
+     * Sets the alias for this participant and persists it in storage. Alias is usually the real
+     * name of the person themselves.
+     */
+    @WorkerThread
+    public void setAlias(String alias) {
+        if (TextUtils.equals(mAlias, alias)) {
+            return;
+        }
+        mAlias = alias;
+
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                iRcs.updateRcsParticipantAlias(mId, mAlias);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
+        }
+    }
+
+    /**
+     * Returns the row id of this participant. This is not meant to be part of the SDK
+     *
+     * @hide
+     */
+    public int getId() {
+        return mId;
+    }
+
     public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
         @Override
         public RcsParticipant createFromParcel(Parcel in) {
@@ -36,6 +135,9 @@
     };
 
     protected RcsParticipant(Parcel in) {
+        mId = in.readInt();
+        mCanonicalAddress = in.readString();
+        mAlias = in.readString();
     }
 
     @Override
@@ -45,6 +147,8 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-
+        dest.writeInt(mId);
+        dest.writeString(mCanonicalAddress);
+        dest.writeString(mAlias);
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index 2969ffd..c0a0d94 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -16,7 +16,9 @@
 
 package android.telephony.ims;
 
+import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 /**
  * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
@@ -24,5 +26,50 @@
  * @hide - TODO(sahinc) make this public
  */
 public abstract class RcsThread implements Parcelable {
+    // Since this is an abstract class that gets parcelled, the sub-classes need to write these
+    // magic values into the parcel so that we know which type to unparcel into.
+    protected static final int RCS_1_TO_1_TYPE = 998;
+    protected static final int RCS_GROUP_TYPE = 999;
 
+    protected int mThreadId;
+
+    protected RcsThread(int threadId) {
+        mThreadId = threadId;
+    }
+
+    protected RcsThread(Parcel in) {
+        mThreadId = in.readInt();
+    }
+
+    public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
+        @Override
+        public RcsThread createFromParcel(Parcel in) {
+            int type = in.readInt();
+
+            switch (type) {
+                case RCS_1_TO_1_TYPE:
+                    return new Rcs1To1Thread(in);
+                case RCS_GROUP_TYPE:
+                    return new RcsGroupThread(in);
+                default:
+                    Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type);
+            }
+            return null;
+        }
+
+        @Override
+        public RcsThread[] newArray(int size) {
+            return new RcsThread[0];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mThreadId);
+    }
 }
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
similarity index 80%
copy from media/java/android/media/session/ParcelableVolumeInfo.aidl
copy to telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
index c4250f0..7bcebfa 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
@@ -1,4 +1,6 @@
-/* Copyright 2014, The Android Open Source Project
+/*
+**
+** 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.
@@ -13,6 +15,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.telephony.ims;
 
-parcelable ParcelableVolumeInfo;
+parcelable RcsThreadQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
new file mode 100644
index 0000000..931e93d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.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.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to
+ * break large queries into manageable chunks
+ * @hide - TODO make this public
+ */
+public class RcsThreadQueryContinuationToken implements Parcelable {
+    protected RcsThreadQueryContinuationToken(Parcel in) {
+    }
+
+    public static final Creator<RcsThreadQueryContinuationToken> CREATOR =
+            new Creator<RcsThreadQueryContinuationToken>() {
+                @Override
+                public RcsThreadQueryContinuationToken createFromParcel(Parcel in) {
+                    return new RcsThreadQueryContinuationToken(in);
+                }
+
+                @Override
+                public RcsThreadQueryContinuationToken[] newArray(int size) {
+                    return new RcsThreadQueryContinuationToken[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
similarity index 81%
copy from media/java/android/media/session/ParcelableVolumeInfo.aidl
copy to telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
index c4250f0..feb2d4d 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
@@ -1,4 +1,6 @@
-/* Copyright 2014, The Android Open Source Project
+/*
+**
+** 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.
@@ -13,6 +15,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.telephony.ims;
 
-parcelable ParcelableVolumeInfo;
+parcelable RcsThreadQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
new file mode 100644
index 0000000..f2c4ab1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
@@ -0,0 +1,225 @@
+/*
+ * 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.annotation.CheckResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in
+ * order to select a subset of {@link RcsThread}s present in the message store.
+ * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
+ */
+public class RcsThreadQueryParameters implements Parcelable {
+    private final boolean mIsGroup;
+    private final Set<RcsParticipant> mRcsParticipants;
+    private final int mLimit;
+    private final boolean mIsAscending;
+
+    RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit,
+            boolean isAscending) {
+        mIsGroup = isGroup;
+        mRcsParticipants = participants;
+        mLimit = limit;
+        mIsAscending = isAscending;
+    }
+
+    /**
+     * Returns a new builder to build a query with.
+     * TODO - make public
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the list of participants.
+     * @hide
+     */
+    public Set<RcsParticipant> getRcsParticipants() {
+        return mRcsParticipants;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * whether group threads should be queried
+     * @hide
+     */
+    public boolean isGroupThread() {
+        return mIsGroup;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the number of tuples the result query should be limited to.
+     */
+    public int getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
+     * determine the sort order.
+     */
+    public boolean isAscending() {
+        return mIsAscending;
+    }
+
+    /**
+     * A helper class to build the {@link RcsThreadQueryParameters}.
+     */
+    public static class Builder {
+        private boolean mIsGroupThread;
+        private Set<RcsParticipant> mParticipants;
+        private int mLimit = 100;
+        private boolean mIsAscending;
+
+        /**
+         * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this,
+         * {@link RcsThreadQueryParameters#builder()} needs to be called.
+         */
+        Builder() {
+            mParticipants = new HashSet<>();
+        }
+
+        /**
+         * Limits the query to only return group threads.
+         * @param isGroupThread Whether to limit the query result to group threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder isGroupThread(boolean isGroupThread) {
+            mIsGroupThread = isGroupThread;
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given participant.
+         * @param participant The participant that must be included in all of the returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder withParticipant(RcsParticipant participant) {
+            mParticipants.add(participant);
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given list of participants.
+         * @param participants An iterable list of participants that must be included in all of the
+         *                     returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder withParticipants(Iterable<RcsParticipant> participants) {
+            for (RcsParticipant participant : participants) {
+                mParticipants.add(participant);
+            }
+            return this;
+        }
+
+        /**
+         * Desired number of threads to be returned from the query. Passing in 0 will return all
+         * existing threads at once. The limit defaults to 100.
+         * @param limit The number to limit the query result to.
+         * @return The same instance of the builder to chain parameters.
+         * @throws InvalidParameterException If the given limit is negative.
+         */
+        @CheckResult
+        public Builder limitResultsTo(int limit) throws InvalidParameterException {
+            if (limit < 0) {
+                throw new InvalidParameterException("The query limit must be non-negative");
+            }
+
+            mLimit = limit;
+            return this;
+        }
+
+        /**
+         * Sorts the results returned from the query via thread IDs.
+         *
+         * TODO - add sorting support for other fields
+         *
+         * @param isAscending whether to sort in ascending order or not
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder sort(boolean isAscending) {
+            mIsAscending = isAscending;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RcsThreadQueryParameters} to use in
+         * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)}
+         *
+         * @return An instance of {@link RcsThreadQueryParameters} to use with the thread query.
+         */
+        public RcsThreadQueryParameters build() {
+            return new RcsThreadQueryParameters(
+                    mIsGroupThread, mParticipants, mLimit, mIsAscending);
+        }
+    }
+
+    /**
+     * Parcelable boilerplate below.
+     */
+    protected RcsThreadQueryParameters(Parcel in) {
+        mIsGroup = in.readBoolean();
+
+        ArrayList<RcsParticipant> participantArrayList = new ArrayList<>();
+        in.readTypedList(participantArrayList, RcsParticipant.CREATOR);
+        mRcsParticipants = new HashSet<>(participantArrayList);
+
+        mLimit = in.readInt();
+        mIsAscending = in.readBoolean();
+    }
+
+    public static final Creator<RcsThreadQueryParameters> CREATOR =
+            new Creator<RcsThreadQueryParameters>() {
+                @Override
+                public RcsThreadQueryParameters createFromParcel(Parcel in) {
+                    return new RcsThreadQueryParameters(in);
+                }
+
+                @Override
+                public RcsThreadQueryParameters[] newArray(int size) {
+                    return new RcsThreadQueryParameters[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsGroup);
+        dest.writeTypedList(new ArrayList<>(mRcsParticipants));
+        dest.writeInt(mLimit);
+        dest.writeBoolean(mIsAscending);
+    }
+
+}
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
similarity index 81%
copy from media/java/android/media/session/ParcelableVolumeInfo.aidl
copy to telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
index c4250f0..4b06529 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
@@ -1,4 +1,6 @@
-/* Copyright 2014, The Android Open Source Project
+/*
+**
+** 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.
@@ -13,6 +15,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.telephony.ims;
 
-parcelable ParcelableVolumeInfo;
+parcelable RcsThreadQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
new file mode 100644
index 0000000..47715f8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.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 android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken,
+ * RcsThreadQueryParameters)}
+ * call. This class allows getting the token for querying the next batch of threads in order to
+ * prevent handling large amounts of data at once.
+ *
+ * @hide
+ */
+public class RcsThreadQueryResult implements Parcelable {
+    private RcsThreadQueryContinuationToken mContinuationToken;
+    private List<RcsThread> mRcsThreads;
+
+    /**
+     * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+     * to create query results
+     *
+     * @hide
+     */
+    public RcsThreadQueryResult(
+            RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) {
+        mContinuationToken = continuationToken;
+        mRcsThreads = rcsThreads;
+    }
+
+    /**
+     * Returns a token to call
+     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)}
+     * to get the next batch of {@link RcsThread}s.
+     */
+    public RcsThreadQueryContinuationToken nextChunkToken() {
+        return mContinuationToken;
+    }
+
+    /**
+     * Returns all the RcsThreads in the current query result. Call {@link
+     * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of
+     * {@link RcsThread}s.
+     */
+    public List<RcsThread> getThreads() {
+        return mRcsThreads;
+    }
+
+    protected RcsThreadQueryResult(Parcel in) {
+        // TODO - implement
+    }
+
+    public static final Creator<RcsThreadQueryResult> CREATOR =
+            new Creator<RcsThreadQueryResult>() {
+                @Override
+                public RcsThreadQueryResult createFromParcel(Parcel in) {
+                    return new RcsThreadQueryResult(in);
+                }
+
+                @Override
+                public RcsThreadQueryResult[] newArray(int size) {
+                    return new RcsThreadQueryResult[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // TODO - implement
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index f25b4b1..d0b31e1 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -138,4 +138,10 @@
      * @param rttMessage Received RTT message
      */
     void callSessionRttMessageReceived(in String rttMessage);
+
+    /*
+     * While in call, there has been a change in RTT audio indicator.
+     * @param profile updated ImsStreamMediaProfile
+     */
+    void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
 }
diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
index b2e2fad..0c958ba 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -16,14 +16,34 @@
 
 package android.telephony.ims.aidl;
 
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.Rcs1To1Thread;
+import android.telephony.ims.RcsThreadQueryContinuationToken;
+import android.telephony.ims.RcsThreadQueryParameters;
+import android.telephony.ims.RcsThreadQueryResult;
+
 /**
  * RPC definition between RCS storage APIs and phone process.
  * {@hide}
  */
 interface IRcs {
     // RcsMessageStore APIs
+    RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters);
+
+    RcsThreadQueryResult getRcsThreadsWithToken(
+        in RcsThreadQueryContinuationToken continuationToken);
+
     void deleteThread(int threadId);
 
+    Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant);
+
     // RcsThread APIs
     int getMessageCount(int rcsThreadId);
+
+    // RcsParticipant APIs
+    RcsParticipant createRcsParticipant(String canonicalAddress);
+
+    void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress);
+
+    void updateRcsParticipantAlias(int id, String alias);
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index 23de2fd..bc58e46 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -591,5 +591,11 @@
         public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
             mNewListener.callSessionRttMessageReceived(rttMessage);
         }
+
+        @Override
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile)
+                throws RemoteException {
+            mNewListener.callSessionRttAudioIndicatorChanged(profile);
+        }
     }
 }
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index a8e8b7dd..bbb27af 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -152,4 +152,10 @@
      * @param rttMessage Received RTT message
      */
     void callSessionRttMessageReceived(in String rttMessage);
+
+    /*
+     * While in call, there has been a change in RTT audio indicator.
+     * @param profile updated ImsStreamMediaProfile
+     */
+    void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
 }
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 96f7a1b..6e8d038 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -66,7 +66,8 @@
     public static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9;
     public static final int EVENT_ROAMING_ON = BASE + 11;
     public static final int EVENT_ROAMING_OFF = BASE + 12;
-    public static final int EVENT_ENABLE_NEW_APN = BASE + 13;
+    public static final int EVENT_ENABLE_APN = BASE + 13;
+    public static final int EVENT_DISABLE_APN = BASE + 14;
     public static final int EVENT_DISCONNECT_DONE = BASE + 15;
     public static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
     public static final int EVENT_DATA_STALL_ALARM = BASE + 17;
@@ -86,12 +87,13 @@
     public static final int CMD_NET_STAT_POLL = BASE + 40;
     public static final int EVENT_DATA_RAT_CHANGED = BASE + 41;
     public static final int CMD_CLEAR_PROVISIONING_SPINNER = BASE + 42;
-    public static final int EVENT_REDIRECTION_DETECTED = BASE + 44;
+    public static final int EVENT_NETWORK_STATUS_CHANGED = BASE + 44;
     public static final int EVENT_PCO_DATA_RECEIVED = BASE + 45;
     public static final int EVENT_DATA_ENABLED_CHANGED = BASE + 46;
     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_PROVISIONED_CHANGE = BASE + 50;
 
     /***** Constants *****/
 
diff --git a/telephony/java/com/android/internal/telephony/IAns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl
similarity index 89%
rename from telephony/java/com/android/internal/telephony/IAns.aidl
rename to telephony/java/com/android/internal/telephony/IOns.aidl
index 98bcd41..0e3d12b 100755
--- a/telephony/java/com/android/internal/telephony/IAns.aidl
+++ b/telephony/java/com/android/internal/telephony/IOns.aidl
@@ -18,13 +18,13 @@
 
 import android.telephony.AvailableNetworkInfo;
 
-interface IAns {
+interface IOns {
 
     /**
-    * Enable or disable Alternative Network service.
+    * Enable or disable Opportunistic Network service.
     *
     * This method should be called to enable or disable
-    * AlternativeNetwork service on the device.
+    * OpportunisticNetwork service on the device.
     *
     * <p>
     * Requires Permission:
@@ -38,9 +38,9 @@
     boolean setEnable(boolean enable, String callingPackage);
 
     /**
-     * is Alternative Network service enabled
+     * is Opportunistic Network service enabled
      *
-     * This method should be called to determine if the Alternative Network service is enabled
+     * This method should be called to determine if the Opportunistic Network service is enabled
     *
     * <p>
     * Requires Permission:
@@ -66,7 +66,7 @@
      * @return true if request is accepted, else false.
      *
      */
-    boolean setPreferredData(int subId, String callingPackage);
+    boolean setPreferredDataSubscriptionId(int subId, String callingPackage);
 
     /**
      * Get preferred opportunistic data subscription Id
@@ -78,13 +78,13 @@
      * subscription id
      *
      */
-    int getPreferredData(String callingPackage);
+    int getPreferredDataSubscriptionId(String callingPackage);
 
     /**
      * Update availability of a list of networks in the current location.
      *
      * This api should be called if the caller is aware of the availability of a network
-     * at the current location. This information will be used by AlternativeNetwork service
+     * at the current location. This information will be used by OpportunisticNetwork service
      * to decide to attach to the network. If an empty list is passed,
      * it is assumed that no network is available.
      * Requires that the calling app has carrier privileges on both primary and
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 00cf9c3..3dbebe8 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -17,14 +17,15 @@
 package com.android.internal.telephony;
 
 import android.os.Bundle;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
+import android.telephony.CallAttributes;
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.emergency.EmergencyNumber;
 
 oneway interface IPhoneStateListener {
@@ -54,6 +55,7 @@
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onPreferredDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
+    void onCallAttributesChanged(in CallAttributes callAttributes);
     void onEmergencyNumberListChanged(in Map emergencyNumberList);
     void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index d169b7d..577ddbd 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -200,7 +200,15 @@
      * @hide
      *
      */
-    int setPreferredData(int subId);
+    int setPreferredDataSubscriptionId(int subId);
+
+    /**
+     * Get which subscription is preferred for cellular data.
+     *
+     * @hide
+     *
+     */
+    int getPreferredDataSubscriptionId();
 
     /**
      * 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 c5d82c5..5736a46 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -28,6 +28,7 @@
 import android.service.carrier.CarrierIdentifier;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellInfo;
 import android.telephony.ClientRequestStats;
 import android.telephony.IccOpenLogicalChannelResponse;
@@ -41,6 +42,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyHistogram;
 import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
@@ -1292,22 +1294,27 @@
     List<TelephonyHistogram> getTelephonyHistograms();
 
     /**
-     * Set the allowed carrier list for slotIndex
-     * Require system privileges. In the future we may add this to carrier APIs.
+     * Set the allowed carrier list and the excluded carrier list, indicating the priority between
+     * the two lists.
      *
-     * @return The number of carriers set successfully. Should match length of
-     * carriers on success.
+     * <p>Requires system privileges. In the future we may add this to carrier APIs.
+     *
+     * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success.
+     * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the
+     * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases.
      */
-    int setAllowedCarriers(int slotIndex, in List<CarrierIdentifier> carriers);
+    int setAllowedCarriers(in CarrierRestrictionRules carrierRestrictionRules);
 
     /**
-     * Get the allowed carrier list for slotIndex.
-     * Require system privileges. In the future we may add this to carrier APIs.
+     * Get the allowed carrier list and the excluded carrier list indicating the priority between
+     * the two lists.
      *
-     * @return List of {@link android.service.carrier.CarrierIdentifier}; empty list
-     * means all carriers are allowed.
+     * <p>Requires system privileges. In the future we may add this to carrier APIs.
+     *
+     * @return {@link CarrierRestrictionRules}; empty lists mean all carriers are allowed. It
+     * returns null in case of error.
      */
-    List<CarrierIdentifier> getAllowedCarriers(int slotIndex);
+    CarrierRestrictionRules getAllowedCarriers();
 
    /**
      * Returns carrier id of the given subscription.
@@ -1776,4 +1783,19 @@
      * Set the String provisioning value for the provisioning key specified.
      */
     int setImsProvisioningString(int subId, int key, String value);
+
+    /**
+     * Update Emergency Number List for Test Mode.
+     */
+    void updateEmergencyNumberListTestMode(int action, in EmergencyNumber num);
+
+    /**
+     * Get the full emergency number list for Test Mode.
+     */
+    List<String> getEmergencyNumberListTestMode();
+
+    /**
+     * Enable or disable a logical modem stack associated with the slotIndex.
+     */
+    boolean enableModemForSlot(int slotIndex, boolean enable);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index d9f5c3f..5632c63 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -50,13 +50,13 @@
     void notifyDataActivity(int state);
     void notifyDataActivityForSubscriber(in int subId, int state);
     void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String apnType, in LinkProperties linkProperties,
+            String apn, String apnType, in LinkProperties linkProperties,
             in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
     void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String apnType, in LinkProperties linkProperties,
+            String apn, String apnType, in LinkProperties linkProperties,
             in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
-    void notifyDataConnectionFailed(String reason, String apnType);
-    void notifyDataConnectionFailedForSubscriber(int subId, String reason, String apnType);
+    void notifyDataConnectionFailed(String apnType);
+    void notifyDataConnectionFailedForSubscriber(int subId, String apnType);
     void notifyCellLocation(in Bundle cellLocation);
     void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation);
     void notifyOtaspChanged(in int otaspMode);
@@ -67,8 +67,8 @@
     void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
             int backgroundCallState);
     void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause);
-    void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn,
-            String failCause);
+    void notifyPreciseDataConnectionFailed(String apnType, String apn,
+            int failCause);
     void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
     void notifySrvccStateChanged(in int subId, in int lteState);
     void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 21f3b92..e87d28c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -79,8 +79,6 @@
     public static final int SIM_ACTIVATION_TYPE_DATA = 1;
 
     public static final String PHONE_NAME_KEY = "phoneName";
-    public static final String FAILURE_REASON_KEY = "reason";
-    public static final String STATE_CHANGE_REASON_KEY = "reason";
     public static final String DATA_NETWORK_TYPE_KEY = "networkType";
     public static final String DATA_FAILURE_CAUSE_KEY = "failCause";
     public static final String DATA_APN_TYPE_KEY = "apnType";
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 1c103a9..9300034 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -489,6 +489,7 @@
     int RIL_REQUEST_STOP_NETWORK_SCAN = 143;
     int RIL_REQUEST_START_KEEPALIVE = 144;
     int RIL_REQUEST_STOP_KEEPALIVE = 145;
+    int RIL_REQUEST_ENABLE_MODEM = 146;
 
     /* The following requests are not defined in RIL.h */
     int RIL_REQUEST_HAL_NON_RIL_BASE = 200;
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 9874f80..a508b06 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -18,6 +18,8 @@
 
 import android.Manifest.permission;
 import android.app.AppOpsManager;
+import android.app.role.RoleManager;
+import android.app.role.RoleManagerCallback;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -29,13 +31,12 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Debug;
 import android.os.Process;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.Rlog;
@@ -50,6 +51,10 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Class for managing the primary application that we will deliver SMS/MMS messages to
@@ -67,6 +72,7 @@
     private static final String SCHEME_SMSTO = "smsto";
     private static final String SCHEME_MMS = "mms";
     private static final String SCHEME_MMSTO = "mmsto";
+    private static final boolean DEBUG = false;
     private static final boolean DEBUG_MULTIUSER = false;
 
     private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
@@ -240,7 +246,11 @@
 
         // Get the list of apps registered for SMS
         Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
-        List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, 0,
+        if (DEBUG) {
+            intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
+        }
+        List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                 userId);
 
         HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
@@ -266,7 +276,8 @@
         // Update any existing entries with mms receiver class
         intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
         intent.setDataAndType(null, "application/vnd.wap.mms-message");
-        List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, 0,
+        List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                 userId);
         for (ResolveInfo resolveInfo : mmsReceivers) {
             final ActivityInfo activityInfo = resolveInfo.activityInfo;
@@ -286,7 +297,8 @@
         // Update any existing entries with respond via message intent class.
         intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE,
                 Uri.fromParts(SCHEME_SMSTO, "", null));
-        List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent, 0,
+        List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                 userId);
         for (ResolveInfo resolveInfo : respondServices) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
@@ -306,7 +318,8 @@
         // Update any existing entries with supports send to.
         intent = new Intent(Intent.ACTION_SENDTO,
                 Uri.fromParts(SCHEME_SMSTO, "", null));
-        List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent, 0,
+        List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                 userId);
         for (ResolveInfo resolveInfo : sendToActivities) {
             final ActivityInfo activityInfo = resolveInfo.activityInfo;
@@ -323,7 +336,9 @@
         // Update any existing entries with the default sms changed handler.
         intent = new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
         List<ResolveInfo> smsAppChangedReceivers =
-                packageManager.queryBroadcastReceiversAsUser(intent, 0, userId);
+                packageManager.queryBroadcastReceiversAsUser(intent,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
                     smsAppChangedReceivers);
@@ -348,7 +363,9 @@
         // Update any existing entries with the external provider changed handler.
         intent = new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE);
         List<ResolveInfo> providerChangedReceivers =
-                packageManager.queryBroadcastReceiversAsUser(intent, 0, userId);
+                packageManager.queryBroadcastReceiversAsUser(intent,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
                     providerChangedReceivers);
@@ -373,7 +390,9 @@
         // Update any existing entries with the sim full handler.
         intent = new Intent(Intents.SIM_FULL_ACTION);
         List<ResolveInfo> simFullReceivers =
-                packageManager.queryBroadcastReceiversAsUser(intent, 0, userId);
+                packageManager.queryBroadcastReceiversAsUser(intent,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers="
                     + simFullReceivers);
@@ -406,7 +425,8 @@
             if (smsApplicationData != null) {
                 if (!smsApplicationData.isComplete()) {
                     Log.w(LOG_TAG, "Package " + packageName
-                            + " lacks required manifest declarations to be a default sms app");
+                            + " lacks required manifest declarations to be a default sms app: "
+                            + smsApplicationData);
                     receivers.remove(packageName);
                 }
             }
@@ -418,7 +438,7 @@
      * Checks to see if we have a valid installed SMS application for the specified package name
      * @return Data for the specified package name or null if there isn't one
      */
-    private static SmsApplicationData getApplicationForPackage(
+    public static SmsApplicationData getApplicationForPackage(
             Collection<SmsApplicationData> applications, String packageName) {
         if (packageName == null) {
             return null;
@@ -456,8 +476,7 @@
             Log.i(LOG_TAG, "getApplication userId=" + userId);
         }
         // Determine which application receives the broadcast
-        String defaultApplication = Settings.Secure.getStringForUser(context.getContentResolver(),
-                Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
+        String defaultApplication = getDefaultSmsPackage(context, userId);
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplication defaultApp=" + defaultApplication);
         }
@@ -469,27 +488,6 @@
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplication appData=" + applicationData);
         }
-        // Picking a new SMS app requires AppOps and Settings.Secure permissions, so we only do
-        // this if the caller asked us to.
-        if (updateIfNeeded && applicationData == null) {
-            // Try to find the default SMS package for this device
-            Resources r = context.getResources();
-            String defaultPackage =
-                    r.getString(com.android.internal.R.string.default_sms_application);
-            applicationData = getApplicationForPackage(applications, defaultPackage);
-
-            if (applicationData == null) {
-                // Are there any applications?
-                if (applications.size() != 0) {
-                    applicationData = (SmsApplicationData)applications.toArray()[0];
-                }
-            }
-
-            // If we found a new default app, update the setting
-            if (applicationData != null) {
-                setDefaultApplicationInternal(applicationData.mPackageName, context, userId);
-            }
-        }
 
         // If we found a package, make sure AppOps permissions are set up correctly
         if (applicationData != null) {
@@ -513,7 +511,7 @@
                 // current SMS app will already be the preferred activity - but checking whether or
                 // not this is true is just as expensive as reconfiguring the preferred activity so
                 // we just reconfigure every time.
-                updateDefaultSmsApp(context, userId, applicationData);
+                defaultSmsAppChanged(context);
             }
         }
         if (DEBUG_MULTIUSER) {
@@ -522,16 +520,17 @@
         return applicationData;
     }
 
-    private static void updateDefaultSmsApp(Context context, int userId,
-            SmsApplicationData applicationData) {
+    private static String getDefaultSmsPackage(Context context, int userId) {
+        return context.getSystemService(RoleManager.class).getDefaultSmsPackage(userId);
+    }
+
+    /**
+     * Grants various permissions and appops on sms app change
+     */
+    private static void defaultSmsAppChanged(Context context) {
         PackageManager packageManager = context.getPackageManager();
         AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
 
-        // Configure this as the preferred activity for SENDTO sms/mms intents
-        configurePreferredActivity(packageManager, new ComponentName(
-                        applicationData.mPackageName, applicationData.mSendToClass),
-                userId);
-
         // Assign permission to special system apps
         assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
                 PHONE_PACKAGE_NAME);
@@ -603,8 +602,7 @@
         final UserHandle userHandle = UserHandle.of(userId);
 
         // Get old package name
-        String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
-                Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
+        String oldPackageName = getDefaultSmsPackage(context, userId);
 
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldPackageName +
@@ -636,16 +634,29 @@
                 }
             }
 
-            // Update the secure setting.
-            Settings.Secure.putStringForUser(context.getContentResolver(),
-                    Settings.Secure.SMS_DEFAULT_APPLICATION, applicationData.mPackageName,
-                    userId);
+            // Update the setting.
+            CompletableFuture<Void> res = new CompletableFuture<>();
+            context.getSystemService(RoleManager.class).addRoleHolderAsUser(
+                    RoleManager.ROLE_SMS, applicationData.mPackageName, UserHandle.of(userId),
+                    AsyncTask.THREAD_POOL_EXECUTOR, new RoleManagerCallback() {
+                        @Override
+                        public void onSuccess() {
+                            res.complete(null);
+                        }
 
-            // Allow relevant appops for the newly configured default SMS app.
-            setExclusiveAppops(applicationData.mPackageName, appOps, applicationData.mUid,
-                    AppOpsManager.MODE_ALLOWED);
+                        @Override
+                        public void onFailure() {
+                            res.completeExceptionally(new RuntimeException());
+                        }
+                    });
+            try {
+                res.get(5, TimeUnit.SECONDS);
+            } catch (InterruptedException | ExecutionException | TimeoutException e) {
+                Log.e(LOG_TAG, "Exception while adding sms role holder " + applicationData, e);
+                return;
+            }
 
-            updateDefaultSmsApp(context, userId, applicationData);
+            defaultSmsAppChanged(context);
 
             if (DEBUG_MULTIUSER) {
                 Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 2a648bd..8523554 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -501,4 +501,18 @@
      */
     public static final String ACTION_LINE1_NUMBER_ERROR_DETECTED =
             "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED";
+
+    /**
+     * Broadcast action to notify radio bug.
+     *
+     * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * @hide
+     */
+    public static final String ACTION_REPORT_RADIO_BUG =
+            "com.android.internal.telephony.ACTION_REPORT_RADIO_BUG";
+
+    // ACTION_REPORT_RADIO_BUG extra keys
+    public static final String EXTRA_SLOT_ID = "slotId";
+    public static final String EXTRA_RADIO_BUG_TYPE = "radioBugType";
 }
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 870a689..14a36c8 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -24,22 +24,25 @@
 
 /** @hide */
 interface IEuiccController {
-    oneway void continueOperation(in Intent resolutionIntent, in Bundle resolutionExtras);
-    oneway void getDownloadableSubscriptionMetadata(in DownloadableSubscription subscription,
+    oneway void continueOperation(int cardId, in Intent resolutionIntent,
+            in Bundle resolutionExtras);
+    oneway void getDownloadableSubscriptionMetadata(int cardId,
+            in DownloadableSubscription subscription,
         String callingPackage, in PendingIntent callbackIntent);
-    oneway void getDefaultDownloadableSubscriptionList(
+    oneway void getDefaultDownloadableSubscriptionList(int cardId,
         String callingPackage, in PendingIntent callbackIntent);
-    String getEid();
-    int getOtaStatus();
-    oneway void downloadSubscription(in DownloadableSubscription subscription,
-        boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle, in PendingIntent callbackIntent);
-    EuiccInfo getEuiccInfo();
-    oneway void deleteSubscription(int subscriptionId, String callingPackage,
+    String getEid(int cardId);
+    int getOtaStatus(int cardId);
+    oneway void downloadSubscription(int cardId, in DownloadableSubscription subscription,
+        boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle,
         in PendingIntent callbackIntent);
-    oneway void switchToSubscription(int subscriptionId, String callingPackage,
+    EuiccInfo getEuiccInfo(int cardId);
+    oneway void deleteSubscription(int cardId, int subscriptionId, String callingPackage,
         in PendingIntent callbackIntent);
-    oneway void updateSubscriptionNickname(int subscriptionId, String nickname,
+    oneway void switchToSubscription(int cardId, int subscriptionId, String callingPackage,
         in PendingIntent callbackIntent);
-    oneway void eraseSubscriptions(in PendingIntent callbackIntent);
-    oneway void retainSubscriptionsForFactoryReset(in PendingIntent callbackIntent);
-}
\ No newline at end of file
+    oneway void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname,
+        String callingPackage, in PendingIntent callbackIntent);
+    oneway void eraseSubscriptions(int cardId, in PendingIntent callbackIntent);
+    oneway void retainSubscriptionsForFactoryReset(int cardId, in PendingIntent callbackIntent);
+}
diff --git a/test-base/api/current.txt b/test-base/api/current.txt
index 7ebd6aa..91fcca5 100644
--- a/test-base/api/current.txt
+++ b/test-base/api/current.txt
@@ -48,6 +48,9 @@
     method public abstract void startTiming(boolean);
   }
 
+  public abstract deprecated class RepetitiveTest implements java.lang.annotation.Annotation {
+  }
+
   public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation {
   }
 
diff --git a/test-base/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java
index 6a7130e..13e89d2 100644
--- a/test-base/src/android/test/RepetitiveTest.java
+++ b/test-base/src/android/test/RepetitiveTest.java
@@ -26,8 +26,10 @@
  * When the annotation is present, the test method is executed the number of times specified by
  * numIterations and defaults to 1.
  *
- * {@hide} Not needed for public API.
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface RepetitiveTest {
@@ -37,4 +39,4 @@
      * @return The total number of iterations, the default is 1.
      */
     int numIterations() default 1;
-}
\ No newline at end of file
+}
diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp
index 833c714..a69f422 100644
--- a/test-legacy/Android.bp
+++ b/test-legacy/Android.bp
@@ -25,7 +25,7 @@
     static_libs: [
         "android.test.base-minus-junit",
         "android.test.runner-minus-junit",
-        "android.test.mock.impl",
+        "android.test.mock_static",
     ],
 
     no_framework_libs: true,
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 37158e5..43b765d 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -25,7 +25,24 @@
         "android.test.mock",
     ],
 
+    srcs_lib: "framework",
+    srcs_lib_whitelist_dirs: ["core/java"],
     srcs_lib_whitelist_pkgs: ["android"],
-    metalava_enabled: false,
     compile_dex: true,
 }
+
+// Build the android.test.mock_static library
+// ==========================================
+// This is only intended for inclusion in the legacy-android-test.
+// Must not be used elewhere.
+java_library_static {
+    name: "android.test.mock_static",
+
+    java_version: "1.8",
+    srcs: ["src/**/*.java"],
+
+    no_framework_libs: true,
+    libs: [
+        "framework",
+    ],
+}
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 0ac35bc..e9a5ff7 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -119,7 +119,7 @@
 
         @Override
         public IBinder asBinder() {
-            throw new UnsupportedOperationException();
+            return MockContentProvider.this.getIContentProviderBinder();
         }
 
         @Override
@@ -279,6 +279,13 @@
     }
 
     /**
+     * @hide
+     */
+    public IBinder getIContentProviderBinder() {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    /**
      * Like {@link #attachInfo(Context, android.content.pm.ProviderInfo)}, but for use
      * when directly instantiating the provider for testing.
      *
diff --git a/tests/ActivityViewTest/Android.mk b/tests/ActivityViewTest/Android.mk
index 9c80764..9c7ca7e 100644
--- a/tests/ActivityViewTest/Android.mk
+++ b/tests/ActivityViewTest/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := ActivityViewTest
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
index de54cc9..17eb029 100644
--- a/tests/ActivityViewTest/AndroidManifest.xml
+++ b/tests/ActivityViewTest/AndroidManifest.xml
@@ -35,17 +35,20 @@
 
         <activity android:name=".ActivityViewActivity"
                   android:label="AV"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+                  android:windowSoftInputMode="stateHidden|adjustResize">
         </activity>
 
         <activity android:name=".ActivityViewResizeActivity"
                   android:label="AV Resize"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+                  android:windowSoftInputMode="stateHidden|adjustResize">
         </activity>
 
         <activity android:name=".ActivityViewScrollActivity"
                   android:label="AV Scroll"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+                  android:windowSoftInputMode="stateHidden">
         </activity>
 
         <activity android:name=".ActivityViewTestActivity"
@@ -54,5 +57,10 @@
                   android:exported="true"
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
         </activity>
+
+        <activity android:name=".ActivityViewVisibilityActivity"
+                  android:label="AV Visibility"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+        </activity>
     </application>
 </manifest>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
index ba2e911..efcaef6 100644
--- a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
+++ b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
@@ -41,4 +41,10 @@
         android:text="Test Resize ActivityView"
         android:textAllCaps="false"/>
 
+    <Button
+        android:id="@+id/visibility_activity_view_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Test ActivityView Visibility"
+        android:textAllCaps="false"/>
 </LinearLayout>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
index f7ec562..338d68a 100644
--- a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
+++ b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
@@ -15,6 +15,7 @@
 -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:id="@+id/test_activity_root"
                 android:orientation="vertical"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
@@ -60,11 +61,10 @@
         android:background="#00000000"
         android:gravity="center" />
 
-    <View
-        android:id="@+id/touch_intercept_view"
+    <EditText
+        android:id="@+id/test_activity_edittext"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="#00000000"
-    />
-
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_margin="16dp" />
 </RelativeLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml
new file mode 100644
index 0000000..d29d4df
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:background="#cfd8dc">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/activity_launch_button"
+            android:layout_width="200dp"
+            android:layout_height="wrap_content"
+            android:text="Launch test activity" />
+
+        <Spinner
+            android:id="@+id/visibility_spinner"
+            android:layout_width="200dp"
+            android:layout_height="match_parent"/>
+
+    </LinearLayout>
+
+    <ActivityView
+        android:id="@+id/activity_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</LinearLayout>
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
index 66f0c6a..4f09c28 100644
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
@@ -35,5 +35,8 @@
 
         findViewById(R.id.resize_activity_view_button).setOnClickListener(
                 v -> startActivity(new Intent(this, ActivityViewResizeActivity.class)));
+
+        findViewById(R.id.visibility_activity_view_button).setOnClickListener(
+                v -> startActivity(new Intent(this, ActivityViewVisibilityActivity.class)));
     }
 }
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
index 0d62786..52aba2b 100644
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
@@ -24,31 +24,31 @@
 import android.app.Activity;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.widget.TextView;
 
-public class ActivityViewTestActivity extends Activity implements View.OnTouchListener {
+public class ActivityViewTestActivity extends Activity {
+    private static final String TAG = "ActivityViewTestActivity";
 
+    private View mRoot;
     private TextView mTextView;
     private TextView mWidthTextView;
     private TextView mHeightTextView;
     private TextView mTouchStateTextView;
-    private View mTouchInterceptView;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_view_test_activity);
-
+        mRoot = findViewById(R.id.test_activity_root);
         mTextView = findViewById(R.id.test_activity_title);
         mWidthTextView = findViewById(R.id.test_activity_width_text);
         mHeightTextView = findViewById(R.id.test_activity_height_text);
         mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
-        mTouchInterceptView = findViewById(R.id.touch_intercept_view);
-        mTouchInterceptView.setOnTouchListener(this);
-        ViewTreeObserver viewTreeObserver = mTouchInterceptView.getViewTreeObserver();
+        ViewTreeObserver viewTreeObserver = mRoot.getViewTreeObserver();
         if (viewTreeObserver.isAlive()) {
             viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
         }
@@ -86,12 +86,13 @@
     }
 
     private void updateStateText(String state) {
+        Log.d(TAG, state);
         mTextView.setText(state);
     }
 
     private void updateDimensionTexts() {
-        mWidthTextView.setText("" + mTouchInterceptView.getWidth());
-        mHeightTextView.setText("" + mTouchInterceptView.getHeight());
+        mWidthTextView.setText("" + mRoot.getWidth());
+        mHeightTextView.setText("" + mRoot.getHeight());
     }
 
     private void updateTouchState(MotionEvent event) {
@@ -108,8 +109,8 @@
     }
 
     @Override
-    public boolean onTouch(View v, MotionEvent event) {
+    public boolean dispatchTouchEvent(MotionEvent event) {
         updateTouchState(event);
-        return true;
+        return super.dispatchTouchEvent(event);
     }
 }
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java
new file mode 100644
index 0000000..ecd2cf3
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2019 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.google.android.test.activityview;
+
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+
+public class ActivityViewVisibilityActivity extends Activity {
+    private static final String[] sVisibilityOptions = {"VISIBLE", "INVISIBLE", "GONE"};
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_visibility_activity);
+
+        final ActivityView activityView = findViewById(R.id.activity_view);
+        final Button launchButton = findViewById(R.id.activity_launch_button);
+        launchButton.setOnClickListener(v -> {
+            final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            activityView.startActivity(intent);
+        });
+
+        final Spinner visibilitySpinner = findViewById(R.id.visibility_spinner);
+        final ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+                android.R.layout.simple_spinner_item, sVisibilityOptions);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        visibilitySpinner.setAdapter(adapter);
+        visibilitySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                switch (position) {
+                    case 0:
+                        activityView.setVisibility(VISIBLE);
+                        break;
+                    case 1:
+                        activityView.setVisibility(INVISIBLE);
+                        break;
+                    case 2:
+                        activityView.setVisibility(GONE);
+                        break;
+                }
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+            }
+        });
+    }
+}
diff --git a/tests/PackageWatchdog/Android.mk b/tests/PackageWatchdog/Android.mk
new file mode 100644
index 0000000..1c1c2a4
--- /dev/null
+++ b/tests/PackageWatchdog/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2019 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# PackageWatchdogTest
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := PackageWatchdogTest
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    junit \
+    frameworks-base-testutils \
+    android-support-test \
+    services.core
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner
+
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/PackageWatchdog/AndroidManifest.xml b/tests/PackageWatchdog/AndroidManifest.xml
new file mode 100644
index 0000000..fa89528
--- /dev/null
+++ b/tests/PackageWatchdog/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.packagewatchdog" >
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.tests.packagewatchdog"
+                     android:label="PackageWatchdog Test"/>
+</manifest>
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
new file mode 100644
index 0000000..ec07037
--- /dev/null
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.server.PackageWatchdog.TRIGGER_FAILURE_COUNT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.test.TestLooper;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+// TODO(zezeozue): Write test without using PackageWatchdog#getPackages. Just rely on
+// behavior of observers receiving crash notifications or not to determine if it's registered
+/**
+ * Test PackageWatchdog.
+ */
+public class PackageWatchdogTest {
+    private static final String APP_A = "com.package.a";
+    private static final String APP_B = "com.package.b";
+    private static final String OBSERVER_NAME_1 = "observer1";
+    private static final String OBSERVER_NAME_2 = "observer2";
+    private static final String OBSERVER_NAME_3 = "observer3";
+    private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(1);
+    private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(5);
+    private TestLooper mTestLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        mTestLooper = new TestLooper();
+        mTestLooper.startAutoDispatch();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        new File(InstrumentationRegistry.getContext().getFilesDir(),
+                "package-watchdog.xml").delete();
+    }
+
+    /**
+     * Test registration, unregistration, package expiry and duration reduction
+     */
+    @Test
+    public void testRegistration() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+        TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+        TestObserver observer3 = new TestObserver(OBSERVER_NAME_3);
+
+        // Start observing for observer1 which will be unregistered
+        watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+        // Start observing for observer2 which will expire
+        watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+        // Start observing for observer3 which will have expiry duration reduced
+        watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), LONG_DURATION);
+
+        // Verify packages observed at start
+        // 1
+        assertEquals(1, watchdog.getPackages(observer1).size());
+        assertTrue(watchdog.getPackages(observer1).contains(APP_A));
+        // 2
+        assertEquals(2, watchdog.getPackages(observer2).size());
+        assertTrue(watchdog.getPackages(observer2).contains(APP_A));
+        assertTrue(watchdog.getPackages(observer2).contains(APP_B));
+        // 3
+        assertEquals(1, watchdog.getPackages(observer3).size());
+        assertTrue(watchdog.getPackages(observer3).contains(APP_A));
+
+        // Then unregister observer1
+        watchdog.unregisterHealthObserver(observer1);
+
+        // Verify observer2 and observer3 left
+        // 1
+        assertNull(watchdog.getPackages(observer1));
+        // 2
+        assertEquals(2, watchdog.getPackages(observer2).size());
+        assertTrue(watchdog.getPackages(observer2).contains(APP_A));
+        assertTrue(watchdog.getPackages(observer2).contains(APP_B));
+        // 3
+        assertEquals(1, watchdog.getPackages(observer3).size());
+        assertTrue(watchdog.getPackages(observer3).contains(APP_A));
+
+        // Then advance time a little and run messages in Handlers so observer2 expires
+        Thread.sleep(SHORT_DURATION);
+        mTestLooper.dispatchAll();
+
+        // Verify observer3 left with reduced expiry duration
+        // 1
+        assertNull(watchdog.getPackages(observer1));
+        // 2
+        assertNull(watchdog.getPackages(observer2));
+        // 3
+        assertEquals(1, watchdog.getPackages(observer3).size());
+        assertTrue(watchdog.getPackages(observer3).contains(APP_A));
+
+        // Then advance time some more and run messages in Handlers so observer3 expires
+        Thread.sleep(LONG_DURATION);
+        mTestLooper.dispatchAll();
+
+        // Verify observer3 expired
+        // 1
+        assertNull(watchdog.getPackages(observer1));
+        // 2
+        assertNull(watchdog.getPackages(observer2));
+        // 3
+        assertNull(watchdog.getPackages(observer3));
+    }
+
+    /**
+     * Test package observers are persisted and loaded on startup
+     */
+    @Test
+    public void testPersistence() throws Exception {
+        PackageWatchdog watchdog1 = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+        TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+        watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+
+        // Verify 2 observers are registered and saved internally
+        // 1
+        assertEquals(1, watchdog1.getPackages(observer1).size());
+        assertTrue(watchdog1.getPackages(observer1).contains(APP_A));
+        // 2
+        assertEquals(2, watchdog1.getPackages(observer2).size());
+        assertTrue(watchdog1.getPackages(observer2).contains(APP_A));
+        assertTrue(watchdog1.getPackages(observer2).contains(APP_B));
+
+
+        // Then advance time and run IO Handler so file is saved
+        mTestLooper.dispatchAll();
+
+        // Then start a new watchdog
+        PackageWatchdog watchdog2 = createWatchdog();
+
+        // Verify the new watchdog loads observers on startup but nothing registered
+        assertEquals(0, watchdog2.getPackages(observer1).size());
+        assertEquals(0, watchdog2.getPackages(observer2).size());
+        // Verify random observer not saved returns null
+        assertNull(watchdog2.getPackages(new TestObserver(OBSERVER_NAME_3)));
+
+        // Then regiser observer1
+        watchdog2.registerHealthObserver(observer1);
+        watchdog2.registerHealthObserver(observer2);
+
+        // Verify 2 observers are registered after reload
+        // 1
+        assertEquals(1, watchdog1.getPackages(observer1).size());
+        assertTrue(watchdog1.getPackages(observer1).contains(APP_A));
+        // 2
+        assertEquals(2, watchdog1.getPackages(observer2).size());
+        assertTrue(watchdog1.getPackages(observer2).contains(APP_A));
+        assertTrue(watchdog1.getPackages(observer2).contains(APP_B));
+    }
+
+    /**
+     * Test package failure under threshold does not notify observers
+     */
+    @Test
+    public void testNoPackageFailureBeforeThreshold() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+        TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+        watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+
+        // Then fail APP_A below the threshold
+        for (int i = 0; i < TRIGGER_FAILURE_COUNT - 1; i++) {
+            watchdog.onPackageFailure(new String[]{APP_A});
+        }
+
+        // Verify that observers are not notified
+        assertEquals(0, observer1.mFailedPackages.size());
+        assertEquals(0, observer2.mFailedPackages.size());
+    }
+
+    /**
+     * Test package failure and notifies all observer since none handles the failure
+     */
+    @Test
+    public void testPackageFailureNotifyAll() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+        TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+        // Start observing for observer1 and observer2 without handling failures
+        watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog.startObservingHealth(observer1, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+
+        // Then fail APP_A and APP_B above the threshold
+        for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+            watchdog.onPackageFailure(new String[]{APP_A, APP_B});
+        }
+
+        // Verify all observers are notifed of all package failures
+        List<String> observer1Packages = observer1.mFailedPackages;
+        List<String> observer2Packages = observer2.mFailedPackages;
+        assertEquals(2, observer1Packages.size());
+        assertEquals(1, observer2Packages.size());
+        assertEquals(APP_A, observer1Packages.get(0));
+        assertEquals(APP_B, observer1Packages.get(1));
+        assertEquals(APP_A, observer2Packages.get(0));
+    }
+
+    /**
+     * Test package failure and notifies only one observer because it handles the failure
+     */
+    @Test
+    public void testPackageFailureNotifyOne() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, true /* shouldHandle */);
+        TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, true /* shouldHandle */);
+
+        // Start observing for observer1 and observer2 with failure handling
+        watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+
+        // Then fail APP_A above the threshold
+        for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+            watchdog.onPackageFailure(new String[]{APP_A});
+        }
+
+        // Verify only one observer is notifed
+        assertEquals(1, observer1.mFailedPackages.size());
+        assertEquals(APP_A, observer1.mFailedPackages.get(0));
+        assertEquals(0, observer2.mFailedPackages.size());
+    }
+
+    private PackageWatchdog createWatchdog() {
+        return new PackageWatchdog(InstrumentationRegistry.getContext(),
+                mTestLooper.getLooper());
+    }
+
+    private static class TestObserver implements PackageHealthObserver {
+        private final String mName;
+        private boolean mShouldHandle;
+        final List<String> mFailedPackages = new ArrayList<>();
+
+        TestObserver(String name) {
+            mName = name;
+        }
+
+        TestObserver(String name, boolean shouldHandle) {
+            mName = name;
+            mShouldHandle = shouldHandle;
+        }
+
+        public boolean onHealthCheckFailed(String packageName) {
+            mFailedPackages.add(packageName);
+            return mShouldHandle;
+        }
+
+        public String getName() {
+            return mName;
+        }
+    }
+}
diff --git a/tests/RcsTests/Android.mk b/tests/RcsTests/Android.mk
index adc7cab..7b348d7 100644
--- a/tests/RcsTests/Android.mk
+++ b/tests/RcsTests/Android.mk
@@ -11,7 +11,7 @@
 
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4 truth-prebuilt
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
similarity index 97%
rename from tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
rename to tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
index 290e04c..44277ed 100644
--- a/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.tests.rcs;
+package com.android.tests.ims;
 
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsMessageStore;
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
new file mode 100644
index 0000000..c402dbf
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantTest {
+    private static final int ID = 123;
+    private static final String ALIAS = "alias";
+    private static final String CANONICAL_ADDRESS = "+1234567890";
+
+    @Test
+    public void testCanUnparcel() {
+        RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS);
+        rcsParticipant.setAlias(ALIAS);
+
+        Bundle bundle = new Bundle();
+        bundle.putParcelable("Some key", rcsParticipant);
+        rcsParticipant = bundle.getParcelable("Some key");
+
+        assertThat(rcsParticipant.getId()).isEqualTo(ID);
+        assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS);
+        assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS);
+    }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
new file mode 100644
index 0000000..a890a38
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsThreadQueryParameters;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsThreadQueryParametersTest {
+    private RcsThreadQueryParameters mRcsThreadQueryParameters;
+    @Mock RcsParticipant mMockParticipant;
+
+    @Test
+    public void testUnparceling() {
+        String key = "some key";
+        mRcsThreadQueryParameters = RcsThreadQueryParameters.builder()
+                .isGroupThread(true)
+                .withParticipant(mMockParticipant)
+                .limitResultsTo(50)
+                .sort(true)
+                .build();
+
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(key, mRcsThreadQueryParameters);
+        mRcsThreadQueryParameters = bundle.getParcelable(key);
+
+        assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue();
+        assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant);
+        assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50);
+        assertThat(mRcsThreadQueryParameters.isAscending()).isTrue();
+    }
+}
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
new file mode 100644
index 0000000..c1d95ac
--- /dev/null
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "RollbackTest"
+    }
+  ]
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 0ccfb19..c2e735e 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -108,6 +108,10 @@
             }
 
             // The app should not be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is uninstalled and when the previously
+            // available rollback, if any, is removed.
+            Thread.sleep(1000);
             assertNull(rm.getAvailableRollback(TEST_APP_A));
             assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
 
@@ -125,6 +129,10 @@
             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
             // The app should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
             RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
             assertNotNull(rollback);
@@ -170,10 +178,10 @@
     }
 
     /**
-     * Test that rollback data is properly persisted.
+     * Test that multiple available rollbacks are properly persisted.
      */
     @Test
-    public void testRollbackDataPersistence() throws Exception {
+    public void testAvailableRollbackPersistence() throws Exception {
         try {
             RollbackTestUtils.adoptShellPermissionIdentity(
                     Manifest.permission.INSTALL_PACKAGES,
@@ -182,30 +190,157 @@
 
             RollbackManager rm = RollbackTestUtils.getRollbackManager();
 
-            // Prep installation of TEST_APP_A
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            RollbackTestUtils.uninstall(TEST_APP_B);
+            RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // Both test apps should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
+
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollbackA);
+            assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
+            assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
+
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
+            RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
+            assertNotNull(rollbackB);
+            assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
+            assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
+
+            // Reload the persisted data.
+            rm.reloadPersistedData();
+
+            // The apps should still be available for rollback.
+            rollbackA = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollbackA);
+            assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
+            assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
+
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
+            rollbackB = rm.getAvailableRollback(TEST_APP_B);
+            assertNotNull(rollbackB);
+            assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
+            assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
+
+            // Rollback of B should not rollback A
+            RollbackTestUtils.rollback(rollbackB);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test that available multi-package rollbacks are properly persisted.
+     */
+    @Test
+    public void testAvailableMultiPackageRollbackPersistence() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.uninstall(TEST_APP_B);
+            RollbackTestUtils.installMultiPackage(false,
+                    "RollbackTestAppAv1.apk",
+                    "RollbackTestAppBv1.apk");
+            RollbackTestUtils.installMultiPackage(true,
+                    "RollbackTestAppAv2.apk",
+                    "RollbackTestAppBv2.apk");
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // The app should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
+
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollbackA);
+            assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
+            assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
+
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
+            RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
+            assertNotNull(rollbackB);
+            assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
+            assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
+
+            // Reload the persisted data.
+            rm.reloadPersistedData();
+
+            // The apps should still be available for rollback.
+            rollbackA = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollbackA);
+            assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
+            assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
+
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
+            rollbackB = rm.getAvailableRollback(TEST_APP_B);
+            assertNotNull(rollbackB);
+            assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
+            assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
+
+            // Rollback of B should rollback A as well
+            RollbackTestUtils.rollback(rollbackB);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test that recently executed rollback data is properly persisted.
+     */
+    @Test
+    public void testRecentlyExecutedRollbackPersistence() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
             RollbackTestUtils.uninstall(TEST_APP_A);
             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
             // The app should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
             RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
-            assertNotNull(rollback);
-            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
-            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
-
-            // Reload the persisted data.
-            rm.reloadPersistedData();
-
-            // The app should still be available for rollback.
-            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
-            rollback = rm.getAvailableRollback(TEST_APP_A);
-            assertNotNull(rollback);
-            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
-            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
 
             // Roll back the app.
             RollbackTestUtils.rollback(rollback);
@@ -263,6 +398,10 @@
             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
             // The app should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
             RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
             assertNotNull(rollback);
@@ -405,6 +544,10 @@
 
             // Both test apps should now be available for rollback, and the
             // targetPackage returned for rollback should be correct.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
             assertNotNull(rollbackA);
             assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
@@ -491,7 +634,7 @@
      * TODO: Stop ignoring this test once support for multi-package rollback
      * is implemented.
      */
-    @Ignore @Test
+    @Test
     public void testMultiPackage() throws Exception {
         try {
             RollbackTestUtils.adoptShellPermissionIdentity(
diff --git a/tests/UsageReportingTest/Android.mk b/tests/UsageReportingTest/Android.mk
new file mode 100644
index 0000000..afb6e16b1
--- /dev/null
+++ b/tests/UsageReportingTest/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := UsageReportingTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsageReportingTest/AndroidManifest.xml b/tests/UsageReportingTest/AndroidManifest.xml
new file mode 100644
index 0000000..be0b09e
--- /dev/null
+++ b/tests/UsageReportingTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Note: Add android:sharedUserId="android.uid.system" to the root element to simulate the system UID
+  caller case.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.usagereporter"
+    >
+
+    <application android:label="@string/reporter_app">
+        <activity android:name="UsageReporterActivity"
+                  android:label="UsageReporter">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/tests/UsageReportingTest/res/layout/row_item.xml b/tests/UsageReportingTest/res/layout/row_item.xml
new file mode 100644
index 0000000..1eb2dab
--- /dev/null
+++ b/tests/UsageReportingTest/res/layout/row_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="16dp"
+    android:paddingRight="16dp"
+    android:background="@color/inactive_color">
+
+    <TextView android:id="@+id/token"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textStyle="bold"/>
+
+    <Button android:id="@+id/start" style="@style/ActionButton"
+            android:text="@string/start" />
+
+    <Button android:id="@+id/stop" style="@style/ActionButton"
+            android:text="@string/stop" />
+</LinearLayout>
diff --git a/tests/UsageReportingTest/res/menu/main.xml b/tests/UsageReportingTest/res/menu/main.xml
new file mode 100644
index 0000000..9847c2d
--- /dev/null
+++ b/tests/UsageReportingTest/res/menu/main.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/add_token"
+          android:title="@string/add_token"/>
+    <item android:id="@+id/add_many_tokens"
+          android:title="@string/add_many_tokens"/>
+    <item android:id="@+id/stop_all"
+          android:title="@string/stop_all_tokens"/>
+    <group android:checkableBehavior="all">
+        <item android:id="@+id/restore_on_start"
+              android:title="@string/restore_tokens_on_start"/>
+    </group>
+</menu>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/colors.xml b/tests/UsageReportingTest/res/values/colors.xml
new file mode 100644
index 0000000..03bcf8a
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <color name="active_color">#FFF</color>
+    <color name="inactive_color">#AAA</color>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/strings.xml b/tests/UsageReportingTest/res/values/strings.xml
new file mode 100644
index 0000000..015290e
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/strings.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Do not translate -->
+    <string name="reporter_app">Usage Reporter App</string>
+    <!-- Do not translate -->
+    <string name="start">Start</string>
+    <!-- Do not translate -->
+    <string name="stop">Stop</string>
+    <!-- Do not translate -->
+    <string name="default_token">SuperSecretToken</string>
+
+    <!-- Do not translate -->
+    <string name="add_token">Add Token</string>
+    <!-- Do not translate -->
+    <string name="add_many_tokens">Add Many Tokens</string>
+    <!-- Do not translate -->
+    <string name="stop_all_tokens">Stop All</string>
+    <!-- Do not translate -->
+    <string name="restore_tokens_on_start">Readd Tokens on Start</string>
+
+
+    <!-- Do not translate -->
+    <string name="token_query">Enter token(s) (delimit tokens with commas)</string>
+    <!-- Do not translate -->
+    <string name="many_tokens_query">Generate how many tokens?</string>
+    <!-- Do not translate -->
+    <string name="stop_all_tokens_query">Stop all tokens?</string>
+    <!-- Do not translate -->
+    <string name="ok">OK</string>
+    <!-- Do not translate -->
+    <string name="cancel">Cancel</string>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/styles.xml b/tests/UsageReportingTest/res/values/styles.xml
new file mode 100644
index 0000000..e5b86c5
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/styles.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <style name="ActionButton">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textAppearance">@style/TextAppearance.ActionButton</item>
+    </style>
+
+    <style name="TextAppearance" parent="android:TextAppearance">
+    </style>
+
+    <style name="TextAppearance.ActionButton">
+        <item name="android:textStyle">italic</item>
+    </style>
+
+</resources>
diff --git a/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
new file mode 100644
index 0000000..946be8f
--- /dev/null
+++ b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
@@ -0,0 +1,320 @@
+/*
+ * 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.tests.usagereporter;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class UsageReporterActivity extends ListActivity {
+
+    private Activity mActivity;
+    private final ArrayList<String> mTokens = new ArrayList();
+    private final ArraySet<String> mActives = new ArraySet();
+    private UsageStatsManager mUsageStatsManager;
+    private Adapter mAdapter;
+    private boolean mRestoreOnStart = false;
+    private static Context sContext;
+
+    /** Called with the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        sContext = getApplicationContext();
+
+        mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+
+        mAdapter = new Adapter();
+        setListAdapter(mAdapter);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mActivity = this;
+
+
+        if (mRestoreOnStart) {
+            ArrayList<String> removed = null;
+            for (String token : mActives) {
+                try {
+                    mUsageStatsManager.reportUsageStart(mActivity, token);
+                } catch (Exception e) {
+                    // Somthing went wrong, recover and move on
+                    if (removed == null) {
+                        removed = new ArrayList();
+                    }
+                    removed.add(token);
+                }
+            }
+            if (removed != null) {
+                for (String token : removed) {
+                    mActives.remove(token);
+                }
+            }
+        } else {
+            mActives.clear();
+        }
+    }
+
+    /**
+     * Called when the activity is about to start interacting with the user.
+     */
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAdapter.notifyDataSetChanged();
+    }
+
+
+    /**
+     * Called when your activity's options menu needs to be created.
+     */
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.main, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+
+    /**
+     * Called when a menu item is selected.
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.add_token:
+                callAddToken();
+                return true;
+            case R.id.add_many_tokens:
+                callAddManyTokens();
+                return true;
+            case R.id.stop_all:
+                callStopAll();
+                return true;
+            case R.id.restore_on_start:
+                mRestoreOnStart = !mRestoreOnStart;
+                item.setChecked(mRestoreOnStart);
+                return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void callAddToken() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.token_query));
+        final EditText input = new EditText(this);
+        input.setInputType(InputType.TYPE_CLASS_TEXT);
+        input.setHint(getString(R.string.default_token));
+        builder.setView(input);
+
+        builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                String tokenNames = input.getText().toString().trim();
+                if (TextUtils.isEmpty(tokenNames)) {
+                    tokenNames = getString(R.string.default_token);
+                }
+                String[] tokens = tokenNames.split(",");
+                for (String token : tokens) {
+                    if (mTokens.contains(token)) continue;
+                    mTokens.add(token);
+                }
+                mAdapter.notifyDataSetChanged();
+
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                                  new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+
+        builder.show();
+    }
+
+    private void callAddManyTokens() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.many_tokens_query));
+        final EditText input = new EditText(this);
+        input.setInputType(InputType.TYPE_CLASS_NUMBER);
+        builder.setView(input);
+
+        builder.setPositiveButton(getString(R.string.ok),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                String val = input.getText().toString().trim();
+                if (TextUtils.isEmpty(val)) return;
+                int n = Integer.parseInt(val);
+                for (int i = 0; i < n; i++) {
+                    final String token = getString(R.string.default_token) + i;
+                    if (mTokens.contains(token)) continue;
+                    mTokens.add(token);
+                }
+                mAdapter.notifyDataSetChanged();
+
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+
+        builder.show();
+    }
+
+    private void callStopAll() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.stop_all_tokens_query));
+
+        builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                for (String token : mActives) {
+                    mUsageStatsManager.reportUsageStop(mActivity, token);
+                }
+                mActives.clear();
+                mAdapter.notifyDataSetChanged();
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+        builder.show();
+    }
+
+    /**
+     * A call-back for when the user presses the back button.
+     */
+    OnClickListener mStartListener = new OnClickListener() {
+        public void onClick(View v) {
+            final View parent = (View) v.getParent();
+            final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+            try {
+                mUsageStatsManager.reportUsageStart(mActivity, token);
+            } catch (Exception e) {
+                Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+            }
+            parent.setBackgroundColor(getColor(R.color.active_color));
+            mActives.add(token);
+        }
+    };
+
+    /**
+     * A call-back for when the user presses the clear button.
+     */
+    OnClickListener mStopListener = new OnClickListener() {
+        public void onClick(View v) {
+            final View parent = (View) v.getParent();
+
+            final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+            try {
+                mUsageStatsManager.reportUsageStop(mActivity, token);
+            } catch (Exception e) {
+                Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+            }
+            parent.setBackgroundColor(getColor(R.color.inactive_color));
+            mActives.remove(token);
+        }
+    };
+
+
+    private class Adapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return mTokens.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mTokens.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final ViewHolder holder;
+            if (convertView == null) {
+                convertView = LayoutInflater.from(UsageReporterActivity.this)
+                        .inflate(R.layout.row_item, parent, false);
+                holder = new ViewHolder();
+                holder.tokenName = (TextView) convertView.findViewById(R.id.token);
+
+                holder.startButton = ((Button) convertView.findViewById(R.id.start));
+                holder.startButton.setOnClickListener(mStartListener);
+                holder.stopButton = ((Button) convertView.findViewById(R.id.stop));
+                holder.stopButton.setOnClickListener(mStopListener);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            final String token = mTokens.get(position);
+            holder.tokenName.setText(mTokens.get(position));
+            if (mActives.contains(token)) {
+                convertView.setBackgroundColor(getColor(R.color.active_color));
+            } else {
+                convertView.setBackgroundColor(getColor(R.color.inactive_color));
+            }
+            return convertView;
+        }
+    }
+
+    private static class ViewHolder {
+        public TextView tokenName;
+        public Button startButton;
+        public Button stopButton;
+    }
+}
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 3d8ce21..3c628f6 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -155,7 +155,7 @@
                     intent.setPackage(getPackageName());
                     intent.putExtra(EXTRA_KEY_TIMEOUT, true);
                     mUsageStatsManager.registerAppUsageObserver(1, packages,
-                            30, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
+                            60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
                                     1, intent, 0));
                 }
             }
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index d5987a5..8564698 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -28,6 +28,7 @@
 import android.view.IWindowSession;
 import android.view.InsetsState;
 import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
@@ -106,7 +107,7 @@
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
                                 new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
-                                new Surface(), new InsetsState());
+                                new SurfaceControl(), new InsetsState());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 9d1edbf..6850673 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -18,6 +18,7 @@
     mockito-target-minus-junit4 \
     platform-test-annotations \
     services.core \
+    services.ipmemorystore \
     services.net
 
 LOCAL_JAVA_LIBRARIES := \
@@ -53,6 +54,7 @@
     libnativehelper \
     libpackagelistparser \
     libpcre2 \
+    libprocessgroup \
     libselinux \
     libui \
     libutils \
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
new file mode 100644
index 0000000..eae9710
--- /dev/null
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.net;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpMemoryStoreTest {
+    @Mock
+    Context mMockContext;
+    @Mock
+    IIpMemoryStore mMockService;
+    IpMemoryStore mStore;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mStore = new IpMemoryStore(mMockContext, mMockService);
+    }
+
+    @Test
+    public void testNetworkAttributes() {
+        // TODO : implement this
+    }
+
+    @Test
+    public void testPrivateData() {
+        // TODO : implement this
+    }
+
+    @Test
+    public void testFindL2Key() {
+        // TODO : implement this
+    }
+
+    @Test
+    public void testIsSameNetwork() {
+        // TODO : implement this
+    }
+
+}
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 9695e9a..299fbef 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -18,6 +18,7 @@
 
 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.junit.Assert.fail;
 
@@ -33,6 +34,9 @@
 import android.system.OsConstants;
 import android.util.ArraySet;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -41,9 +45,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LinkPropertiesTest {
@@ -53,6 +54,8 @@
     private static InetAddress DNS1 = NetworkUtils.numericToInetAddress("75.208.7.1");
     private static InetAddress DNS2 = NetworkUtils.numericToInetAddress("69.78.7.1");
     private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888");
+    private static InetAddress PCSCFV6 =  NetworkUtils.numericToInetAddress(
+            "2001:0db8:85a3:0000:0000:8a2e:0370:1");
     private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
     private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1");
     private static InetAddress GATEWAY61 = NetworkUtils.numericToInetAddress("fe80::6:0000:613");
@@ -86,6 +89,9 @@
         assertTrue(source.isIdenticalValidatedPrivateDnses(target));
         assertTrue(target.isIdenticalValidatedPrivateDnses(source));
 
+        assertTrue(source.isIdenticalPcscfs(target));
+        assertTrue(target.isIdenticalPcscfs(source));
+
         assertTrue(source.isIdenticalRoutes(target));
         assertTrue(target.isIdenticalRoutes(source));
 
@@ -128,6 +134,8 @@
         // set 2 dnses
         source.addDnsServer(DNS1);
         source.addDnsServer(DNS2);
+        // set 1 pcscf
+        source.addPcscfServer(PCSCFV6);
         // set 2 gateways
         source.addRoute(new RouteInfo(GATEWAY1));
         source.addRoute(new RouteInfo(GATEWAY2));
@@ -141,6 +149,7 @@
         target.addLinkAddress(LINKADDRV6);
         target.addDnsServer(DNS1);
         target.addDnsServer(DNS2);
+        target.addPcscfServer(PCSCFV6);
         target.addRoute(new RouteInfo(GATEWAY1));
         target.addRoute(new RouteInfo(GATEWAY2));
         target.setMtu(MTU);
@@ -154,6 +163,7 @@
         target.addLinkAddress(LINKADDRV6);
         target.addDnsServer(DNS1);
         target.addDnsServer(DNS2);
+        target.addPcscfServer(PCSCFV6);
         target.addRoute(new RouteInfo(GATEWAY1));
         target.addRoute(new RouteInfo(GATEWAY2));
         target.setMtu(MTU);
@@ -167,6 +177,7 @@
         target.addLinkAddress(LINKADDRV6);
         target.addDnsServer(DNS1);
         target.addDnsServer(DNS2);
+        target.addPcscfServer(PCSCFV6);
         target.addRoute(new RouteInfo(GATEWAY1));
         target.addRoute(new RouteInfo(GATEWAY2));
         target.setMtu(MTU);
@@ -179,6 +190,21 @@
         // change dnses
         target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
         target.addDnsServer(DNS2);
+        target.addPcscfServer(PCSCFV6);
+        target.addRoute(new RouteInfo(GATEWAY1));
+        target.addRoute(new RouteInfo(GATEWAY2));
+        target.setMtu(MTU);
+        assertFalse(source.equals(target));
+
+        target.clear();
+        target.setInterfaceName(NAME);
+        target.addLinkAddress(LINKADDRV4);
+        target.addLinkAddress(LINKADDRV6);
+        target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+        target.addDnsServer(DNS2);
+        // change pcscf
+        target.addPcscfServer(NetworkUtils.numericToInetAddress(
+            "2001::1"));
         target.addRoute(new RouteInfo(GATEWAY1));
         target.addRoute(new RouteInfo(GATEWAY2));
         target.setMtu(MTU);
@@ -479,6 +505,40 @@
     }
 
     @Test
+    public void testNat64Prefix() throws Exception {
+        LinkProperties lp = new LinkProperties();
+        lp.addLinkAddress(LINKADDRV4);
+        lp.addLinkAddress(LINKADDRV6);
+
+        assertNull(lp.getNat64Prefix());
+
+        IpPrefix p = new IpPrefix("64:ff9b::/96");
+        lp.setNat64Prefix(p);
+        assertEquals(p, lp.getNat64Prefix());
+
+        p = new IpPrefix("2001:db8:a:b:1:2:3::/96");
+        lp.setNat64Prefix(p);
+        assertEquals(p, lp.getNat64Prefix());
+
+        p = new IpPrefix("2001:db8:a:b:1:2::/80");
+        try {
+            lp.setNat64Prefix(p);
+        } catch (IllegalArgumentException expected) {
+        }
+
+        p = new IpPrefix("64:ff9b::/64");
+        try {
+            lp.setNat64Prefix(p);
+        } catch (IllegalArgumentException expected) {
+        }
+
+        assertEquals(new IpPrefix("2001:db8:a:b:1:2:3::/96"), lp.getNat64Prefix());
+
+        lp.setNat64Prefix(null);
+        assertNull(lp.getNat64Prefix());
+    }
+
+    @Test
     public void testIsProvisioned() {
         LinkProperties lp4 = new LinkProperties();
         assertFalse("v4only:empty", lp4.isProvisioned());
@@ -789,8 +849,20 @@
         assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
     }
 
+    private void assertParcelingIsLossless(LinkProperties source) {
+        Parcel p = Parcel.obtain();
+        source.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        final byte[] marshalled = p.marshall();
+        p = Parcel.obtain();
+        p.unmarshall(marshalled, 0, marshalled.length);
+        p.setDataPosition(0);
+        LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
+        assertEquals(source, dest);
+    }
+
     @Test
-    public void testLinkPropertiesParcelable() {
+    public void testLinkPropertiesParcelable() throws Exception {
         LinkProperties source = new LinkProperties();
         source.setInterfaceName(NAME);
         // set 2 link addresses
@@ -808,15 +880,14 @@
 
         source.setMtu(MTU);
 
-        Parcel p = Parcel.obtain();
-        source.writeToParcel(p, /* flags */ 0);
-        p.setDataPosition(0);
-        final byte[] marshalled = p.marshall();
-        p = Parcel.obtain();
-        p.unmarshall(marshalled, 0, marshalled.length);
-        p.setDataPosition(0);
-        LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
+        source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
 
-        assertEquals(source, dest);
+        assertParcelingIsLossless(source);
+    }
+
+    @Test
+    public void testParcelUninitialized() throws Exception {
+        LinkProperties empty = new LinkProperties();
+        assertParcelingIsLossless(empty);
     }
 }
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
new file mode 100644
index 0000000..4a6f20a
--- /dev/null
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.net.dhcp;
+
+import static android.net.InetAddresses.parseNumericAddress;
+
+import static com.google.android.collect.Sets.newHashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpServingParamsParcelExtTest {
+    private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123");
+    private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b;
+    private static final int TEST_PREFIX_LENGTH = 17;
+    private static final int TEST_LEASE_TIME_SECS = 120;
+    private static final int TEST_MTU = 1000;
+    private static final Set<Inet4Address> TEST_ADDRESS_SET =
+            newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124"));
+    private static final Set<Integer> TEST_ADDRESS_SET_PARCELED =
+            newHashSet(0xc0a8017b, 0xc0a8017c);
+
+    private DhcpServingParamsParcelExt mParcel;
+
+    @Before
+    public void setUp() {
+        mParcel = new DhcpServingParamsParcelExt();
+    }
+
+    @Test
+    public void testSetServerAddr() {
+        mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH));
+
+        assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr);
+        assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength);
+    }
+
+    @Test
+    public void testSetDefaultRouters() {
+        mParcel.setDefaultRouters(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters));
+    }
+
+    @Test
+    public void testSetDnsServers() {
+        mParcel.setDnsServers(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers));
+    }
+
+    @Test
+    public void testSetExcludedAddrs() {
+        mParcel.setExcludedAddrs(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs));
+    }
+
+    @Test
+    public void testSetDhcpLeaseTimeSecs() {
+        mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS);
+        assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs);
+    }
+
+    @Test
+    public void testSetLinkMtu() {
+        mParcel.setLinkMtu(TEST_MTU);
+        assertEquals(TEST_MTU, mParcel.linkMtu);
+    }
+
+    @Test
+    public void testSetMetered() {
+        mParcel.setMetered(true);
+        assertTrue(mParcel.metered);
+        mParcel.setMetered(false);
+        assertFalse(mParcel.metered);
+    }
+
+    private static Inet4Address inet4Addr(String addr) {
+        return (Inet4Address) parseNumericAddress(addr);
+    }
+
+    private static Set<Integer> asSet(int[] ints) {
+        return IntStream.of(ints).boxed().collect(Collectors.toSet());
+    }
+}
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 2c675c6..c3162af 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -16,31 +16,37 @@
 
 package android.net.ip;
 
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_TETHERED;
+import static android.net.ip.IpServer.STATE_UNAVAILABLE;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ip.IpServer.STATE_AVAILABLE;
-import static android.net.ip.IpServer.STATE_TETHERED;
-import static android.net.ip.IpServer.STATE_UNAVAILABLE;
-
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
@@ -48,8 +54,9 @@
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServer;
+import android.net.dhcp.IDhcpServerCallbacks;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
@@ -60,8 +67,6 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 
-import java.net.Inet4Address;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -71,6 +76,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class IpServerTest {
@@ -82,16 +89,18 @@
     private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
             IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
 
+    private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
+
     @Mock private INetworkManagementService mNMService;
     @Mock private INetworkStatsService mStatsService;
     @Mock private IpServer.Callback mCallback;
     @Mock private InterfaceConfiguration mInterfaceConfiguration;
     @Mock private SharedLog mSharedLog;
-    @Mock private DhcpServer mDhcpServer;
+    @Mock private IDhcpServer mDhcpServer;
     @Mock private RouterAdvertisementDaemon mRaDaemon;
     @Mock private IpServer.Dependencies mDependencies;
 
-    @Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
+    @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
 
     private final TestLooper mLooper = new TestLooper();
     private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -112,8 +121,18 @@
         mLooper.dispatchAll();
         reset(mNMService, mStatsService, mCallback);
         when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
-        when(mDependencies.makeDhcpServer(
-                any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
+
+        doAnswer(inv -> {
+            final IDhcpServerCallbacks cb = inv.getArgument(2);
+            new Thread(() -> {
+                try {
+                    cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
+                } catch (RemoteException e) {
+                    fail(e.getMessage());
+                }
+            }).run();
+            return null;
+        }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
         when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
         when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
 
@@ -399,21 +418,20 @@
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
 
-        verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
+        verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
     }
 
-    private void assertDhcpStarted(IpPrefix expectedPrefix) {
-        verify(mDependencies, times(1)).makeDhcpServer(
-                eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
-        verify(mDhcpServer, times(1)).start();
-        final DhcpServingParams params = mDhcpParamsCaptor.getValue();
+    private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
+        verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
+        verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
+        final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
         // Last address byte is random
-        assertTrue(expectedPrefix.contains(params.serverAddr.getAddress()));
-        assertEquals(expectedPrefix.getPrefixLength(), params.serverAddr.getPrefixLength());
-        assertEquals(1, params.defaultRouters.size());
-        assertEquals(params.serverAddr.getAddress(), params.defaultRouters.iterator().next());
-        assertEquals(1, params.dnsServers.size());
-        assertEquals(params.serverAddr.getAddress(), params.dnsServers.iterator().next());
+        assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
+        assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
+        assertEquals(1, params.defaultRouters.length);
+        assertEquals(params.serverAddr, params.defaultRouters[0]);
+        assertEquals(1, params.dnsServers.length);
+        assertEquals(params.serverAddr, params.dnsServers[0]);
         assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
     }
 
@@ -458,7 +476,7 @@
             addr4 = addr;
             break;
         }
-        assertTrue("missing IPv4 address", addr4 != null);
+        assertNotNull("missing IPv4 address", addr4);
 
         // Assert the presence of the associated directly connected route.
         final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
new file mode 100644
index 0000000..a9f9758
--- /dev/null
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -0,0 +1,113 @@
+/*
+ * 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.net.ipmemorystore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Collections;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ParcelableTests {
+    @Test
+    public void testNetworkAttributesParceling() throws Exception {
+        final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+        NetworkAttributes in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+        builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+        // groupHint stays null this time around
+        builder.setDnsAddresses(Collections.emptyList());
+        builder.setMtu(18);
+        in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+        builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
+        builder.setGroupHint("groupHint");
+        builder.setDnsAddresses(Arrays.asList(
+                InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
+                InetAddress.getByName("6.7.8.9")));
+        builder.setMtu(1_000_000);
+        in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+        builder.setMtu(null);
+        in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+    }
+
+    @Test
+    public void testPrivateDataParceling() throws Exception {
+        final Blob in = new Blob();
+        in.data = new byte[] {89, 111, 108, 111};
+        final Blob out = parcelingRoundTrip(in);
+        // Object.equals on byte[] tests the references
+        assertEquals(in.data.length, out.data.length);
+        assertTrue(Arrays.equals(in.data, out.data));
+    }
+
+    @Test
+    public void testSameL3NetworkResponseParceling() throws Exception {
+        final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable();
+        parcelable.l2Key1 = "key 1";
+        parcelable.l2Key2 = "key 2";
+        parcelable.confidence = 0.43f;
+
+        final SameL3NetworkResponse in = new SameL3NetworkResponse(parcelable);
+        assertEquals("key 1", in.l2Key1);
+        assertEquals("key 2", in.l2Key2);
+        assertEquals(0.43f, in.confidence, 0.01f /* delta */);
+
+        final SameL3NetworkResponse out =
+                new SameL3NetworkResponse(parcelingRoundTrip(in.toParcelable()));
+
+        assertEquals(in, out);
+        assertEquals(in.l2Key1, out.l2Key1);
+        assertEquals(in.l2Key2, out.l2Key2);
+        assertEquals(in.confidence, out.confidence, 0.01f /* delta */);
+    }
+
+    private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception {
+        final Parcel p = Parcel.obtain();
+        in.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        final byte[] marshalledData = p.marshall();
+        p.recycle();
+
+        final Parcel q = Parcel.obtain();
+        q.unmarshall(marshalledData, 0, marshalledData.length);
+        q.setDataPosition(0);
+
+        final Parcelable.Creator<T> creator = (Parcelable.Creator<T>)
+                in.getClass().getField("CREATOR").get(null); // static object, so null receiver
+        final T unmarshalled = (T) creator.createFromParcel(q);
+        q.recycle();
+        return unmarshalled;
+    }
+}
diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
new file mode 100644
index 0000000..4cabfc9
--- /dev/null
+++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.shared;
+
+import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
+import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.ProxyInfo;
+import android.net.RouteInfo;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Tests for {@link LinkPropertiesParcelableUtil}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LinkPropertiesParcelableUtilTest {
+    private LinkProperties mLinkProperties;
+
+    private static final String TEST_LINKPROPS_IFACE = "TEST_IFACE";
+    private static final String TEST_STACKED_LINK_1_IFACE = "TEST_STACKED_IFACE_1";
+    private static final String TEST_STACKED_LINK_2_IFACE = "TEST_STACKED_IFACE_2";
+
+    @Before
+    public void setUp() {
+        mLinkProperties = makeLinkProperties(TEST_LINKPROPS_IFACE);
+        mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_1_IFACE));
+        mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_2_IFACE));
+    }
+
+    private static LinkProperties makeLinkProperties(String iface) {
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(iface);
+        lp.setLinkAddresses(Arrays.asList(
+                new LinkAddress(InetAddresses.parseNumericAddress("192.168.0.42"), 16),
+                new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::7"), 42)));
+        lp.setDnsServers(Arrays.asList(
+                InetAddresses.parseNumericAddress("2001:db8::42"),
+                InetAddresses.parseNumericAddress("192.168.1.1")
+        ));
+        lp.setValidatedPrivateDnsServers(Arrays.asList(
+                InetAddresses.parseNumericAddress("2001:db8::43"),
+                InetAddresses.parseNumericAddress("192.168.42.43")
+        ));
+        lp.setPcscfServers(Arrays.asList(
+                InetAddresses.parseNumericAddress("2001:db8::47"),
+                InetAddresses.parseNumericAddress("192.168.42.47")
+        ));
+        lp.setUsePrivateDns(true);
+        lp.setPrivateDnsServerName("test.example.com");
+        lp.setDomains("test1.example.com,test2.example.com");
+        lp.addRoute(new RouteInfo(
+                new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::44"), 45),
+                InetAddresses.parseNumericAddress("2001:db8::45"),
+                iface,
+                RouteInfo.RTN_UNICAST
+        ));
+        lp.addRoute(new RouteInfo(
+                new IpPrefix(InetAddresses.parseNumericAddress("192.168.44.45"), 16),
+                InetAddresses.parseNumericAddress("192.168.45.1"),
+                iface,
+                RouteInfo.RTN_THROW
+        ));
+        lp.setHttpProxy(new ProxyInfo("test3.example.com", 8000,
+                "excl1.example.com,excl2.example.com"));
+        lp.setMtu(5000);
+        lp.setTcpBufferSizes("1,2,3,4,5,6");
+        lp.setNat64Prefix(new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96));
+
+        // Verify that this test does not miss any new field added later.
+        // If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals
+        // must also be updated.
+        assertEquals(14, Arrays.stream(LinkProperties.class.getDeclaredFields())
+                .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
+
+        return lp;
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_NullInterface() {
+        mLinkProperties.setInterfaceName(null);
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_NullPrivateDnsServer() {
+        mLinkProperties.setPrivateDnsServerName(null);
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_NullDomains() {
+        mLinkProperties.setDomains(null);
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_NullProxy() {
+        mLinkProperties.setHttpProxy(null);
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_NullTcpBufferSizes() {
+        mLinkProperties.setTcpBufferSizes(null);
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_EmptyLinkAddresses() {
+        mLinkProperties.setLinkAddresses(Collections.emptyList());
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_EmptyDnses() {
+        mLinkProperties.setDnsServers(Collections.emptyList());
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_EmptyValidatedPrivateDnses() {
+        mLinkProperties.setValidatedPrivateDnsServers(Collections.emptyList());
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_EmptyRoutes() {
+        for (RouteInfo r : mLinkProperties.getAllRoutes()) {
+            mLinkProperties.removeRoute(r);
+        }
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_PacFileProxyInfo() {
+        mLinkProperties.setHttpProxy(new ProxyInfo(Uri.parse("http://pacfile.example.com")));
+        doParcelUnparcelTest();
+    }
+
+    @Test
+    public void testParcelUnparcel_NullNat64Prefix() {
+        mLinkProperties.setNat64Prefix(null);
+        doParcelUnparcelTest();
+    }
+
+    private void doParcelUnparcelTest() {
+        final LinkProperties unparceled = fromStableParcelable(toStableParcelable(mLinkProperties));
+        assertLinkPropertiesEquals(mLinkProperties, unparceled);
+    }
+
+    private static void assertLinkPropertiesEquals(LinkProperties expected, LinkProperties actual) {
+        assertEquals(expected, actual);
+
+        // LinkProperties equals() does not include stacked links
+        assertEquals(expected.getStackedLinks(), actual.getStackedLinks());
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 71529fd..882babf 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -26,6 +26,8 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
 import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
@@ -69,17 +71,19 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -89,7 +93,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
-import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.ConnectivityManager.PacketKeepalive;
@@ -97,6 +100,8 @@
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
 import android.net.INetd;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
@@ -114,12 +119,13 @@
 import android.net.NetworkMisc;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
+import android.net.NetworkStack;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
-import android.net.StringNetworkSpecifier;
 import android.net.UidRange;
-import android.net.captiveportal.CaptivePortalProbeResult;
 import android.net.metrics.IpConnectivityLog;
+import android.net.shared.NetworkMonitorUtils;
+import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
 import android.os.ConditionVariable;
 import android.os.Handler;
@@ -138,6 +144,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -148,12 +155,9 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.connectivity.ConnectivityConstants;
 import com.android.server.connectivity.DefaultNetworkMetrics;
-import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
 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;
@@ -168,6 +172,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -230,6 +235,7 @@
     @Mock INetworkStatsService mStatsService;
     @Mock INetworkPolicyManager mNpm;
     @Mock INetd mMockNetd;
+    @Mock NetworkStack mNetworkStack;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -299,6 +305,7 @@
         public Object getSystemService(String name) {
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
             if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class);
+            if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
             return super.getSystemService(name);
         }
 
@@ -386,7 +393,7 @@
     }
 
     private class MockNetworkAgent {
-        private final WrappedNetworkMonitor mWrappedNetworkMonitor;
+        private final INetworkMonitor mNetworkMonitor;
         private final NetworkInfo mNetworkInfo;
         private final NetworkCapabilities mNetworkCapabilities;
         private final HandlerThread mHandlerThread;
@@ -402,6 +409,26 @@
         // mNetworkStatusReceived.
         private String mRedirectUrl;
 
+        private INetworkMonitorCallbacks mNmCallbacks;
+        private int mNmValidationResult = NETWORK_TEST_RESULT_INVALID;
+        private String mNmValidationRedirectUrl = null;
+        private boolean mNmProvNotificationRequested = false;
+
+        void setNetworkValid() {
+            mNmValidationResult = NETWORK_TEST_RESULT_VALID;
+            mNmValidationRedirectUrl = null;
+        }
+
+        void setNetworkInvalid() {
+            mNmValidationResult = NETWORK_TEST_RESULT_INVALID;
+            mNmValidationRedirectUrl = null;
+        }
+
+        void setNetworkPortal(String redirectUrl) {
+            setNetworkInvalid();
+            mNmValidationRedirectUrl = redirectUrl;
+        }
+
         MockNetworkAgent(int transport) {
             this(transport, new LinkProperties());
         }
@@ -434,6 +461,29 @@
             }
             mHandlerThread = new HandlerThread("Mock-" + typeName);
             mHandlerThread.start();
+
+            mNetworkMonitor = mock(INetworkMonitor.class);
+            final Answer validateAnswer = inv -> {
+                new Thread(this::onValidationRequested).start();
+                return null;
+            };
+
+            try {
+                doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected();
+                doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
+            } catch (RemoteException e) {
+                fail(e.getMessage());
+            }
+
+            final ArgumentCaptor<Network> nmNetworkCaptor =
+                    ArgumentCaptor.forClass(Network.class);
+            final ArgumentCaptor<INetworkMonitorCallbacks> nmCbCaptor =
+                    ArgumentCaptor.forClass(INetworkMonitorCallbacks.class);
+            doNothing().when(mNetworkStack).makeNetworkMonitor(
+                    nmNetworkCaptor.capture(),
+                    any() /* name */,
+                    nmCbCaptor.capture());
+
             mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
                     "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
                     linkProperties, mScore, new NetworkMisc()) {
@@ -465,10 +515,40 @@
                     mPreventReconnectReceived.open();
                 }
             };
+
+            assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId);
+            mNmCallbacks = nmCbCaptor.getValue();
+
+            try {
+                mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
+            } catch (RemoteException e) {
+                fail(e.getMessage());
+            }
+
             // Waits for the NetworkAgent to be registered, which includes the creation of the
             // NetworkMonitor.
             waitForIdle();
-            mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor();
+        }
+
+        private void onValidationRequested() {
+            try {
+                if (mNmProvNotificationRequested
+                        && mNmValidationResult == NETWORK_TEST_RESULT_VALID) {
+                    mNmCallbacks.hideProvisioningNotification();
+                    mNmProvNotificationRequested = false;
+                }
+
+                mNmCallbacks.notifyNetworkTested(
+                        mNmValidationResult, mNmValidationRedirectUrl);
+
+                if (mNmValidationRedirectUrl != null) {
+                    mNmCallbacks.showProvisioningNotification(
+                            "test_provisioning_notif_action");
+                    mNmProvNotificationRequested = true;
+                }
+            } catch (RemoteException e) {
+                fail(e.getMessage());
+            }
         }
 
         public void adjustScore(int change) {
@@ -539,7 +619,7 @@
             NetworkCallback callback = null;
             final ConditionVariable validatedCv = new ConditionVariable();
             if (validated) {
-                mWrappedNetworkMonitor.gen204ProbeResult = 204;
+                setNetworkValid();
                 NetworkRequest request = new NetworkRequest.Builder()
                         .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
                         .clearCapabilities()
@@ -564,15 +644,14 @@
             if (validated) {
                 // Wait for network to validate.
                 waitFor(validatedCv);
-                mWrappedNetworkMonitor.gen204ProbeResult = 500;
+                setNetworkInvalid();
             }
 
             if (callback != null) mCm.unregisterNetworkCallback(callback);
         }
 
         public void connectWithCaptivePortal(String redirectUrl) {
-            mWrappedNetworkMonitor.gen204ProbeResult = 200;
-            mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl;
+            setNetworkPortal(redirectUrl);
             connect(false);
         }
 
@@ -603,10 +682,6 @@
             return mDisconnected;
         }
 
-        public WrappedNetworkMonitor getWrappedNetworkMonitor() {
-            return mWrappedNetworkMonitor;
-        }
-
         public void sendLinkProperties(LinkProperties lp) {
             mNetworkAgent.sendLinkProperties(lp);
         }
@@ -880,28 +955,6 @@
         }
     }
 
-    // NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
-    private class WrappedNetworkMonitor extends NetworkMonitor {
-        public final Handler connectivityHandler;
-        // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
-        public int gen204ProbeResult = 500;
-        public String gen204ProbeRedirectUrl = null;
-
-        public WrappedNetworkMonitor(Context context, Handler handler,
-                NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
-                IpConnectivityLog log) {
-            super(context, handler, networkAgentInfo, defaultRequest, log,
-                    NetworkMonitor.Dependencies.DEFAULT);
-            connectivityHandler = handler;
-        }
-
-        @Override
-        protected CaptivePortalProbeResult isCaptivePortal() {
-            if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
-            return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
-        }
-    }
-
     private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
         public volatile boolean configRestrictsAvoidBadWifi;
         public volatile int configMeteredMultipathPreference;
@@ -923,7 +976,6 @@
 
     private class WrappedConnectivityService extends ConnectivityService {
         public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker;
-        private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
         private MockableSystemProperties mSystemProperties;
 
         public WrappedConnectivityService(Context context, INetworkManagementService netManager,
@@ -971,15 +1023,6 @@
             }
         }
 
-        @Override
-        public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
-                NetworkAgentInfo nai, NetworkRequest defaultRequest) {
-            final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(
-                    context, handler, nai, defaultRequest, mock(IpConnectivityLog.class));
-            mLastCreatedNetworkMonitor = monitor;
-            return monitor;
-        }
-
         public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
             return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
         }
@@ -1017,10 +1060,6 @@
         protected void registerNetdEventCallback() {
         }
 
-        public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
-            return mLastCreatedNetworkMonitor;
-        }
-
         public void mockVpn(int uid) {
             synchronized (mVpns) {
                 int userId = UserHandle.getUserId(uid);
@@ -2439,7 +2478,7 @@
 
         // Make captive portal disappear then revalidate.
         // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
-        mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
+        mWiFiNetworkAgent.setNetworkValid();
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
         captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
@@ -2448,13 +2487,13 @@
 
         // Break network connectivity.
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
-        mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500;
+        mWiFiNetworkAgent.setNetworkInvalid();
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
         validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
     }
 
     @Test
-    public void testCaptivePortalApp() {
+    public void testCaptivePortalApp() throws RemoteException {
         final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
         final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
                 .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2477,21 +2516,19 @@
         mServiceContext.expectNoStartActivityIntent(fastTimeoutMs);
 
         // Turn into a captive portal.
-        mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 302;
+        mWiFiNetworkAgent.setNetworkPortal("http://example.com");
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
-        // Check that startCaptivePortalApp sends the expected intent.
+        // Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
         mCm.startCaptivePortalApp(wifiNetwork);
-        Intent intent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS);
-        assertEquals(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
-        assertEquals(wifiNetwork, intent.getExtra(ConnectivityManager.EXTRA_NETWORK));
+        verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1))
+                .launchCaptivePortalApp();
 
-        // Have the app report that the captive portal is dismissed, and check that we revalidate.
-        mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
-        CaptivePortal c = (CaptivePortal) intent.getExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
-        c.reportCaptivePortalDismissed();
+        // Report that the captive portal is dismissed, and check that callbacks are fired
+        mWiFiNetworkAgent.setNetworkValid();
+        mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
         validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
@@ -2524,36 +2561,82 @@
         waitFor(avoidCv);
 
         assertNoCallbacks(captivePortalCallback, validatedCallback);
-
-        // Now test ignore mode.
-        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
-
-        // Bring up a network with a captive portal.
-        // Since we're ignoring captive portals, the network will validate.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        String secondRedirectUrl = "http://example.com/secondPath";
-        mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
-
-        // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
-        validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
-        // But there should be no CaptivePortal callback.
-        captivePortalCallback.assertNoCallback();
     }
 
     private NetworkRequest.Builder newWifiRequestBuilder() {
         return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
     }
 
+    /**
+     * Verify request matching behavior with network specifiers.
+     *
+     * Note: this test is somewhat problematic since it involves removing capabilities from
+     * agents - i.e. agents rejecting requests which they previously accepted. This is flagged
+     * as a WTF bug in
+     * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)} but
+     * does work.
+     */
     @Test
     public void testNetworkSpecifier() {
+        // A NetworkSpecifier subclass that matches all networks but must not be visible to apps.
+        class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements
+                Parcelable {
+            @Override
+            public boolean satisfiedBy(NetworkSpecifier other) {
+                return true;
+            }
+
+            @Override
+            public int describeContents() {
+                return 0;
+            }
+
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {}
+
+            @Override
+            public NetworkSpecifier redact() {
+                return null;
+            }
+        }
+
+        // A network specifier that matches either another LocalNetworkSpecifier with the same
+        // string or a ConfidentialMatchAllNetworkSpecifier, and can be passed to apps as is.
+        class LocalStringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+            private String mString;
+
+            LocalStringNetworkSpecifier(String string) {
+                mString = string;
+            }
+
+            @Override
+            public boolean satisfiedBy(NetworkSpecifier other) {
+                if (other instanceof LocalStringNetworkSpecifier) {
+                    return TextUtils.equals(mString,
+                            ((LocalStringNetworkSpecifier) other).mString);
+                }
+                if (other instanceof ConfidentialMatchAllNetworkSpecifier) return true;
+                return false;
+            }
+
+            @Override
+            public int describeContents() {
+                return 0;
+            }
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {}
+        }
+
+
         NetworkRequest rEmpty1 = newWifiRequestBuilder().build();
         NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build();
         NetworkRequest rEmpty3 = newWifiRequestBuilder().setNetworkSpecifier("").build();
         NetworkRequest rEmpty4 = newWifiRequestBuilder().setNetworkSpecifier(
             (NetworkSpecifier) null).build();
-        NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier("foo").build();
+        NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier(
+                new LocalStringNetworkSpecifier("foo")).build();
         NetworkRequest rBar = newWifiRequestBuilder().setNetworkSpecifier(
-                new StringNetworkSpecifier("bar")).build();
+                new LocalStringNetworkSpecifier("bar")).build();
 
         TestNetworkCallback cEmpty1 = new TestNetworkCallback();
         TestNetworkCallback cEmpty2 = new TestNetworkCallback();
@@ -2562,7 +2645,7 @@
         TestNetworkCallback cFoo = new TestNetworkCallback();
         TestNetworkCallback cBar = new TestNetworkCallback();
         TestNetworkCallback[] emptyCallbacks = new TestNetworkCallback[] {
-                cEmpty1, cEmpty2, cEmpty3 };
+                cEmpty1, cEmpty2, cEmpty3, cEmpty4 };
 
         mCm.registerNetworkCallback(rEmpty1, cEmpty1);
         mCm.registerNetworkCallback(rEmpty2, cEmpty2);
@@ -2571,6 +2654,9 @@
         mCm.registerNetworkCallback(rFoo, cFoo);
         mCm.registerNetworkCallback(rBar, cBar);
 
+        LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo");
+        LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar");
+
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2579,30 +2665,54 @@
         cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertNoCallbacks(cFoo, cBar);
 
-        mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo"));
+        mWiFiNetworkAgent.setNetworkSpecifier(nsFoo);
         cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         for (TestNetworkCallback c: emptyCallbacks) {
-            c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+            c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsFoo),
+                    mWiFiNetworkAgent);
         }
-        cFoo.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+        cFoo.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsFoo),
+                mWiFiNetworkAgent);
+        assertEquals(nsFoo,
+                mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
         cFoo.assertNoCallback();
 
-        mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar"));
+        mWiFiNetworkAgent.setNetworkSpecifier(nsBar);
         cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         for (TestNetworkCallback c: emptyCallbacks) {
-            c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+            c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsBar),
+                    mWiFiNetworkAgent);
         }
-        cBar.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+        cBar.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsBar),
+                mWiFiNetworkAgent);
+        assertEquals(nsBar,
+                mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
+        cBar.assertNoCallback();
+
+        mWiFiNetworkAgent.setNetworkSpecifier(new ConfidentialMatchAllNetworkSpecifier());
+        cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        for (TestNetworkCallback c : emptyCallbacks) {
+            c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null,
+                    mWiFiNetworkAgent);
+        }
+        cFoo.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null,
+                mWiFiNetworkAgent);
+        cBar.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null,
+                mWiFiNetworkAgent);
+        assertNull(
+                mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
+        cFoo.assertNoCallback();
         cBar.assertNoCallback();
 
         mWiFiNetworkAgent.setNetworkSpecifier(null);
+        cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         cBar.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         for (TestNetworkCallback c: emptyCallbacks) {
             c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
         }
 
-        assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cFoo, cBar);
+        assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar);
     }
 
     @Test
@@ -3169,7 +3279,7 @@
         Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi.
-        mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
+        mWiFiNetworkAgent.setNetworkInvalid();
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3213,7 +3323,7 @@
         wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi and expect the dialog to appear.
-        mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
+        mWiFiNetworkAgent.setNetworkInvalid();
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -4002,11 +4112,9 @@
         final String TLS_SERVER6 = "2001:db8:53::53";
         final InetAddress[] TLS_IPS = new InetAddress[]{ InetAddress.getByName(TLS_SERVER6) };
         final String[] TLS_SERVERS = new String[]{ TLS_SERVER6 };
-        final Handler h = mCellNetworkAgent.getWrappedNetworkMonitor().connectivityHandler;
-        h.sendMessage(h.obtainMessage(
-                NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0,
-                mCellNetworkAgent.getNetwork().netId,
-                new DnsManager.PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS)));
+        mCellNetworkAgent.mNmCallbacks.notifyPrivateDnsConfigResolved(
+                new PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS).toParcel());
+
         waitForIdle();
         verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
@@ -4294,6 +4402,12 @@
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
+        // VPN networks do not satisfy the default request and are automatically validated
+        // by NetworkMonitor
+        assertFalse(NetworkMonitorUtils.isValidationRequired(
+                mCm.getDefaultRequest().networkCapabilities, vpnNetworkAgent.mNetworkCapabilities));
+        vpnNetworkAgent.setNetworkValid();
+
         vpnNetworkAgent.connect(false);
         mMockVpn.connect();
 
@@ -4656,7 +4770,7 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         mCellNetworkAgent.connect(true);
         networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-        verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME);
+        verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME);
         Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
 
         // Clat iface up, expect stack link updated.
@@ -4683,7 +4797,7 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
-        verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME);
+        verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
 
         // Clat iface removed, expect linkproperties revert to original one
         clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index d8f9618..a844cfe 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -535,7 +535,10 @@
 
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+
+        Socket socket = new Socket();
+        socket.bind(null);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
 
         int resourceId = createTransformResp.resourceId;
         mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
@@ -552,7 +555,9 @@
 
     @Test
     public void testRemoveTransportModeTransform() throws Exception {
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+        Socket socket = new Socket();
+        socket.bind(null);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
         mIpSecService.removeTransportModeTransforms(pfd);
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 724446e..5be7c7b 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -422,7 +422,9 @@
 
     @Test
     public void testRemoveTransportModeTransform() throws Exception {
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+        Socket socket = new Socket();
+        socket.bind(null);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
         mIpSecService.removeTransportModeTransforms(pfd);
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 01b468a..38322e9 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -17,7 +17,6 @@
 package com.android.server.connectivity;
 
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
@@ -29,13 +28,13 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.RouteInfo;
+import android.net.shared.PrivateDnsConfig;
 import android.os.INetworkManagementService;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
@@ -43,18 +42,16 @@
 import android.test.mock.MockContentResolver;
 
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
-import com.android.server.connectivity.MockableSystemProperties;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.net.InetAddress;
 import java.util.Arrays;
 
-import org.junit.runner.RunWith;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
 /**
  * Tests for {@link DnsManager}.
  *
@@ -133,7 +130,7 @@
                 PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
         Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
         mDnsManager.updatePrivateDns(new Network(TEST_NETID),
-                new DnsManager.PrivateDnsConfig("strictmode.com", new InetAddress[] {
+                new PrivateDnsConfig("strictmode.com", new InetAddress[] {
                     InetAddress.parseNumericAddress("6.6.6.6"),
                     InetAddress.parseNumericAddress("2001:db8:66:66::1")
                     }));
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 354cf2f..9578ded 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -23,31 +23,33 @@
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.reset;
 
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.INetd;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkMisc;
-import android.support.test.runner.AndroidJUnit4;
+import android.net.NetworkStack;
+import android.os.INetworkManagementService;
 import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.text.format.DateUtils;
 
 import com.android.internal.R;
 import com.android.server.ConnectivityService;
-import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 
-import org.junit.runner.RunWith;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -66,17 +68,22 @@
     LingerMonitor mMonitor;
 
     @Mock ConnectivityService mConnService;
+    @Mock INetd mNetd;
+    @Mock INetworkManagementService mNMS;
     @Mock Context mCtx;
     @Mock NetworkMisc mMisc;
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
+    @Mock NetworkStack mNetworkStack;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCtx.getResources()).thenReturn(mResources);
         when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
-        when(mConnService.createNetworkMonitor(any(), any(), any(), any())).thenReturn(null);
+        when(mCtx.getSystemServiceName(NetworkStack.class))
+                .thenReturn(Context.NETWORK_STACK_SERVICE);
+        when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
 
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
     }
@@ -349,7 +356,7 @@
         caps.addCapability(0);
         caps.addTransportType(transport);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
-                caps, 50, mCtx, null, mMisc, null, mConnService);
+                caps, 50, mCtx, null, mMisc, mConnService, mNetd, mNMS);
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index bf42412..07b1d05 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -17,9 +17,7 @@
 package com.android.server.connectivity;
 
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -27,6 +25,7 @@
 import static org.mockito.Mockito.when;
 
 import android.net.ConnectivityManager;
+import android.net.INetd;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -57,6 +56,7 @@
 
     @Mock ConnectivityService mConnectivity;
     @Mock NetworkMisc mMisc;
+    @Mock INetd mNetd;
     @Mock INetworkManagementService mNms;
     @Mock InterfaceConfiguration mConfig;
     @Mock NetworkAgentInfo mNai;
@@ -65,7 +65,7 @@
     Handler mHandler;
 
     Nat464Xlat makeNat464Xlat() {
-        return new Nat464Xlat(mNms, mNai);
+        return new Nat464Xlat(mNai, mNetd, mNms);
     }
 
     @Before
@@ -129,7 +129,7 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // Stacked interface up notification arrives.
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -144,7 +144,7 @@
         // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
         nat.stop();
 
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
 
         // Stacked interface removed notification arrives.
         nat.interfaceRemoved(STACKED_IFACE);
@@ -156,7 +156,7 @@
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
         assertIdle(nat);
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     @Test
@@ -168,7 +168,7 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // Stacked interface up notification arrives.
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -185,7 +185,7 @@
         mLooper.dispatchNext();
 
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
         assertTrue(c.getValue().getStackedLinks().isEmpty());
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -194,7 +194,7 @@
         // ConnectivityService stops clat: no-op.
         nat.stop();
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     @Test
@@ -205,13 +205,13 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
         nat.stop();
 
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
         assertIdle(nat);
 
         // In-flight interface up notification arrives: no-op
@@ -225,7 +225,7 @@
 
         assertIdle(nat);
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     @Test
@@ -236,16 +236,16 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
         nat.stop();
 
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
         assertIdle(nat);
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     static void assertIdle(Nat464Xlat nat) {
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index e6b43d2..1ea83c2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -27,6 +27,7 @@
 import static android.net.ConnectivityManager.TETHERING_WIFI;
 import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -37,6 +38,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Matchers.anyInt;
@@ -47,6 +49,8 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -74,8 +78,9 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServer;
 import android.net.ip.IpServer;
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.util.InterfaceParams;
@@ -86,7 +91,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.INetworkManagementService;
-import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -129,6 +133,8 @@
     private static final String TEST_USB_IFNAME = "test_rndis0";
     private static final String TEST_WLAN_IFNAME = "test_wlan0";
 
+    private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
+
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
     @Mock private INetworkManagementService mNMService;
@@ -143,9 +149,11 @@
     @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
     @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
     @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
-    @Mock private DhcpServer mDhcpServer;
+    @Mock private IDhcpServer mDhcpServer;
     @Mock private INetd mNetd;
 
+    private final MockIpServerDependencies mIpServerDependencies =
+            spy(new MockIpServerDependencies());
     private final MockTetheringDependencies mTetheringDependencies =
             new MockTetheringDependencies();
 
@@ -185,6 +193,47 @@
         }
     }
 
+    public class MockIpServerDependencies extends IpServer.Dependencies {
+        MockIpServerDependencies() {
+            super(null);
+        }
+
+        @Override
+        public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
+                InterfaceParams ifParams) {
+            return mRouterAdvertisementDaemon;
+        }
+
+        @Override
+        public InterfaceParams getInterfaceParams(String ifName) {
+            assertTrue("Non-mocked interface " + ifName,
+                    ifName.equals(TEST_USB_IFNAME)
+                            || ifName.equals(TEST_WLAN_IFNAME)
+                            || ifName.equals(TEST_MOBILE_IFNAME));
+            final String[] ifaces = new String[] {
+                    TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+            return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
+                    MacAddress.ALL_ZEROS_ADDRESS);
+        }
+
+        @Override
+        public INetd getNetdService() {
+            return mNetd;
+        }
+
+        @Override
+        public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+                DhcpServerCallbacks cb) {
+            new Thread(() -> {
+                try {
+                    cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
+                } catch (RemoteException e) {
+                    fail(e.getMessage());
+                }
+            }).run();
+        }
+    }
+
     public class MockTetheringDependencies extends TetheringDependencies {
         StateMachine upstreamNetworkMonitorMasterSM;
         ArrayList<IpServer> ipv6CoordinatorNotifyList;
@@ -216,35 +265,8 @@
         }
 
         @Override
-        public IpServer.Dependencies getIpServerDependencies() {
-            return new IpServer.Dependencies() {
-                @Override
-                public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
-                        InterfaceParams ifParams) {
-                    return mRouterAdvertisementDaemon;
-                }
-
-                @Override
-                public InterfaceParams getInterfaceParams(String ifName) {
-                    final String[] ifaces = new String[] {
-                            TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
-                    final int index = ArrayUtils.indexOf(ifaces, ifName);
-                    assertTrue("Non-mocked interface: " + ifName, index >= 0);
-                    return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
-                            MacAddress.ALL_ZEROS_ADDRESS);
-                }
-
-                @Override
-                public INetd getNetdService() {
-                    return mNetd;
-                }
-
-                @Override
-                public DhcpServer makeDhcpServer(Looper looper, String ifName,
-                        DhcpServingParams params, SharedLog log) {
-                    return mDhcpServer;
-                }
-            };
+        public IpServer.Dependencies getIpServerDependencies(Context context) {
+            return mIpServerDependencies;
         }
 
         @Override
@@ -546,7 +568,7 @@
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
-        verify(mDhcpServer, times(1)).start();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
     }
 
     @Test
@@ -557,7 +579,7 @@
         runUsbTethering(upstreamState);
         sendIPv6TetherUpdates(upstreamState);
 
-        verify(mDhcpServer, never()).start();
+        verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
     }
 
     @Test
@@ -581,7 +603,7 @@
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mRouterAdvertisementDaemon, times(1)).start();
-        verify(mDhcpServer, times(1)).start();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -595,7 +617,7 @@
 
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mDhcpServer, times(1)).start();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
                 TEST_XLAT_MOBILE_IFNAME);
@@ -612,7 +634,7 @@
         runUsbTethering(upstreamState);
 
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mDhcpServer, times(1)).start();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         // Then 464xlat comes up
@@ -636,7 +658,7 @@
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         // DHCP not restarted on downstream (still times(1))
-        verify(mDhcpServer, times(1)).start();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
new file mode 100644
index 0000000..e63c3b0
--- /dev/null
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.net.ipmemorystore;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+
+/** Unit tests for {@link IpMemoryStoreServiceTest}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IpMemoryStoreServiceTest {
+    @Mock
+    Context mMockContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(new File("/tmp/test.db")).when(mMockContext).getDatabasePath(anyString());
+    }
+
+    @Test
+    public void testNetworkAttributes() {
+        final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+        // TODO : implement this
+    }
+
+    @Test
+    public void testPrivateData() {
+        final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+        // TODO : implement this
+    }
+
+    @Test
+    public void testFindL2Key() {
+        final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+        // TODO : implement this
+    }
+
+    @Test
+    public void testIsSameNetwork() {
+        final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+        // TODO : implement this
+    }
+}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java
new file mode 100644
index 0000000..8d367e2
--- /dev/null
+++ b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java
@@ -0,0 +1,149 @@
+/*
+ * 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.net.ipmemorystore;
+
+import static com.android.server.net.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link RelevanceUtils}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RelevanceUtilsTests {
+    @Test
+    public void testComputeRelevanceForTargetDate() {
+        final long dayInMillis = 24L * 60 * 60 * 1000;
+        final long base = 1_000_000L; // any given point in time
+        // Relevance when the network expires in 1000 years must be capped
+        assertEquals(CAPPED_RELEVANCE, RelevanceUtils.computeRelevanceForTargetDate(
+                base + 1000L * dayInMillis, base));
+        // Relevance when expiry is before the date must be 0
+        assertEquals(0, RelevanceUtils.computeRelevanceForTargetDate(base - 1, base));
+        // Make sure the relevance for a given target date is higher if the expiry is further
+        // in the future
+        assertTrue(RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base)
+                < RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base));
+
+        // Make sure the relevance falls slower as the expiry is closing in. This is to ensure
+        // the decay is indeed logarithmic.
+        final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(base, base);
+        final int relevance50DaysBeforeExpiry =
+                RelevanceUtils.computeRelevanceForTargetDate(base + 50 * dayInMillis, base);
+        final int relevance100DaysBeforeExpiry =
+                RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base);
+        final int relevance150DaysBeforeExpiry =
+                RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base);
+        assertEquals(0, relevanceAtExpiry);
+        assertTrue(relevance50DaysBeforeExpiry - relevanceAtExpiry
+                < relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry);
+        assertTrue(relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry
+                < relevance150DaysBeforeExpiry - relevance100DaysBeforeExpiry);
+    }
+
+    @Test
+    public void testIncreaseRelevance() {
+        long expiry = System.currentTimeMillis();
+
+        final long firstBump = RelevanceUtils.bumpExpiryDate(expiry);
+        // Though a few milliseconds might have elapsed, the first bump should push the duration
+        // to days in the future, so unless this test takes literal days between these two lines,
+        // this should always pass.
+        assertTrue(firstBump > expiry);
+
+        expiry = 0;
+        long lastDifference = Long.MAX_VALUE;
+        // The relevance should be capped in at most this many steps. Otherwise, fail.
+        final int steps = 1000;
+        for (int i = 0; i < steps; ++i) {
+            final long newExpiry = RelevanceUtils.bumpExpiryDuration(expiry);
+            if (newExpiry == expiry) {
+                // The relevance should be capped. Make sure it is, then exit without failure.
+                assertEquals(newExpiry, RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS);
+                return;
+            }
+            // Make sure the new expiry is further in the future than last time.
+            assertTrue(newExpiry > expiry);
+            // Also check that it was not bumped as much as the last bump, because the
+            // decay must be exponential.
+            assertTrue(newExpiry - expiry < lastDifference);
+            lastDifference = newExpiry - expiry;
+            expiry = newExpiry;
+        }
+        fail("Relevance failed to go to the maximum value after " + steps + " bumps");
+    }
+
+    @Test
+    public void testContinuity() {
+        final long expiry = System.currentTimeMillis();
+
+        // Relevance at expiry and after expiry should be the cap.
+        final int relevanceBeforeMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry,
+                expiry - (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1_000_000));
+        assertEquals(relevanceBeforeMaxLifetime, CAPPED_RELEVANCE);
+        final int relevanceForMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry,
+                expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS);
+        assertEquals(relevanceForMaxLifetime, CAPPED_RELEVANCE);
+
+        // If the max relevance is reached at the cap lifetime, one millisecond less than this
+        // should be very close. Strictly speaking this is a bit brittle, but it should be
+        // good enough for the purposes of the memory store.
+        final int relevanceForOneMillisecLessThanCap = RelevanceUtils.computeRelevanceForTargetDate(
+                expiry, expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1);
+        assertTrue(relevanceForOneMillisecLessThanCap <= CAPPED_RELEVANCE);
+        assertTrue(relevanceForOneMillisecLessThanCap >= CAPPED_RELEVANCE - 10);
+
+        // Likewise the relevance one millisecond before expiry should be very close to 0. It's
+        // fine if it rounds down to 0.
+        final int relevanceOneMillisecBeforeExpiry = RelevanceUtils.computeRelevanceForTargetDate(
+                expiry, expiry - 1);
+        assertTrue(relevanceOneMillisecBeforeExpiry <= 10);
+        assertTrue(relevanceOneMillisecBeforeExpiry >= 0);
+
+        final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, expiry);
+        assertEquals(relevanceAtExpiry, 0);
+        final int relevanceAfterExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry,
+                expiry + 1_000_000);
+        assertEquals(relevanceAfterExpiry, 0);
+    }
+
+    // testIncreaseRelevance makes sure bumping the expiry continuously always yields a
+    // monotonically increasing date as a side effect, but this tests that the relevance (as
+    // opposed to the expiry date) increases monotonically with increasing periods.
+    @Test
+    public void testMonotonicity() {
+        // Hopefully the relevance is granular enough to give a different value for every one
+        // of this number of steps.
+        final int steps = 40;
+        final long expiry = System.currentTimeMillis();
+
+        int lastRelevance = -1;
+        for (int i = 0; i < steps; ++i) {
+            final long date = expiry - i * (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS / steps);
+            final int relevance = RelevanceUtils.computeRelevanceForTargetDate(expiry, date);
+            assertTrue(relevance > lastRelevance);
+            lastRelevance = relevance;
+        }
+    }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index c42a888..7783e10 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -58,6 +58,7 @@
         "libprotobuf-cpp-lite",
         "libz",
     ],
+    stl: "libc++_static",
     group_static_libs: true,
 }
 
@@ -114,6 +115,7 @@
         "optimize/MultiApkGenerator.cpp",
         "optimize/ResourceDeduper.cpp",
         "optimize/ResourceFilter.cpp",
+        "optimize/ResourcePathShortener.cpp",
         "optimize/VersionCollapser.cpp",
         "process/SymbolTable.cpp",
         "split/TableSplitter.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index b353ff0..45719ef 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -223,8 +223,17 @@
     io::IFile* file = iterator->Next();
     std::string path = file->GetSource().path;
 
+    std::string output_path = path;
+    bool is_resource = path.find("res/") == 0;
+    if (is_resource) {
+      auto it = options.shortened_path_map.find(path);
+      if (it != options.shortened_path_map.end()) {
+        output_path = it->second;
+      }
+    }
+
     // Skip resources that are not referenced if requested.
-    if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
+    if (is_resource && referenced_resources.find(output_path) == referenced_resources.end()) {
       if (context->IsVerbose()) {
         context->GetDiagnostics()->Note(DiagMessage()
                                         << "Removing resource '" << path << "' from APK.");
@@ -283,7 +292,8 @@
         return false;
       }
     } else {
-      if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
+      if (!io::CopyFileToArchivePreserveCompression(
+              context, file, output_path, writer)) {
         return false;
       }
     }
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 58702dc..2f8ca2d 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -714,7 +714,8 @@
     }
   }
 
-  diag_->Warn(DiagMessage(out_resource->source)
+  // If the resource type was not recognized, write the error and return false.
+  diag_->Error(DiagMessage(out_resource->source)
               << "unknown resource type '" << parser->element_name() << "'");
   return false;
 }
@@ -1164,8 +1165,6 @@
             current_policies |= OverlayableItem::Policy::kPublic;
           } else if (trimmed_part == "product") {
             current_policies |= OverlayableItem::Policy::kProduct;
-          } else if (trimmed_part == "product_services") {
-            current_policies |= OverlayableItem::Policy::kProductServices;
           } else if (trimmed_part == "system") {
             current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index debca9c..827c7de 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -935,9 +935,6 @@
         <policy type="product">
           <item type="string" name="bar" />
         </policy>
-        <policy type="product_services">
-          <item type="string" name="baz" />
-        </policy>
         <policy type="system">
           <item type="string" name="fiz" />
         </policy>
@@ -966,14 +963,6 @@
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
-  search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  result_overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices));
-
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
@@ -1028,7 +1017,7 @@
 TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
   std::string input = R"(
       <overlayable name="Name">
-        <policy type="vendor|product_services">
+        <policy type="vendor|public">
           <item type="string" name="foo" />
         </policy>
         <policy type="product|system">
@@ -1044,7 +1033,7 @@
   OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
-                                                   | OverlayableItem::Policy::kProductServices));
+                                                   | OverlayableItem::Policy::kPublic));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
@@ -1139,7 +1128,7 @@
   std::string input = R"(
       <overlayable name="Name">
         <policy type="vendor|product">
-          <policy type="product_services">
+          <policy type="public">
             <item type="string" name="foo" />
           </policy>
         </policy>
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index eaf6a47..7ca99ea 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -92,9 +92,6 @@
 
     // The resource can be overlaid by any overlay on the product partition.
     kProduct = 0x08,
-
-    // The resource can be overlaid by any overlay on the product services partition.
-    kProductServices = 0x10
   };
 
   std::shared_ptr<Overlayable> overlayable;
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index a733134..b97dc6b 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -248,7 +248,7 @@
                                                    Source("res/values/overlayable.xml", 40));
   OverlayableItem overlayable_item(overlayable);
   overlayable_item.policies |= OverlayableItem::Policy::kProduct;
-  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item.policies |= OverlayableItem::Policy::kVendor;
   overlayable_item.comment = "comment";
   overlayable_item.source = Source("res/values/overlayable.xml", 42);
 
@@ -265,7 +265,7 @@
   EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
   EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
-                                                   | OverlayableItem::Policy::kProductServices));
+                                                   | OverlayableItem::Policy::kVendor));
   ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
   EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
   EXPECT_THAT(result_overlayable_item.source.line, 42);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index c6f9152..ab4805f 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -16,6 +16,7 @@
 
 #include "ResourceUtils.h"
 
+#include <algorithm>
 #include <sstream>
 
 #include "android-base/stringprintf.h"
@@ -503,6 +504,14 @@
   if (entry.first == trimmed_str) {
     return entry.second;
   }
+
+  // Try parsing codename from "[codename].[preview_sdk_fingerprint]" value.
+  const StringPiece::const_iterator begin = std::begin(trimmed_str);
+  const StringPiece::const_iterator end = std::end(trimmed_str);
+  const StringPiece::const_iterator codename_end = std::find(begin, end, '.');
+  if (codename_end != end && entry.first == trimmed_str.substr(begin, codename_end)) {
+    return entry.second;
+  }
   return {};
 }
 
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 5ce4640..9018b0f 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -16,6 +16,7 @@
 
 #include "ResourceUtils.h"
 
+#include "SdkConstants.h"
 #include "Resource.h"
 #include "test/Test.h"
 
@@ -212,6 +213,17 @@
               Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened))));
 }
 
+TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) {
+  const android::StringPiece codename =
+      GetDevelopmentSdkCodeNameAndVersion().first;
+  const int version = GetDevelopmentSdkCodeNameAndVersion().second;
+
+  EXPECT_THAT(ResourceUtils::ParseSdkVersion(codename), Eq(Maybe<int>(version)));
+  EXPECT_THAT(
+      ResourceUtils::ParseSdkVersion(codename.to_string() + ".fingerprint"),
+      Eq(Maybe<int>(version)));
+}
+
 TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
   EXPECT_THAT(ResourceUtils::StringBuilder()
                   .AppendText("    hey guys ")
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index da541be..73b568e 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -155,7 +155,6 @@
     SYSTEM = 1;
     VENDOR = 2;
     PRODUCT = 3;
-    PRODUCT_SERVICES = 4;
   }
 
   // The location of the <item> declaration in source.
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 52375a3..92beb4e 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -701,7 +701,10 @@
     }
 
     const std::string out_path = BuildIntermediateContainerFilename(path_data);
-    error |= !compile_func(context, options, path_data, file, output_writer, out_path);
+    if (!compile_func(context, options, path_data, file, output_writer, out_path)) {
+      context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "file failed to compile");
+      error = true;
+    }
   }
 
   return error ? 1 : 0;
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 328b0be..2e6af18 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -41,6 +41,7 @@
 #include "optimize/MultiApkGenerator.h"
 #include "optimize/ResourceDeduper.h"
 #include "optimize/ResourceFilter.h"
+#include "optimize/ResourcePathShortener.h"
 #include "optimize/VersionCollapser.h"
 #include "split/TableSplitter.h"
 #include "util/Files.h"
@@ -52,6 +53,7 @@
 using ::android::ResTable_config;
 using ::android::StringPiece;
 using ::android::base::ReadFileToString;
+using ::android::base::WriteStringToFile;
 using ::android::base::StringAppendF;
 using ::android::base::StringPrintf;
 
@@ -143,6 +145,21 @@
       return 1;
     }
 
+    if (options_.shorten_resource_paths) {
+      ResourcePathShortener shortener(options_.table_flattener_options.shortened_path_map);
+      if (!shortener.Consume(context_, apk->GetResourceTable())) {
+        context_->GetDiagnostics()->Error(DiagMessage() << "failed shortening resource paths");
+        return 1;
+      }
+      if (options_.shortened_paths_map_path
+          && !WriteShortenedPathsMap(options_.table_flattener_options.shortened_path_map,
+                                      options_.shortened_paths_map_path.value())) {
+        context_->GetDiagnostics()->Error(DiagMessage()
+                                          << "failed to write shortened resource paths to file");
+        return 1;
+      }
+    }
+
     // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
     // equal to the minSdk.
     options_.split_constraints =
@@ -264,6 +281,15 @@
                                         ArchiveEntry::kAlign, writer);
   }
 
+  bool WriteShortenedPathsMap(const std::map<std::string, std::string> &path_map,
+                               const std::string &file_path) {
+    std::stringstream ss;
+    for (auto it = path_map.cbegin(); it != path_map.cend(); ++it) {
+      ss << it->first << " -> " << it->second << "\n";
+    }
+    return WriteStringToFile(ss.str(), file_path);
+  }
+
   OptimizeOptions options_;
   OptimizeContext* context_;
 };
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index d07305b..7f4a3ed 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -55,6 +55,12 @@
   // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
   // are kept and will be written as output.
   std::unordered_set<std::string> kept_artifacts;
+
+  // Whether or not to shorten resource paths in the APK.
+  bool shorten_resource_paths;
+
+  // Path to the output map of original resource paths to shortened paths.
+  Maybe<std::string> shortened_paths_map_path;
 };
 
 class OptimizeCommand : public Command {
@@ -101,6 +107,12 @@
     AddOptionalSwitch("--enable-resource-obfuscation",
         "Enables obfuscation of key string pool to single value",
         &options_.table_flattener_options.collapse_key_stringpool);
+    AddOptionalSwitch("--enable-resource-path-shortening",
+        "Enables shortening of the path of the resources inside the APK.",
+        &options_.shorten_resource_paths);
+    AddOptionalFlag("--resource-path-shortening-map",
+        "Path to output the map of old resource paths to shortened paths.",
+        &options_.shortened_paths_map_path);
     AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
   }
 
@@ -109,6 +121,9 @@
  private:
   OptimizeOptions options_;
 
+  bool WriteObfuscatedPathsMap(const std::map<std::string, std::string> &path_map,
+                               const std::string &file_path);
+
   Maybe<std::string> config_path_;
   Maybe<std::string> whitelist_path_;
   Maybe<std::string> resources_config_path_;
@@ -122,4 +137,4 @@
 
 }// namespace aapt
 
-#endif //AAPT2_OPTIMIZE_H
\ No newline at end of file
+#endif //AAPT2_OPTIMIZE_H
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index c496ff0..40aaa05 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -42,6 +42,19 @@
 
 namespace {
 
+static std::u16string strcpy16_dtoh(const char16_t* src, size_t len) {
+  size_t utf16_len = strnlen16(src, len);
+  if (utf16_len == 0) {
+    return {};
+  }
+  std::u16string dst;
+  dst.resize(utf16_len);
+  for (size_t i = 0; i < utf16_len; i++) {
+    dst[i] = util::DeviceToHost16(src[i]);
+  }
+  return dst;
+}
+
 // Visitor that converts a reference's resource ID to a resource name, given a mapping from
 // resource ID to resource name.
 class ReferenceIdToNameVisitor : public DescendingValueVisitor {
@@ -176,12 +189,8 @@
   }
 
   // Extract the package name.
-  size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name));
-  std::u16string package_name;
-  package_name.resize(len);
-  for (size_t i = 0; i < len; i++) {
-    package_name[i] = util::DeviceToHost16(package_header->name[i]);
-  }
+  std::u16string package_name = strcpy16_dtoh((const char16_t*)package_header->name,
+                                              arraysize(package_header->name));
 
   ResourceTablePackage* package =
       table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
@@ -435,6 +444,11 @@
   }
 
   auto overlayable = std::make_shared<Overlayable>();
+  overlayable->name = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->name,
+                                                      arraysize(header->name)));
+  overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor,
+                                                       arraysize(header->name)));
+  overlayable->source = source_.WithLine(0);
 
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
@@ -459,10 +473,6 @@
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
         policies |= OverlayableItem::Policy::kProduct;
       }
-      if (policy_header->policy_flags
-          & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
-        policies |= OverlayableItem::Policy::kProductServices;
-      }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
           ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 931d57b..9d341cc 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -217,9 +217,10 @@
   size_t entry_count_ = 0;
 };
 
-struct PolicyChunk {
-  uint32_t policy_flags;
-  std::set<ResourceId> ids;
+struct OverlayableChunk {
+  std::string actor;
+  Source source;
+  std::map<OverlayableItem::PolicyFlags, std::set<ResourceId>> policy_ids;
 };
 
 class PackageFlattener {
@@ -421,8 +422,9 @@
     return sorted_entries;
   }
 
-  void FlattenOverlayable(BigBuffer* buffer) {
-    std::vector<PolicyChunk> policies;
+  bool FlattenOverlayable(BigBuffer* buffer) {
+    std::set<ResourceId> seen_ids;
+    std::map<std::string, OverlayableChunk> overlayable_chunks;
 
     CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
     for (auto& type : package_->types) {
@@ -433,79 +435,116 @@
           continue;
         }
 
-        OverlayableItem& overlayable = entry->overlayable_item.value();
-        uint32_t policy_flags = OverlayableItem::Policy::kNone;
-        if (overlayable.policies & OverlayableItem::Policy::kPublic) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-        }
-        if (overlayable.policies & OverlayableItem::Policy::kSystem) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
-        }
-        if (overlayable.policies & OverlayableItem::Policy::kVendor) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
-        }
-        if (overlayable.policies & OverlayableItem::Policy::kProduct) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
-        }
-        if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+        OverlayableItem& item = entry->overlayable_item.value();
+
+        // Resource ids should only appear once in the resource table
+        ResourceId id = android::make_resid(package_->id.value(), type->id.value(),
+                                            entry->id.value());
+        CHECK(seen_ids.find(id) == seen_ids.end())
+            << "multiple overlayable definitions found for resource "
+            << ResourceName(package_->name, type->type, entry->name).to_string();
+        seen_ids.insert(id);
+
+        // Find the overlayable chunk with the specified name
+        OverlayableChunk* overlayable_chunk = nullptr;
+        auto iter = overlayable_chunks.find(item.overlayable->name);
+        if (iter == overlayable_chunks.end()) {
+          OverlayableChunk chunk{item.overlayable->actor, item.overlayable->source};
+          overlayable_chunk =
+              &overlayable_chunks.insert({item.overlayable->name, chunk}).first->second;
+        } else {
+          OverlayableChunk& chunk = iter->second;
+          if (!(chunk.source == item.overlayable->source)) {
+            // The name of an overlayable set of resources must be unique
+            context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
+                                                  << "duplicate overlayable name"
+                                                  << item.overlayable->name << "'");
+            context_->GetDiagnostics()->Error(DiagMessage(chunk.source)
+                                                  << "previous declaration here");
+            return false;
+          }
+
+          CHECK(chunk.actor == item.overlayable->actor);
+          overlayable_chunk = &chunk;
         }
 
-        if (overlayable.policies == OverlayableItem::Policy::kNone) {
+        uint32_t policy_flags = 0;
+        if (item.policies == OverlayableItem::Policy::kNone) {
           // Encode overlayable entries defined without a policy as publicly overlayable
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-        }
-
-        // Find the overlayable policy chunk with the same policies as the entry
-        PolicyChunk* policy_chunk = nullptr;
-        for (PolicyChunk& policy : policies) {
-          if (policy.policy_flags == policy_flags) {
-            policy_chunk = &policy;
-            break;
+        } else {
+          if (item.policies & OverlayableItem::Policy::kPublic) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+          }
+          if (item.policies & OverlayableItem::Policy::kSystem) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+          }
+          if (item.policies & OverlayableItem::Policy::kVendor) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+          }
+          if (item.policies & OverlayableItem::Policy::kProduct) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
           }
         }
 
-        // Create a new policy chunk if an existing one with the same policy cannot be found
-        if (policy_chunk == nullptr) {
-          PolicyChunk p;
-          p.policy_flags = policy_flags;
-          policies.push_back(p);
-          policy_chunk = &policies.back();
+        auto policy = overlayable_chunk->policy_ids.find(policy_flags);
+        if (policy != overlayable_chunk->policy_ids.end()) {
+          policy->second.insert(id);
+        } else {
+          overlayable_chunk->policy_ids.insert(
+              std::make_pair(policy_flags, std::set<ResourceId>{id}));
         }
-
-        policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
-                                                     entry->id.value()));
       }
     }
 
-    if (policies.empty()) {
-      // Only write the overlayable chunk if the APK has overlayable entries
-      return;
-    }
+    for (auto& overlayable_pair : overlayable_chunks) {
+      std::string name = overlayable_pair.first;
+      OverlayableChunk& overlayable = overlayable_pair.second;
 
-    ChunkWriter writer(buffer);
-    writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
-
-    // Write each policy block for the overlayable
-    for (PolicyChunk& policy : policies) {
-      ChunkWriter policy_writer(buffer);
-      ResTable_overlayable_policy_header* policy_type =
-          policy_writer.StartChunk<ResTable_overlayable_policy_header>(
-              RES_TABLE_OVERLAYABLE_POLICY_TYPE);
-      policy_type->policy_flags = util::HostToDevice32(policy.policy_flags);
-      policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size()));
-
-      // Write the ids after the policy header
-      ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size());
-      for (const ResourceId& id : policy.ids) {
-        id_block->ident = util::HostToDevice32(id.id);
-        id_block++;
+      // Write the header of the overlayable chunk
+      ChunkWriter overlayable_writer(buffer);
+      auto* overlayable_type =
+          overlayable_writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
+      if (name.size() >= arraysize(overlayable_type->name)) {
+        diag_->Error(DiagMessage() << "overlayable name '" << name
+                                   << "' exceeds maximum length ("
+                                   << arraysize(overlayable_type->name)
+                                   << " utf16 characters)");
+        return false;
       }
+      strcpy16_htod(overlayable_type->name, arraysize(overlayable_type->name),
+                    util::Utf8ToUtf16(name));
 
-      policy_writer.Finish();
+      if (overlayable.actor.size() >= arraysize(overlayable_type->actor)) {
+        diag_->Error(DiagMessage() << "overlayable name '" << overlayable.actor
+                                   << "' exceeds maximum length ("
+                                   << arraysize(overlayable_type->actor)
+                                   << " utf16 characters)");
+        return false;
+      }
+      strcpy16_htod(overlayable_type->actor, arraysize(overlayable_type->actor),
+                    util::Utf8ToUtf16(overlayable.actor));
+
+      // Write each policy block for the overlayable
+      for (auto& policy_ids : overlayable.policy_ids) {
+        ChunkWriter policy_writer(buffer);
+        auto* policy_type = policy_writer.StartChunk<ResTable_overlayable_policy_header>(
+            RES_TABLE_OVERLAYABLE_POLICY_TYPE);
+        policy_type->policy_flags = util::HostToDevice32(static_cast<uint32_t>(policy_ids.first));
+        policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(
+                                                            policy_ids.second.size()));
+        // Write the ids after the policy header
+        auto* id_block = policy_writer.NextBlock<ResTable_ref>(policy_ids.second.size());
+        for (const ResourceId& id : policy_ids.second) {
+          id_block->ident = util::HostToDevice32(id.id);
+          id_block++;
+        }
+        policy_writer.Finish();
+      }
+      overlayable_writer.Finish();
     }
 
-    writer.Finish();
+    return true;
   }
 
   bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 635cb21..71330e3 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -46,6 +46,9 @@
 
   // When true, sort the entries in the values string pool by priority and configuration.
   bool sort_stringpool_entries = true;
+
+  // Map from original resource paths to shortened resource paths.
+  std::map<std::string, std::string> shortened_path_map;
 };
 
 class TableFlattener : public IResourceTableConsumer {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index a5fb6fd..ddc1173 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -657,36 +657,34 @@
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
   auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
   std::string name_zero = "com.app.test:integer/overlayable_zero_item";
-  OverlayableItem overlayable_zero_item(overlayable);
-  overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct;
-  overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem;
-  overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices;
+  OverlayableItem overlayable_item_zero(overlayable);
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
 
   std::string name_one = "com.app.test:integer/overlayable_one_item";
-  OverlayableItem overlayable_one_item(overlayable);
-  overlayable_one_item.policies |= OverlayableItem::Policy::kPublic;
-  overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices;
+  OverlayableItem overlayable_item_one(overlayable);
+  overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
 
   std::string name_two = "com.app.test:integer/overlayable_two_item";
-  OverlayableItem overlayable_two_item(overlayable);
-  overlayable_two_item.policies |= OverlayableItem::Policy::kProduct;
-  overlayable_two_item.policies |= OverlayableItem::Policy::kSystem;
-  overlayable_two_item.policies |= OverlayableItem::Policy::kVendor;
+  OverlayableItem overlayable_item_two(overlayable);
+  overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
 
   std::string name_three = "com.app.test:integer/overlayable_three_item";
-  OverlayableItem overlayable_three_item(overlayable);
+  OverlayableItem overlayable_item_three(overlayable);
 
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name_zero, ResourceId(0x7f020000))
-          .SetOverlayable(name_zero, overlayable_zero_item)
+          .SetOverlayable(name_zero, overlayable_item_zero)
           .AddSimple(name_one, ResourceId(0x7f020001))
-          .SetOverlayable(name_one, overlayable_one_item)
+          .SetOverlayable(name_one, overlayable_item_one)
           .AddSimple(name_two, ResourceId(0x7f020002))
-          .SetOverlayable(name_two, overlayable_two_item)
+          .SetOverlayable(name_two, overlayable_item_two)
           .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_three_item)
+          .SetOverlayable(name_three, overlayable_item_three)
           .Build();
 
   ResourceTable output_table;
@@ -698,16 +696,14 @@
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
-                                       | OverlayableItem::Policy::kProduct
-                                       | OverlayableItem::Policy::kProductServices);
+                                       | OverlayableItem::Policy::kProduct);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic
-                                       | OverlayableItem::Policy::kProductServices);
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
   ASSERT_TRUE(search_result);
@@ -724,6 +720,80 @@
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
+  EXPECT_EQ(overlayable_item.overlayable->name, "TestName");
+  EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme");
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
+}
+
+TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
+  auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  OverlayableItem overlayable_item_zero(group);
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+
+  auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization");
+  std::string name_one = "com.app.test:integer/overlayable_one";
+  OverlayableItem overlayable_item_one(group_one);
+  overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+
+  std::string name_two = "com.app.test:integer/overlayable_two";
+  OverlayableItem overlayable_item_two(group);
+  overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+
+  std::string name_three = "com.app.test:integer/overlayable_three";
+  OverlayableItem overlayable_item_three(group_one);
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .SetOverlayable(name_zero, overlayable_item_zero)
+          .AddSimple(name_one, ResourceId(0x7f020001))
+          .SetOverlayable(name_one, overlayable_item_one)
+          .AddSimple(name_two, ResourceId(0x7f020002))
+          .SetOverlayable(name_two, overlayable_item_two)
+          .AddSimple(name_three, ResourceId(0x7f020003))
+          .SetOverlayable(name_three, overlayable_item_three)
+          .Build();
+  ResourceTable output_table;
+  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
+  auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem
+                                         | OverlayableItem::Policy::kProduct);
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem
+                                         | OverlayableItem::Policy::kProduct
+                                         | OverlayableItem::Policy::kVendor);
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 6b5746d..aff1b39 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -390,9 +390,6 @@
       case pb::OverlayableItem::PRODUCT:
         out_overlayable->policies |= OverlayableItem::Policy::kProduct;
         break;
-      case pb::OverlayableItem::PRODUCT_SERVICES:
-        out_overlayable->policies |= OverlayableItem::Policy::kProductServices;
-        break;
       default:
         *out_error = "unknown overlayable policy";
         return false;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 76fbb46..b549e23 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -303,9 +303,6 @@
   if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
     pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
   }
-  if (overlayable_item.policies & OverlayableItem::Policy::kProductServices) {
-    pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT_SERVICES);
-  }
   if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
     pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
   }
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 4a3c1b8..cce3939 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -519,7 +519,7 @@
 
   OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
       "TaskBar", "overlay://theme"));
-  overlayable_item_bar.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item_bar.policies |= OverlayableItem::Policy::kPublic;
   overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
 
   OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
@@ -565,7 +565,7 @@
   overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
   EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
                                               | OverlayableItem::Policy::kVendor));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 85bf6f2..5812ec4 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -213,6 +213,27 @@
   return true;
 }
 
+static bool AddDeprecatedUsesFeatures(xml::Element* el, SourcePathDiagnostics* diag) {
+  if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) {
+    if (attr->value.empty()) {
+      return true;
+    }
+
+    // Add "android.hardware.fingerprint" when "android.hardware.biometric.fingerprint" is found,
+    // since the former is deprecated in Q and the latter is not present pre-Q. (see b/115639644)
+    if (attr->value == "android.hardware.biometrics.fingerprint") {
+      auto element = el->CloneElement([&](const xml::Element& el, xml::Element* out_el) {
+        xml::Attribute* cloned_attr = out_el->FindOrCreateAttribute(xml::kSchemaAndroid, "name");
+        cloned_attr->value = "android.hardware.fingerprint";
+      });
+
+      el->parent->AppendChild(std::move(element));
+    }
+  }
+
+  return true;
+}
+
 bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
                                IDiagnostics* diag) {
   // First verify some options.
@@ -247,6 +268,7 @@
   // Common <uses-feature> actions.
   xml::XmlNodeAction uses_feature_action;
   uses_feature_action.Action(VerifyUsesFeature);
+  uses_feature_action.Action(AddDeprecatedUsesFeatures);
 
   // Common component actions.
   xml::XmlNodeAction component_action;
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index adea627..fcc9f55 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -832,4 +832,36 @@
   EXPECT_THAT(Verify(input), NotNull());
 }
 
+TEST_F(ManifestFixerTest, UsesFeatureAddDeprecated) {
+  std::string input = R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android">
+        <uses-feature android:name="android.hardware.biometrics.fingerprint" />
+        <feature-group>
+          <uses-feature android:name="android.hardware.biometrics.fingerprint" />
+        </feature-group>
+      </manifest>)";
+
+  std::unique_ptr<xml::XmlResource> manifest = Verify(input);
+  ASSERT_THAT(manifest, NotNull());
+  EXPECT_THAT(manifest->root->FindChildWithAttribute("", "uses-feature",
+                                                     xml::kSchemaAndroid, "name",
+                                                     "android.hardware.biometrics.fingerprint"),
+              Ne(nullptr));
+  EXPECT_THAT(manifest->root->FindChildWithAttribute("", "uses-feature",
+                                                     xml::kSchemaAndroid, "name",
+                                                     "android.hardware.fingerprint"),
+              Ne(nullptr));
+
+  xml::Element* feature_group = manifest->root->FindChild("", "feature-group");
+  ASSERT_THAT(feature_group, Ne(nullptr));
+
+  EXPECT_THAT(feature_group->FindChildWithAttribute("", "uses-feature", xml::kSchemaAndroid, "name",
+                                                    "android.hardware.biometrics.fingerprint"),
+              Ne(nullptr));
+  EXPECT_THAT(feature_group->FindChildWithAttribute("", "uses-feature", xml::kSchemaAndroid, "name",
+                                                    "android.hardware.fingerprint"),
+              Ne(nullptr));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 921d634..ad3674e 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -484,7 +484,7 @@
 
   OverlayableItem overlayable_item(overlayable);
   overlayable_item.policies |= OverlayableItem::Policy::kPublic;
-  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item.policies |= OverlayableItem::Policy::kSystem;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
@@ -506,7 +506,7 @@
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
-                                                   | OverlayableItem::Policy::kProductServices));
+                                                   | OverlayableItem::Policy::kSystem));
 }
 
 TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
diff --git a/tools/aapt2/optimize/ResourcePathShortener.cpp b/tools/aapt2/optimize/ResourcePathShortener.cpp
new file mode 100644
index 0000000..c5df3dd
--- /dev/null
+++ b/tools/aapt2/optimize/ResourcePathShortener.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "optimize/ResourcePathShortener.h"
+
+#include <math.h>
+#include <unordered_set>
+
+#include "androidfw/StringPiece.h"
+
+#include "ResourceTable.h"
+#include "ValueVisitor.h"
+
+
+static const std::string base64_chars =
+             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+             "abcdefghijklmnopqrstuvwxyz"
+             "0123456789-_";
+
+namespace aapt {
+
+ResourcePathShortener::ResourcePathShortener(
+    std::map<std::string, std::string>& path_map_out)
+    : path_map_(path_map_out) {
+}
+
+std::string ShortenFileName(const android::StringPiece& file_path, int output_length) {
+  std::size_t hash_num = std::hash<android::StringPiece>{}(file_path);
+  std::string result = "";
+  // Convert to (modified) base64 so that it is a proper file path.
+  for (int i = 0; i < output_length; i++) {
+    uint8_t sextet = hash_num & 0x3f;
+    hash_num >>= 6;
+    result += base64_chars[sextet];
+  }
+  return result;
+}
+
+
+// Calculate the optimal hash length such that an average of 10% of resources
+// collide in their shortened path.
+// Reference: http://matt.might.net/articles/counting-hash-collisions/
+int OptimalShortenedLength(int num_resources) {
+  int num_chars = 2;
+  double N = 64*64; // hash space when hash is 2 chars long
+  double max_collisions = num_resources * 0.1;
+  while (num_resources - N + N * pow((N - 1) / N, num_resources) > max_collisions) {
+    N *= 64;
+    num_chars++;
+  }
+  return num_chars;
+}
+
+std::string GetShortenedPath(const android::StringPiece& shortened_filename,
+    const android::StringPiece& extension, int collision_count) {
+  std::string shortened_path = "res/" + shortened_filename.to_string();
+  if (collision_count > 0) {
+    shortened_path += std::to_string(collision_count);
+  }
+  shortened_path += extension;
+  return shortened_path;
+}
+
+bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table) {
+  // used to detect collisions
+  std::unordered_set<std::string> shortened_paths;
+  std::unordered_set<FileReference*> file_refs;
+  for (auto& package : table->packages) {
+    for (auto& type : package->types) {
+      for (auto& entry : type->entries) {
+        for (auto& config_value : entry->values) {
+          FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
+          if (file_ref) {
+            file_refs.insert(file_ref);
+          }
+        }
+      }
+    }
+  }
+  int num_chars = OptimalShortenedLength(file_refs.size());
+  for (auto& file_ref : file_refs) {
+    android::StringPiece res_subdir, actual_filename, extension;
+    util::ExtractResFilePathParts(*file_ref->path, &res_subdir, &actual_filename, &extension);
+
+    std::string shortened_filename = ShortenFileName(*file_ref->path, num_chars);
+    int collision_count = 0;
+    std::string shortened_path = GetShortenedPath(shortened_filename, extension, collision_count);
+    while (shortened_paths.find(shortened_path) != shortened_paths.end()) {
+      collision_count++;
+      shortened_path = GetShortenedPath(shortened_filename, extension, collision_count);
+    }
+    shortened_paths.insert(shortened_path);
+    path_map_.insert({*file_ref->path, shortened_path});
+    file_ref->path = table->string_pool.MakeRef(shortened_path, file_ref->path.GetContext());
+  }
+  return true;
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/optimize/ResourcePathShortener.h b/tools/aapt2/optimize/ResourcePathShortener.h
new file mode 100644
index 0000000..f1074ef
--- /dev/null
+++ b/tools/aapt2/optimize/ResourcePathShortener.h
@@ -0,0 +1,44 @@
+/*
+ * 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 AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
+#define AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
+
+#include <map>
+
+#include "android-base/macros.h"
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+class ResourceTable;
+
+// Maps resources in the apk to shortened paths.
+class ResourcePathShortener : public IResourceTableConsumer {
+ public:
+  explicit ResourcePathShortener(std::map<std::string, std::string>& path_map_out);
+
+  bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ResourcePathShortener);
+  std::map<std::string, std::string>& path_map_;
+};
+
+} // namespace aapt
+
+#endif  // AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
diff --git a/tools/aapt2/optimize/ResourcePathShortener_test.cpp b/tools/aapt2/optimize/ResourcePathShortener_test.cpp
new file mode 100644
index 0000000..88cadc7
--- /dev/null
+++ b/tools/aapt2/optimize/ResourcePathShortener_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "optimize/ResourcePathShortener.h"
+
+#include "ResourceTable.h"
+#include "test/Test.h"
+
+using ::aapt::test::GetValue;
+using ::testing::Not;
+using ::testing::NotNull;
+using ::testing::Eq;
+
+namespace aapt {
+
+TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddFileReference("android:drawable/xmlfile", "res/drawables/xmlfile.xml")
+          .AddFileReference("android:drawable/xmlfile2", "res/drawables/xmlfile2.xml")
+          .AddString("android:string/string", "res/should/still/be/the/same.png")
+          .Build();
+
+  std::map<std::string, std::string> path_map;
+  ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
+
+  // Expect that the path map is populated
+  ASSERT_THAT(path_map.find("res/drawables/xmlfile.xml"), Not(Eq(path_map.end())));
+  ASSERT_THAT(path_map.find("res/drawables/xmlfile2.xml"), Not(Eq(path_map.end())));
+
+  // The file paths were changed
+  EXPECT_THAT(path_map.at("res/drawables/xmlfile.xml"), Not(Eq("res/drawables/xmlfile.xml")));
+  EXPECT_THAT(path_map.at("res/drawables/xmlfile2.xml"), Not(Eq("res/drawables/xmlfile2.xml")));
+
+  // Different file paths should remain different
+  EXPECT_THAT(path_map["res/drawables/xmlfile.xml"],
+              Not(Eq(path_map["res/drawables/xmlfile2.xml"])));
+
+  FileReference* ref =
+      GetValue<FileReference>(table.get(), "android:drawable/xmlfile");
+  ASSERT_THAT(ref, NotNull());
+  // The map correctly points to the new location of the file
+  EXPECT_THAT(path_map["res/drawables/xmlfile.xml"], Eq(*ref->path));
+
+  // Strings should not be affected, only file paths
+  EXPECT_THAT(
+      *GetValue<String>(table.get(), "android:string/string")->value,
+              Eq("res/should/still/be/the/same.png"));
+  EXPECT_THAT(path_map.find("res/should/still/be/the/same.png"), Eq(path_map.end()));
+}
+
+}   // namespace aapt
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 6476abd..d1fe43e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -26,7 +26,7 @@
 $ apilint.py /tmp/currentblame.txt previous.txt --no-color
 """
 
-import re, sys, collections, traceback, argparse
+import re, sys, collections, traceback, argparse, itertools
 
 
 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
@@ -50,45 +50,37 @@
     return "\033[%sm" % (";".join(codes))
 
 
-def ident(raw):
-    """Strips superficial signature changes, giving us a strong key that
-    can be used to identify members across API levels."""
-    raw = raw.replace(" deprecated ", " ")
-    raw = raw.replace(" synchronized ", " ")
-    raw = raw.replace(" final ", " ")
-    raw = re.sub("<.+?>", "", raw)
-    if " throws " in raw:
-        raw = raw[:raw.index(" throws ")]
-    return raw
-
-
 class Field():
-    def __init__(self, clazz, line, raw, blame):
+    def __init__(self, clazz, line, raw, blame, sig_format = 1):
         self.clazz = clazz
         self.line = line
         self.raw = raw.strip(" {;")
         self.blame = blame
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_field(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        raw = raw.split()
-        self.split = list(raw)
+            raw = raw.split()
+            self.split = list(raw)
 
-        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
-            while r in raw: raw.remove(r)
+            for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
+                while r in raw: raw.remove(r)
 
-        # ignore annotations for now
-        raw = [ r for r in raw if not r.startswith("@") ]
+            # ignore annotations for now
+            raw = [ r for r in raw if not r.startswith("@") ]
 
-        self.typ = raw[0]
-        self.name = raw[1].strip(";")
-        if len(raw) >= 4 and raw[2] == "=":
-            self.value = raw[3].strip(';"')
-        else:
-            self.value = None
-        self.ident = ident(self.raw)
+            self.typ = raw[0]
+            self.name = raw[1].strip(";")
+            if len(raw) >= 4 and raw[2] == "=":
+                self.value = raw[3].strip(';"')
+            else:
+                self.value = None
+
+        self.ident = "-".join((self.typ, self.name, self.value or ""))
 
     def __hash__(self):
         return hash(self.raw)
@@ -96,48 +88,55 @@
     def __repr__(self):
         return self.raw
 
-
 class Method():
-    def __init__(self, clazz, line, raw, blame):
+    def __init__(self, clazz, line, raw, blame, sig_format = 1):
         self.clazz = clazz
         self.line = line
         self.raw = raw.strip(" {;")
         self.blame = blame
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_method(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        # handle each clause differently
-        raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups()
+            # handle each clause differently
+            raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups()
 
-        # parse prefixes
-        raw = re.split("[\s]+", raw_prefix)
-        for r in ["", ";"]:
-            while r in raw: raw.remove(r)
-        self.split = list(raw)
+            # parse prefixes
+            raw = re.split("[\s]+", raw_prefix)
+            for r in ["", ";"]:
+                while r in raw: raw.remove(r)
+            self.split = list(raw)
 
-        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator"]:
-            while r in raw: raw.remove(r)
+            for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator", "synchronized"]:
+                while r in raw: raw.remove(r)
 
-        self.typ = raw[0]
-        self.name = raw[1]
+            self.typ = raw[0]
+            self.name = raw[1]
 
-        # parse args
-        self.args = []
-        for arg in re.split(",\s*", raw_args):
-            arg = re.split("\s", arg)
-            # ignore annotations for now
-            arg = [ a for a in arg if not a.startswith("@") ]
-            if len(arg[0]) > 0:
-                self.args.append(arg[0])
+            # parse args
+            self.args = []
+            for arg in re.split(",\s*", raw_args):
+                arg = re.split("\s", arg)
+                # ignore annotations for now
+                arg = [ a for a in arg if not a.startswith("@") ]
+                if len(arg[0]) > 0:
+                    self.args.append(arg[0])
 
-        # parse throws
-        self.throws = []
-        for throw in re.split(",\s*", raw_throws):
-            self.throws.append(throw)
+            # parse throws
+            self.throws = []
+            for throw in re.split(",\s*", raw_throws):
+                self.throws.append(throw)
+        else:
+            raise ValueError("Unknown signature format: " + sig_format)
 
-        self.ident = ident(self.raw)
+        self.ident = "-".join((self.typ, self.name, "-".join(self.args)))
+
+    def sig_matches(self, typ, name, args):
+        return typ == self.typ and name == self.name and args == self.args
 
     def __hash__(self):
         return hash(self.raw)
@@ -147,7 +146,7 @@
 
 
 class Class():
-    def __init__(self, pkg, line, raw, blame):
+    def __init__(self, pkg, line, raw, blame, sig_format = 1):
         self.pkg = pkg
         self.line = line
         self.raw = raw.strip(" {;")
@@ -156,31 +155,44 @@
         self.fields = []
         self.methods = []
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_class(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        raw = raw.split()
-        self.split = list(raw)
-        if "class" in raw:
-            self.fullname = raw[raw.index("class")+1]
-        elif "interface" in raw:
-            self.fullname = raw[raw.index("interface")+1]
-        elif "@interface" in raw:
-            self.fullname = raw[raw.index("@interface")+1]
-        else:
-            raise ValueError("Funky class type %s" % (self.raw))
+            raw = raw.split()
+            self.split = list(raw)
+            if "class" in raw:
+                self.fullname = raw[raw.index("class")+1]
+            elif "interface" in raw:
+                self.fullname = raw[raw.index("interface")+1]
+            elif "@interface" in raw:
+                self.fullname = raw[raw.index("@interface")+1]
+            else:
+                raise ValueError("Funky class type %s" % (self.raw))
 
-        if "extends" in raw:
-            self.extends = raw[raw.index("extends")+1]
-            self.extends_path = self.extends.split(".")
+            if "extends" in raw:
+                self.extends = raw[raw.index("extends")+1]
+            else:
+                self.extends = None
+
+            if "implements" in raw:
+                self.implements = raw[raw.index("implements")+1]
+            else:
+                self.implements = None
         else:
-            self.extends = None
-            self.extends_path = []
+            raise ValueError("Unknown signature format: " + sig_format)
 
         self.fullname = self.pkg.name + "." + self.fullname
         self.fullname_path = self.fullname.split(".")
 
+        if self.extends is not None:
+            self.extends_path = self.extends.split(".")
+        else:
+            self.extends_path = []
+
         self.name = self.fullname[self.fullname.rindex(".")+1:]
 
     def merge_from(self, other):
@@ -208,6 +220,330 @@
     def __repr__(self):
         return self.raw
 
+class V2Tokenizer(object):
+    __slots__ = ["raw"]
+
+    DELIMITER = re.compile(r'\s+|[()@<>;,={}/"!?]|\[\]|\.\.\.')
+    STRING_SPECIAL = re.compile(r'["\\]')
+
+    def __init__(self, raw):
+        self.raw = raw
+
+    def tokenize(self):
+        tokens = []
+        current = 0
+        raw = self.raw
+        length = len(raw)
+
+        while current < length:
+            while current < length:
+                start = current
+                match = V2Tokenizer.DELIMITER.search(raw, start)
+                if match is not None:
+                    match_start = match.start()
+                    if match_start == current:
+                        end = match.end()
+                    else:
+                        end = match_start
+                else:
+                    end = length
+
+                token = raw[start:end]
+                current = end
+
+                if token == "" or token[0] == " ":
+                    continue
+                else:
+                    break
+
+            if token == "@":
+                if raw[start:start+11] == "@interface ":
+                    current = start + 11
+                    tokens.append("@interface")
+                    continue
+            elif token == '/':
+                if raw[start:start+2] == "//":
+                    current = length
+                    continue
+            elif token == '"':
+                current, string_token = self.tokenize_string(raw, length, current)
+                tokens.append(token + string_token)
+                continue
+
+            tokens.append(token)
+
+        return tokens
+
+    def tokenize_string(self, raw, length, current):
+        start = current
+        end = length
+        while start < end:
+            match = V2Tokenizer.STRING_SPECIAL.search(raw, start)
+            if match:
+                if match.group() == '"':
+                    end = match.end()
+                    break
+                elif match.group() == '\\':
+                    # ignore whatever is after the slash
+                    start += 2
+                else:
+                    raise ValueError("Unexpected match: `%s`" % (match.group()))
+            else:
+                raise ValueError("Unexpected EOF tokenizing string: `%s`" % (raw[current - 1:],))
+
+        token = raw[current:end]
+        return end, token
+
+class V2LineParser(object):
+    __slots__ = ["tokenized", "current", "len"]
+
+    MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized native operator sealed strictfp infix inline suspend vararg".split())
+    JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split())
+
+    def __init__(self, raw):
+        self.tokenized = V2Tokenizer(raw).tokenize()
+        self.current = 0
+        self.len = len(self.tokenized)
+
+    def parse_into_method(self, method):
+        method.split = []
+        kind = self.parse_one_of("ctor", "method")
+        method.split.append(kind)
+        annotations = self.parse_annotations()
+        method.split.extend(self.parse_modifiers())
+        self.parse_matching_paren("<", ">")
+        if "@Deprecated" in annotations:
+            method.split.append("deprecated")
+        if kind == "ctor":
+            method.typ = "ctor"
+        else:
+            method.typ = self.parse_type()
+            method.split.append(method.typ)
+        method.name = self.parse_name()
+        method.split.append(method.name)
+        self.parse_token("(")
+        method.args = self.parse_args()
+        self.parse_token(")")
+        method.throws = self.parse_throws()
+        if "@interface" in method.clazz.split:
+            self.parse_annotation_default()
+        self.parse_token(";")
+        self.parse_eof()
+
+    def parse_into_class(self, clazz):
+        clazz.split = []
+        annotations = self.parse_annotations()
+        if "@Deprecated" in annotations:
+            clazz.split.append("deprecated")
+        clazz.split.extend(self.parse_modifiers())
+        kind = self.parse_one_of("class", "interface", "@interface", "enum")
+        if kind == "enum":
+            # enums are implicitly final
+            clazz.split.append("final")
+        clazz.split.append(kind)
+        clazz.fullname = self.parse_name()
+        self.parse_matching_paren("<", ">")
+        extends = self.parse_extends()
+        clazz.extends = extends[0] if extends else None
+        implements = self.parse_implements()
+        clazz.implements = implements[0] if implements else None
+        # The checks assume that interfaces are always found in implements, which isn't true for
+        # subinterfaces.
+        if not implements and "interface" in clazz.split:
+            clazz.implements = clazz.extends
+        self.parse_token("{")
+        self.parse_eof()
+
+    def parse_into_field(self, field):
+        kind = self.parse_one_of("field", "property")
+        field.split = [kind]
+        annotations = self.parse_annotations()
+        if "@Deprecated" in annotations:
+            field.split.append("deprecated")
+        field.split.extend(self.parse_modifiers())
+        field.typ = self.parse_type()
+        field.split.append(field.typ)
+        field.name = self.parse_name()
+        field.split.append(field.name)
+        if self.parse_if("="):
+            field.value = self.parse_value_stripped()
+        else:
+            field.value = None
+
+        self.parse_token(";")
+        self.parse_eof()
+
+    def lookahead(self):
+        return self.tokenized[self.current]
+
+    def parse_one_of(self, *options):
+        found = self.lookahead()
+        if found not in options:
+            raise ValueError("Parsing failed, expected one of `%s` but found `%s` in %s" % (options, found, repr(self.tokenized)))
+        return self.parse_token()
+
+    def parse_token(self, tok = None):
+        found = self.lookahead()
+        if tok is not None and found != tok:
+            raise ValueError("Parsing failed, expected `%s` but found `%s` in %s" % (tok, found, repr(self.tokenized)))
+        self.current += 1
+        return found
+
+    def eof(self):
+        return self.current == self.len
+
+    def parse_eof(self):
+        if not self.eof():
+            raise ValueError("Parsing failed, expected EOF, but %s has not been parsed in %s" % (self.tokenized[self.current:], self.tokenized))
+
+    def parse_if(self, tok):
+        if not self.eof() and self.lookahead() == tok:
+            self.parse_token()
+            return True
+        return False
+
+    def parse_annotations(self):
+        ret = []
+        while self.lookahead() == "@":
+            ret.append(self.parse_annotation())
+        return ret
+
+    def parse_annotation(self):
+        ret = self.parse_token("@") + self.parse_token()
+        self.parse_matching_paren("(", ")")
+        return ret
+
+    def parse_matching_paren(self, open, close):
+        start = self.current
+        if not self.parse_if(open):
+            return
+        length = len(self.tokenized)
+        count = 1
+        while count > 0:
+            if self.current == length:
+                raise ValueError("Unexpected EOF looking for closing paren: `%s`" % (self.tokenized[start:],))
+            t = self.parse_token()
+            if t == open:
+                count += 1
+            elif t == close:
+                count -= 1
+        return self.tokenized[start:self.current]
+
+    def parse_modifiers(self):
+        ret = []
+        while self.lookahead() in V2LineParser.MODIFIERS:
+            ret.append(self.parse_token())
+        return ret
+
+    def parse_kotlin_nullability(self):
+        t = self.lookahead()
+        if t == "?" or t == "!":
+            return self.parse_token()
+        return None
+
+    def parse_type(self):
+        self.parse_annotations()
+        type = self.parse_token()
+        if type[-1] == '.':
+            self.parse_annotations()
+            type += self.parse_token()
+        if type in V2LineParser.JAVA_LANG_TYPES:
+            type = "java.lang." + type
+        self.parse_matching_paren("<", ">")
+        while True:
+            t = self.lookahead()
+            if t == "@":
+                self.parse_annotation()
+            elif t == "[]":
+                type += self.parse_token()
+            elif self.parse_kotlin_nullability() is not None:
+                pass  # discard nullability for now
+            else:
+                break
+        return type
+
+    def parse_arg_type(self):
+        type = self.parse_type()
+        if self.parse_if("..."):
+            type += "..."
+        self.parse_kotlin_nullability() # discard nullability for now
+        return type
+
+    def parse_name(self):
+        return self.parse_token()
+
+    def parse_args(self):
+        args = []
+        if self.lookahead() == ")":
+            return args
+
+        while True:
+            args.append(self.parse_arg())
+            if self.lookahead() == ")":
+                return args
+            self.parse_token(",")
+
+    def parse_arg(self):
+        self.parse_if("vararg")  # kotlin vararg
+        self.parse_annotations()
+        type = self.parse_arg_type()
+        l = self.lookahead()
+        if l != "," and l != ")":
+            if self.lookahead() != '=':
+                self.parse_token()  # kotlin argument name
+            if self.parse_if('='): # kotlin default value
+                self.parse_expression()
+        return type
+
+    def parse_expression(self):
+        while not self.lookahead() in [')', ',', ';']:
+            (self.parse_matching_paren('(', ')') or
+            self.parse_matching_paren('{', '}') or
+            self.parse_token())
+
+    def parse_throws(self):
+        ret = []
+        if self.parse_if("throws"):
+            ret.append(self.parse_type())
+            while self.parse_if(","):
+                ret.append(self.parse_type())
+        return ret
+
+    def parse_extends(self):
+        if self.parse_if("extends"):
+            return self.parse_space_delimited_type_list()
+        return []
+
+    def parse_implements(self):
+        if self.parse_if("implements"):
+            return self.parse_space_delimited_type_list()
+        return []
+
+    def parse_space_delimited_type_list(self, terminals = ["implements", "{"]):
+        types = []
+        while True:
+            types.append(self.parse_type())
+            if self.lookahead() in terminals:
+                return types
+
+    def parse_annotation_default(self):
+        if self.parse_if("default"):
+            self.parse_expression()
+
+    def parse_value(self):
+        if self.lookahead() == "{":
+            return " ".join(self.parse_matching_paren("{", "}"))
+        elif self.lookahead() == "(":
+            return " ".join(self.parse_matching_paren("(", ")"))
+        else:
+            return self.parse_token()
+
+    def parse_value_stripped(self):
+        value = self.parse_value()
+        if value[0] == '"':
+            return value[1:-1]
+        return value
+
 
 def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None,
                   in_classes_with_base=[]):
@@ -252,6 +588,7 @@
     pkg = None
     clazz = None
     blame = None
+    sig_format = 1
 
     re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
     for raw in f:
@@ -264,16 +601,18 @@
         else:
             blame = None
 
-        if raw.startswith("package"):
+        if line == 1 and raw == "// Signature format: 2.0":
+            sig_format = 2
+        elif raw.startswith("package"):
             pkg = Package(line, raw, blame)
         elif raw.startswith("  ") and raw.endswith("{"):
-            clazz = Class(pkg, line, raw, blame)
+            clazz = Class(pkg, line, raw, blame, sig_format=sig_format)
         elif raw.startswith("    ctor"):
-            clazz.ctors.append(Method(clazz, line, raw, blame))
+            clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("    method"):
-            clazz.methods.append(Method(clazz, line, raw, blame))
-        elif raw.startswith("    field"):
-            clazz.fields.append(Field(clazz, line, raw, blame))
+            clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format))
+        elif raw.startswith("    field") or raw.startswith("    property"):
+            clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("  }") and clazz:
             yield clazz
 
@@ -367,7 +706,7 @@
     """Records an API failure to be processed later."""
     global failures
 
-    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
+    sig = "%s-%s-%s" % (clazz.fullname, detail.ident if detail else None, msg)
     sig = sig.replace(" deprecated ", " ")
 
     failures[sig] = Failure(sig, clazz, detail, error, rule, msg)
@@ -408,7 +747,7 @@
 
 def verify_enums(clazz):
     """Enums are bad, mmkay?"""
-    if "extends java.lang.Enum" in clazz.raw:
+    if clazz.extends == "java.lang.Enum" or "enum" in clazz.split:
         error(clazz, None, "F5", "Enums are not allowed")
 
 
@@ -467,7 +806,7 @@
         interface OnFooListener { void onFoo() }"""
 
     if clazz.name.endswith("Listener"):
-        if " abstract class " in clazz.raw:
+        if "abstract" in clazz.split and "class" in clazz.split:
             error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback")
 
         for m in clazz.methods:
@@ -546,16 +885,16 @@
     eq = False
     hc = False
     for m in clazz.methods:
-        if " static " in m.raw: continue
-        if "boolean equals(java.lang.Object)" in m.raw: eq = True
-        if "int hashCode()" in m.raw: hc = True
+        if "static" in m.split: continue
+        if m.sig_matches("boolean", "equals", ["java.lang.Object"]): eq = True
+        if m.sig_matches("int", "hashCode", []): hc = True
     if eq != hc:
         error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
 
 
 def verify_parcelable(clazz):
     """Verify that Parcelable objects aren't hiding required bits."""
-    if "implements android.os.Parcelable" in clazz.raw:
+    if clazz.implements == "android.os.Parcelable":
         creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
         write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
         describe = [ i for i in clazz.methods if i.name == "describeContents" ]
@@ -563,8 +902,7 @@
         if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
             error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
 
-        if ((" final class " not in clazz.raw) and
-            (" final deprecated class " not in clazz.raw)):
+        if "final" not in clazz.split:
             error(clazz, None, "FW8", "Parcelable classes must be final")
 
         for c in clazz.ctors:
@@ -616,7 +954,7 @@
             else:
                 error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable")
 
-        if not "static" in f.split:
+        if "static" not in f.split and "property" not in f.split:
             if not re.match("[a-z]([a-zA-Z]+)?", f.name):
                 error(clazz, f, "S1", "Non-static fields must be named using myField style")
 
@@ -684,7 +1022,7 @@
     """Verify that helper classes are named consistently with what they extend.
     All developer extendable methods should be named onFoo()."""
     test_methods = False
-    if "extends android.app.Service" in clazz.raw:
+    if clazz.extends == "android.app.Service":
         test_methods = True
         if not clazz.name.endswith("Service"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooService")
@@ -696,7 +1034,7 @@
                 if f.value != clazz.fullname:
                     error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
 
-    if "extends android.content.ContentProvider" in clazz.raw:
+    if clazz.extends == "android.content.ContentProvider":
         test_methods = True
         if not clazz.name.endswith("Provider"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider")
@@ -708,12 +1046,12 @@
                 if f.value != clazz.fullname:
                     error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
 
-    if "extends android.content.BroadcastReceiver" in clazz.raw:
+    if clazz.extends == "android.content.BroadcastReceiver":
         test_methods = True
         if not clazz.name.endswith("Receiver"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver")
 
-    if "extends android.app.Activity" in clazz.raw:
+    if clazz.extends == "android.app.Activity":
         test_methods = True
         if not clazz.name.endswith("Activity"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity")
@@ -731,7 +1069,7 @@
 def verify_builder(clazz):
     """Verify builder classes.
     Methods should return the builder to enable chaining."""
-    if " extends " in clazz.raw: return
+    if clazz.extends: return
     if not clazz.name.endswith("Builder"): return
 
     if clazz.name != "Builder":
@@ -759,7 +1097,7 @@
 
 def verify_aidl(clazz):
     """Catch people exposing raw AIDL."""
-    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
+    if clazz.extends == "android.os.Binder" or clazz.implements == "android.os.IInterface":
         error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
 
 
@@ -768,48 +1106,66 @@
     if clazz.pkg.name.startswith("com.android"):
         error(clazz, None, None, "Internal classes must not be exposed")
 
+def layering_build_ranking(ranking_list):
+    r = {}
+    for rank, ps in enumerate(ranking_list):
+        if not isinstance(ps, list):
+            ps = [ps]
+        for p in ps:
+            rs = r
+            for n in p.split('.'):
+                if n not in rs:
+                    rs[n] = {}
+                rs = rs[n]
+            rs['-rank'] = rank
+    return r
+
+LAYERING_PACKAGE_RANKING = layering_build_ranking([
+    ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
+    "android.app",
+    "android.widget",
+    "android.view",
+    "android.animation",
+    "android.provider",
+    ["android.content","android.graphics.drawable"],
+    "android.database",
+    "android.text",
+    "android.graphics",
+    "android.os",
+    "android.util"
+])
 
 def verify_layering(clazz):
     """Catch package layering violations.
     For example, something in android.os depending on android.app."""
-    ranking = [
-        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
-        "android.app",
-        "android.widget",
-        "android.view",
-        "android.animation",
-        "android.provider",
-        ["android.content","android.graphics.drawable"],
-        "android.database",
-        "android.text",
-        "android.graphics",
-        "android.os",
-        "android.util"
-    ]
 
     def rank(p):
-        for i in range(len(ranking)):
-            if isinstance(ranking[i], list):
-                for j in ranking[i]:
-                    if p.startswith(j): return i
+        r = None
+        l = LAYERING_PACKAGE_RANKING
+        for n in p.split('.'):
+            if n in l:
+                l = l[n]
+                if '-rank' in l:
+                    r = l['-rank']
             else:
-                if p.startswith(ranking[i]): return i
+                break
+        return r
 
     cr = rank(clazz.pkg.name)
     if cr is None: return
 
     for f in clazz.fields:
         ir = rank(f.typ)
-        if ir and ir < cr:
+        if ir is not None and ir < cr:
             warn(clazz, f, "FW6", "Field type violates package layering")
 
-    for m in clazz.methods:
+    for m in itertools.chain(clazz.methods, clazz.ctors):
         ir = rank(m.typ)
-        if ir and ir < cr:
+        if ir is not None and ir < cr:
             warn(clazz, m, "FW6", "Method return type violates package layering")
         for arg in m.args:
             ir = rank(arg)
-            if ir and ir < cr:
+            if ir is not None and ir < cr:
                 warn(clazz, m, "FW6", "Method argument type violates package layering")
 
 
@@ -900,21 +1256,18 @@
             if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]:
                 warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException")
 
+GOOGLE_IGNORECASE = re.compile("google", re.IGNORECASE)
 
 def verify_google(clazz):
     """Verifies that APIs never reference Google."""
 
-    if re.search("google", clazz.raw, re.IGNORECASE):
+    if GOOGLE_IGNORECASE.search(clazz.raw) is not None:
         error(clazz, None, None, "Must never reference Google")
 
-    test = []
-    test.extend(clazz.ctors)
-    test.extend(clazz.fields)
-    test.extend(clazz.methods)
-
-    for t in test:
-        if re.search("google", t.raw, re.IGNORECASE):
-            error(clazz, t, None, "Must never reference Google")
+    for test in clazz.ctors, clazz.fields, clazz.methods:
+        for t in test:
+            if GOOGLE_IGNORECASE.search(t.raw) is not None:
+                error(clazz, t, None, "Must never reference Google")
 
 
 def verify_bitset(clazz):
@@ -1168,7 +1521,7 @@
     """Verifies that abstract inner classes are static."""
 
     if re.match(".+?\.[A-Z][^\.]+\.[A-Z]", clazz.fullname):
-        if " abstract " in clazz.raw and " static " not in clazz.raw:
+        if "abstract" in clazz.split and "static" not in clazz.split:
             warn(clazz, None, None, "Abstract inner classes should be static to improve testability")
 
 
@@ -1263,8 +1616,8 @@
 
 def verify_closable(clazz):
     """Verifies that classes are AutoClosable."""
-    if "implements java.lang.AutoCloseable" in clazz.raw: return
-    if "implements java.io.Closeable" in clazz.raw: return
+    if clazz.implements == "java.lang.AutoCloseable": return
+    if clazz.implements == "java.io.Closeable": return
 
     for m in clazz.methods:
         if len(m.args) > 0: continue
@@ -1309,7 +1662,7 @@
         binary.add(op)
 
     for m in clazz.methods:
-        if 'static' in m.split:
+        if 'static' in m.split or 'operator' in m.split:
             continue
 
         # https://kotlinlang.org/docs/reference/operator-overloading.html#unary-prefix-operators
@@ -1350,6 +1703,9 @@
 def verify_collections_over_arrays(clazz):
     """Warn that [] should be Collections."""
 
+    if "@interface" in clazz.split:
+        return
+
     safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"]
     for m in clazz.methods:
         if m.typ.endswith("[]") and m.typ not in safe:
@@ -1683,11 +2039,11 @@
             del cur[prev_clazz.fullname]
 
     for clazz in cur.values():
-        if " deprecated " in clazz.raw and not clazz.fullname in prev:
+        if "deprecated" in clazz.split and not clazz.fullname in prev:
             error(clazz, None, None, "Found API deprecation at birth")
 
         for i in clazz.ctors + clazz.methods + clazz.fields:
-            if " deprecated " in i.raw:
+            if "deprecated" in i.split:
                 error(clazz, i, None, "Found API deprecation at birth")
 
     print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
index ece69a9..fde61a9 100644
--- a/tools/apilint/apilint_test.py
+++ b/tools/apilint/apilint_test.py
@@ -143,5 +143,189 @@
                                       out_classes_with_base=classes_with_base)
         self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
 
+class V2TokenizerTests(unittest.TestCase):
+    def _test(self, raw, expected):
+        self.assertEquals(apilint.V2Tokenizer(raw).tokenize(), expected)
+
+    def test_simple(self):
+        self._test("  method public some.Type someName(some.Argument arg, int arg);",
+                   ['method', 'public', 'some.Type', 'someName', '(', 'some.Argument',
+                    'arg', ',', 'int', 'arg', ')', ';'])
+        self._test("class Some.Class extends SomeOther {",
+                   ['class', 'Some.Class', 'extends', 'SomeOther', '{'])
+
+    def test_varargs(self):
+        self._test("name(String...)",
+                   ['name', '(', 'String', '...', ')'])
+
+    def test_kotlin(self):
+        self._test("String? name(String!...)",
+                   ['String', '?', 'name', '(', 'String', '!',  '...', ')'])
+
+    def test_annotation(self):
+        self._test("method @Nullable public void name();",
+                   ['method', '@', 'Nullable', 'public', 'void', 'name', '(', ')', ';'])
+
+    def test_annotation_args(self):
+        self._test("@Some(val=1, other=2) class Class {",
+                   ['@', 'Some', '(', 'val', '=', '1', ',', 'other', '=', '2', ')',
+                    'class', 'Class', '{'])
+    def test_comment(self):
+        self._test("some //comment", ['some'])
+
+    def test_strings(self):
+        self._test(r'"" "foo" "\"" "\\"', ['""', '"foo"', r'"\""', r'"\\"'])
+
+    def test_at_interface(self):
+        self._test("public @interface Annotation {",
+                   ['public', '@interface', 'Annotation', '{'])
+
+    def test_array_type(self):
+        self._test("int[][]", ['int', '[]', '[]'])
+
+    def test_generics(self):
+        self._test("<>foobar<A extends Object>",
+                   ['<', '>', 'foobar', '<', 'A', 'extends', 'Object', '>'])
+
+class V2ParserTests(unittest.TestCase):
+    def _cls(self, raw):
+        pkg = apilint.Package(999, "package pkg {", None)
+        return apilint.Class(pkg, 1, raw, '', sig_format=2)
+
+    def _method(self, raw, cls=None):
+        if not cls:
+            cls = self._cls("class Class {")
+        return apilint.Method(cls, 1, raw, '', sig_format=2)
+
+    def _field(self, raw):
+        cls = self._cls("class Class {")
+        return apilint.Field(cls, 1, raw, '', sig_format=2)
+
+    def test_class(self):
+        cls = self._cls("@Deprecated @IntRange(from=1, to=2) public static abstract class Some.Name extends Super<Class> implements Interface<Class> {")
+        self.assertTrue('deprecated' in cls.split)
+        self.assertTrue('static' in cls.split)
+        self.assertTrue('abstract' in cls.split)
+        self.assertTrue('class' in cls.split)
+        self.assertEquals('Super', cls.extends)
+        self.assertEquals('Interface', cls.implements)
+        self.assertEquals('pkg.Some.Name', cls.fullname)
+
+    def test_interface(self):
+        cls = self._cls("@Deprecated @IntRange(from=1, to=2) public interface Some.Name extends Interface<Class> {")
+        self.assertTrue('deprecated' in cls.split)
+        self.assertTrue('interface' in cls.split)
+        self.assertEquals('Interface', cls.extends)
+        self.assertEquals('Interface', cls.implements)
+        self.assertEquals('pkg.Some.Name', cls.fullname)
+
+    def test_at_interface(self):
+        cls = self._cls("@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface SuppressLint {")
+        self.assertTrue('@interface' in cls.split)
+        self.assertEquals('pkg.SuppressLint', cls.fullname)
+
+    def test_parse_method(self):
+        m = self._method("method @Deprecated public static native <T> Class<T>[][] name("
+                         + "Class<T[]>[][], Class<T[][][]>[][]...) throws Exception, T;")
+        self.assertTrue('static' in m.split)
+        self.assertTrue('public' in m.split)
+        self.assertTrue('method' in m.split)
+        self.assertTrue('native' in m.split)
+        self.assertTrue('deprecated' in m.split)
+        self.assertEquals('java.lang.Class[][]', m.typ)
+        self.assertEquals('name', m.name)
+        self.assertEquals(['java.lang.Class[][]', 'java.lang.Class[][]...'], m.args)
+        self.assertEquals(['java.lang.Exception', 'T'], m.throws)
+
+    def test_ctor(self):
+        m = self._method("ctor @Deprecated <T> ClassName();")
+        self.assertTrue('ctor' in m.split)
+        self.assertTrue('deprecated' in m.split)
+        self.assertEquals('ctor', m.typ)
+        self.assertEquals('ClassName', m.name)
+
+    def test_parse_annotation_method(self):
+        cls = self._cls("@interface Annotation {")
+        self._method('method abstract String category() default "";', cls=cls)
+        self._method('method abstract boolean deepExport() default false;', cls=cls)
+        self._method('method abstract ViewDebug.FlagToString[] flagMapping() default {};', cls=cls)
+        self._method('method abstract ViewDebug.FlagToString[] flagMapping() default (double)java.lang.Float.NEGATIVE_INFINITY;', cls=cls)
+
+    def test_parse_string_field(self):
+        f = self._field('field @Deprecated public final String SOME_NAME = "value";')
+        self.assertTrue('field' in f.split)
+        self.assertTrue('deprecated' in f.split)
+        self.assertTrue('final' in f.split)
+        self.assertEquals('java.lang.String', f.typ)
+        self.assertEquals('SOME_NAME', f.name)
+        self.assertEquals('value', f.value)
+
+    def test_parse_field(self):
+        f = self._field('field public Object SOME_NAME;')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('java.lang.Object', f.typ)
+        self.assertEquals('SOME_NAME', f.name)
+        self.assertEquals(None, f.value)
+
+    def test_parse_int_field(self):
+        f = self._field('field public int NAME = 123;')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('int', f.typ)
+        self.assertEquals('NAME', f.name)
+        self.assertEquals('123', f.value)
+
+    def test_parse_quotient_field(self):
+        f = self._field('field public int NAME = (0.0/0.0);')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('int', f.typ)
+        self.assertEquals('NAME', f.name)
+        self.assertEquals('( 0.0 / 0.0 )', f.value)
+
+    def test_kotlin_types(self):
+        self._field('field public List<Integer[]?[]!>?[]![]? NAME;')
+        self._method("method <T?> Class<T!>?[]![][]? name(Type!, Type argname,"
+                         + "Class<T?>[][]?[]!...!) throws Exception, T;")
+        self._method("method <T> T name(T a = 1, T b = A(1), Lambda f = { false }, N? n = null, "
+                         + """double c = (1/0), float d = 1.0f, String s = "heyo", char c = 'a');""")
+
+    def test_kotlin_operator(self):
+        self._method('method public operator void unaryPlus(androidx.navigation.NavDestination);')
+        self._method('method public static operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);')
+        self._method('method public static operator <T> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);')
+
+    def test_kotlin_property(self):
+        self._field('property public VM value;')
+        self._field('property public final String? action;')
+
+    def test_kotlin_varargs(self):
+        self._method('method public void error(int p = "42", Integer int2 = "null", int p1 = "42", vararg String args);')
+
+    def test_kotlin_default_values(self):
+        self._method('method public void foo(String! = null, String! = "Hello World", int = 42);')
+        self._method('method void method(String, String firstArg = "hello", int secondArg = "42", String thirdArg = "world");')
+        self._method('method void method(String, String firstArg = "hello", int secondArg = "42");')
+        self._method('method void method(String, String firstArg = "hello");')
+        self._method('method void edit(android.Type, boolean commit = false, Function1<? super Editor,kotlin.Unit> action);')
+        self._method('method <K, V> LruCache<K,V> lruCache(int maxSize, Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> 1 }, Function1<? extends V> create = { (V)null }, Function4<kotlin.Unit> onEntryRemoved = { _, _, _, _ ->  });')
+        self._method('method android.Bitmap? drawToBitmap(android.View, android.Config config = android.graphics.Bitmap.Config.ARGB_8888);')
+        self._method('method void emptyLambda(Function0<kotlin.Unit> sizeOf = {});')
+        self._method('method void method1(int p = 42, Integer? int2 = null, int p1 = 42, String str = "hello world", java.lang.String... args);')
+        self._method('method void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE);')
+        self._method('method void method3(String str, int p, int int2 = double(int) + str.length);')
+        self._method('method void print(test.pkg.Foo foo = test.pkg.Foo());')
+
+    def test_type_use_annotation(self):
+        self._method('method public static int codePointAt(char @NonNull [], int);')
+        self._method('method @NonNull public java.util.Set<java.util.Map.@NonNull Entry<K,V>> entrySet();')
+
+        m = self._method('method @NonNull public java.lang.annotation.@NonNull Annotation @NonNull [] getAnnotations();')
+        self.assertEquals('java.lang.annotation.Annotation[]', m.typ)
+
+        m = self._method('method @NonNull public abstract java.lang.annotation.@NonNull Annotation @NonNull [] @NonNull [] getParameterAnnotations();')
+        self.assertEquals('java.lang.annotation.Annotation[][]', m.typ)
+
+        m = self._method('method @NonNull public @NonNull String @NonNull [] split(@NonNull String, int);')
+        self.assertEquals('java.lang.String[]', m.typ)
+
 if __name__ == "__main__":
-    unittest.main()
\ No newline at end of file
+    unittest.main()
diff --git a/tools/bit/command.h b/tools/bit/command.h
index fb44900..dd7103e 100644
--- a/tools/bit/command.h
+++ b/tools/bit/command.h
@@ -25,7 +25,7 @@
 
 struct Command
 {
-    Command(const string& prog);
+    explicit Command(const string& prog);
     ~Command();
 
     void AddArg(const string& arg);
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
index ca6b3c4..9b5df56 100644
--- a/tools/processors/view_inspector/Android.bp
+++ b/tools/processors/view_inspector/Android.bp
@@ -5,7 +5,7 @@
     java_resource_dirs: ["src/resources"],
 
     static_libs: [
-    	"javapoet",
+        "javapoet",
     ],
 
     use_tools_jar: true,
@@ -18,9 +18,9 @@
     java_resource_dirs: ["test/resources"],
 
     static_libs: [
-        "guava",
         "junit",
-        "view-inspector-annotation-processor",
+        "guava",
+        "view-inspector-annotation-processor"
     ],
 
     test_suites: ["general-tests"],
diff --git a/tools/processors/view_inspector/TEST_MAPPING b/tools/processors/view_inspector/TEST_MAPPING
index a91b5b4..b4c9cab 100644
--- a/tools/processors/view_inspector/TEST_MAPPING
+++ b/tools/processors/view_inspector/TEST_MAPPING
@@ -2,6 +2,8 @@
   "presubmit": [
     {
       "name": "view-inspector-annotation-processor-test"
+    }, {
+      "name": "CtsViewInspectorAnnotationProcessorTestCases"
     }
   ]
 }
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
index f157949..fc4cd01 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -24,6 +24,7 @@
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.Types;
@@ -78,40 +79,126 @@
     }
 
     /**
-     * Extract a string-valued property from an {@link AnnotationMirror}.
+     * Determine if an annotation with the supplied qualified name is present on the element.
      *
-     * @param propertyName The name of the requested property
-     * @param annotationMirror The mirror to search for the property
-     * @return The String value of the annotation or null
+     * @param element The element to check for the presence of an annotation
+     * @param annotationQualifiedName The name of the annotation to check for
+     * @return True if the annotation is present, false otherwise
      */
-    Optional<String> stringProperty(String propertyName, AnnotationMirror annotationMirror) {
-        final AnnotationValue value = valueByName(propertyName, annotationMirror);
-        if (value != null) {
-            return Optional.of((String) value.getValue());
-        } else {
-            return Optional.empty();
+    boolean hasAnnotation(Element element, String annotationQualifiedName) {
+        final TypeElement namedElement = mElementUtils.getTypeElement(annotationQualifiedName);
+
+        if (namedElement != null) {
+            final TypeMirror annotationType = namedElement.asType();
+
+            for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
+                if (mTypeUtils.isSubtype(annotation.getAnnotationType(), annotationType)) {
+                    return true;
+                }
+            }
         }
+
+        return false;
     }
 
+    /**
+     * Get the typed value of an annotation property by name.
+     *
+     * The returned optional will be empty if the value was left at the default, or if the value
+     * of the property is null.
+     *
+     * @param propertyName The name of the property to search for
+     * @param valueClass The expected class of the property value
+     * @param element The element the annotation is on, used for exceptions
+     * @param annotationMirror An annotation mirror to search for the property
+     * @param <T> The type of the value
+     * @return An optional containing the typed value of the named property
+     */
+    <T> Optional<T> typedValueByName(
+            String propertyName,
+            Class<T> valueClass,
+            Element element,
+            AnnotationMirror annotationMirror) {
+        return valueByName(propertyName, annotationMirror).map(annotationValue -> {
+            final Object value = annotationValue.getValue();
+
+            if (value == null) {
+                throw new ProcessingException(
+                        String.format(
+                                "Unexpected null value for annotation property \"%s\".",
+                                propertyName),
+                        element,
+                        annotationMirror,
+                        annotationValue);
+            }
+
+            if (valueClass.isAssignableFrom(value.getClass())) {
+                return valueClass.cast(value);
+            } else {
+                throw new ProcessingException(
+                        String.format(
+                                "Expected annotation property \"%s\" to have type %s, but got %s.",
+                                propertyName,
+                                valueClass.getCanonicalName(),
+                                value.getClass().getCanonicalName()),
+                        element,
+                        annotationMirror,
+                        annotationValue);
+            }
+        });
+    }
+
+    /**
+     * Get the untyped value of an annotation property by name.
+     *
+     * The returned optional will be empty if the value was left at the default, or if the value
+     * of the property is null.
+     *
+     * @param propertyName The name of the property to search for
+     * @param element The element the annotation is on, used for exceptions
+     * @param annotationMirror An annotation mirror to search for the property
+     * @return An optional containing the untyped value of the named property
+     * @see AnnotationValue#getValue()
+     */
+    Optional<Object> untypedValueByName(
+            String propertyName,
+            Element element,
+            AnnotationMirror annotationMirror) {
+        return valueByName(propertyName, annotationMirror).map(annotationValue -> {
+            final Object value = annotationValue.getValue();
+
+            if (value == null) {
+                throw new ProcessingException(
+                        String.format(
+                                "Unexpected null value for annotation property \"%s\".",
+                                propertyName),
+                        element,
+                        annotationMirror,
+                        annotationValue);
+            }
+
+            return value;
+        });
+    }
 
     /**
      * Extract a {@link AnnotationValue} from a mirror by string property name.
      *
      * @param propertyName The name of the property requested property
-     * @param annotationMirror
-     * @return
+     * @param annotationMirror The mirror to search for the property
+     * @return The value of the property
      */
-    AnnotationValue valueByName(String propertyName, AnnotationMirror annotationMirror) {
+    Optional<AnnotationValue> valueByName(String propertyName, AnnotationMirror annotationMirror) {
         final Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap =
                 annotationMirror.getElementValues();
 
         for (ExecutableElement method : valueMap.keySet()) {
             if (method.getSimpleName().contentEquals(propertyName)) {
-                return valueMap.get(method);
+                return Optional.ofNullable(valueMap.get(method));
             }
         }
 
-        return null;
+        // Property not explicitly defined, use default value.
+        return Optional.empty();
     }
-
 }
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
index 579745d..f1ebb87 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -21,6 +21,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 
 /**
@@ -70,7 +71,7 @@
      * @return The property or an empty optional
      */
     public Optional<Property> getProperty(String name) {
-        return Optional.of(mPropertyMap.get(name));
+        return Optional.ofNullable(mPropertyMap.get(name));
     }
 
     /**
@@ -87,13 +88,15 @@
      */
     public static final class Property {
         private final String mName;
-        private String mGetter;
-        private Type mType;
+        private final String mGetter;
+        private final Type mType;
         private boolean mAttributeIdInferrableFromR = true;
         private int mAttributeId = 0;
 
-        public Property(String name) {
-            mName = name;
+        public Property(String name, String getter, Type type) {
+            mName = Objects.requireNonNull(name, "Name must not be null");
+            mGetter = Objects.requireNonNull(getter, "Getter must not be null");
+            mType = Objects.requireNonNull(type, "Type must not be null");
         }
 
         public int getAttributeId() {
@@ -126,18 +129,10 @@
             return mGetter;
         }
 
-        public void setGetter(String getter) {
-            mGetter = getter;
-        }
-
         public Type getType() {
             return mType;
         }
 
-        public void setType(Type type) {
-            mType = type;
-        }
-
         public enum Type {
             /** Primitive or boxed {@code boolean} */
             BOOLEAN,
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
index a186a82..46819b2 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -23,7 +23,7 @@
 import javax.lang.model.element.Element;
 
 /**
- * Process {InspectableNodeName} annotations
+ * Process {@code @InspectableNodeName} annotations.
  *
  * @see android.view.inspector.InspectableNodeName
  */
@@ -58,7 +58,8 @@
         try {
             final AnnotationMirror mirror =
                     mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
-            final Optional<String> nodeName = mAnnotationUtils.stringProperty("value", mirror);
+            final Optional<String> nodeName = mAnnotationUtils
+                    .typedValueByName("value", String.class, element, mirror);
 
             if (!model.getNodeName().isPresent() || model.getNodeName().equals(nodeName)) {
                 model.setNodeName(nodeName);
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
new file mode 100644
index 0000000..f666be7
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2019 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.processor.view.inspector;
+
+import android.processor.view.inspector.InspectableClassModel.Property;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Process {@code @InspectableProperty} annotations.
+ *
+ * @see android.view.inspector.InspectableProperty
+ */
+public final class InspectablePropertyProcessor implements ModelProcessor {
+    private final String mQualifiedName;
+    private final ProcessingEnvironment mProcessingEnv;
+    private final AnnotationUtils mAnnotationUtils;
+
+    /**
+     * Regex that matches methods names of the form {@code #getValue()}.
+     */
+    private static final Pattern GETTER_GET_PREFIX = Pattern.compile("\\Aget[A-Z]");
+
+    /**
+     * Regex that matches method name of the form {@code #isPredicate()}.
+     */
+    private static final Pattern GETTER_IS_PREFIX = Pattern.compile("\\Ais[A-Z]");
+
+    /**
+     * Set of android and androidx annotation qualified names for colors packed into {@code int}.
+     *
+     * @see android.annotation.ColorInt
+     */
+    private static final String[] COLOR_INT_ANNOTATION_NAMES = {
+            "android.annotation.ColorInt",
+            "androidx.annotation.ColorInt"};
+
+    /**
+     * Set of android and androidx annotation qualified names for colors packed into {@code long}.
+     * @see android.annotation.ColorLong
+     */
+    private static final String[] COLOR_LONG_ANNOTATION_NAMES = {
+            "android.annotation.ColorLong",
+            "androidx.annotation.ColorLong"};
+
+    /**
+     * @param annotationQualifiedName The qualified name of the annotation to process
+     * @param processingEnv The processing environment from the parent processor
+     */
+    public InspectablePropertyProcessor(
+            String annotationQualifiedName,
+            ProcessingEnvironment processingEnv) {
+        mQualifiedName = annotationQualifiedName;
+        mProcessingEnv = processingEnv;
+        mAnnotationUtils = new AnnotationUtils(processingEnv);
+    }
+
+    @Override
+    public void process(Element element, InspectableClassModel model) {
+        try {
+            final AnnotationMirror annotation =
+                    mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
+            final ExecutableElement getter = ensureGetter(element);
+            final Property property = buildProperty(getter, annotation);
+
+            model.getProperty(property.getName()).ifPresent(p -> {
+                throw new ProcessingException(
+                        String.format(
+                                "Property \"%s\" is already defined on #%s().",
+                                p.getName(),
+                                p.getGetter()),
+                        getter,
+                        annotation);
+            });
+
+            model.putProperty(property);
+        } catch (ProcessingException processingException) {
+            processingException.print(mProcessingEnv.getMessager());
+        }
+    }
+
+    /**
+     * Check that an element is shaped like a getter.
+     *
+     * @param element An element that hopefully represents a getter
+     * @throws ProcessingException if the element isn't a getter
+     * @return An {@link ExecutableElement} that represents a getter method.
+     */
+    private ExecutableElement ensureGetter(Element element) {
+        if (element.getKind() != ElementKind.METHOD) {
+            throw new ProcessingException(
+                    String.format("Expected a method, got a %s", element.getKind()),
+                    element);
+        }
+
+        final ExecutableElement method = (ExecutableElement) element;
+        final Set<Modifier> modifiers = method.getModifiers();
+
+        if (modifiers.contains(Modifier.PRIVATE)) {
+            throw new ProcessingException(
+                    "Property getter methods must not be private.",
+                    element);
+        }
+
+        if (modifiers.contains(Modifier.ABSTRACT)) {
+            throw new ProcessingException(
+                    "Property getter methods must not be abstract.",
+                    element);
+        }
+
+        if (modifiers.contains(Modifier.STATIC)) {
+            throw new ProcessingException(
+                    "Property getter methods must not be static.",
+                    element);
+        }
+
+        if (!method.getParameters().isEmpty()) {
+            throw new ProcessingException(
+                    String.format(
+                            "Expected a getter method to take no parameters, "
+                            + "but got %d parameters.",
+                            method.getParameters().size()),
+                    element);
+        }
+
+        if (method.isVarArgs()) {
+            throw new ProcessingException(
+                    "Expected a getter method to take no arguments, but got a var args method.",
+                    element);
+        }
+
+        if (method.getReturnType() instanceof NoType) {
+            throw new ProcessingException(
+                    "Expected a getter to have a return type, got void.",
+                    element);
+        }
+
+        return method;
+    }
+
+    /**
+     * Build a {@link Property} from a getter and an inspectable property annotation.
+     *
+     * @param getter An element representing the getter to build from
+     * @param annotation A mirror of an inspectable property-shaped annotation
+     * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled
+     * @return A property for the getter and annotation
+     */
+    private Property buildProperty(ExecutableElement getter, AnnotationMirror annotation) {
+        final String name = mAnnotationUtils
+                .typedValueByName("name", String.class, getter, annotation)
+                .orElseGet(() -> inferPropertyNameFromGetter(getter));
+
+        final Property property = new Property(
+                name,
+                getter.getSimpleName().toString(),
+                determinePropertyType(getter, annotation));
+
+        mAnnotationUtils
+                .typedValueByName("hasAttributeId", Boolean.class, getter, annotation)
+                .ifPresent(property::setAttributeIdInferrableFromR);
+
+        mAnnotationUtils
+                .typedValueByName("attributeId", Integer.class, getter, annotation)
+                .ifPresent(property::setAttributeId);
+
+        return property;
+    }
+
+    /**
+     * Determine the property type from the annotation, return type, or context clues.
+     *
+     * @param getter An element representing the getter to build from
+     * @param annotation A mirror of an inspectable property-shaped annotation
+     * @return The resolved property type
+     * @throws ProcessingException If the property type cannot be resolved
+     * @see android.view.inspector.InspectableProperty#valueType()
+     */
+    private Property.Type determinePropertyType(
+            ExecutableElement getter,
+            AnnotationMirror annotation) {
+
+        final String valueType = mAnnotationUtils
+                .untypedValueByName("valueType", getter, annotation)
+                .map(Object::toString)
+                .orElse("INFERRED");
+
+        final Property.Type returnType = convertReturnTypeToPropertyType(getter);
+
+        switch (valueType) {
+            case "INFERRED":
+                if (hasColorAnnotation(getter)) {
+                    return Property.Type.COLOR;
+                } else {
+                    return returnType;
+                }
+            case "NONE":
+                return returnType;
+            case "COLOR":
+                switch (returnType) {
+                    case COLOR:
+                    case INT:
+                    case LONG:
+                        return Property.Type.COLOR;
+                    default:
+                        throw new ProcessingException(
+                                "Color must be a long, integer, or android.graphics.Color",
+                                getter,
+                                annotation);
+                }
+            case "GRAVITY":
+                if (returnType == Property.Type.INT) {
+                    return Property.Type.GRAVITY;
+                } else {
+                    throw new ProcessingException(
+                            String.format("Gravity must be an integer, got %s", returnType),
+                            getter,
+                            annotation);
+                }
+            case "INT_ENUM":
+            case "INT_FLAG":
+                throw new ProcessingException("Not implemented", getter, annotation);
+            default:
+                throw new ProcessingException(
+                        String.format("Unknown value type enumeration value: %s", valueType),
+                        getter,
+                        annotation);
+        }
+    }
+
+    /**
+     * Get a property type from the return type of a getter.
+     *
+     * @param getter The getter to extract the return type of
+     * @throws ProcessingException If the return type is not a primitive or an object
+     * @return The property type returned by the getter
+     */
+    private Property.Type convertReturnTypeToPropertyType(ExecutableElement getter) {
+        final TypeMirror returnType = getter.getReturnType();
+
+        switch (unboxType(returnType)) {
+            case BOOLEAN:
+                return Property.Type.BOOLEAN;
+            case BYTE:
+                return Property.Type.BYTE;
+            case CHAR:
+                return Property.Type.CHAR;
+            case DOUBLE:
+                return Property.Type.DOUBLE;
+            case FLOAT:
+                return Property.Type.FLOAT;
+            case INT:
+                return Property.Type.INT;
+            case LONG:
+                return Property.Type.LONG;
+            case SHORT:
+                return Property.Type.SHORT;
+            case DECLARED:
+                if (isColorType(returnType)) {
+                    return Property.Type.COLOR;
+                } else {
+                    return Property.Type.OBJECT;
+                }
+            default:
+                throw new ProcessingException(
+                        String.format("Unsupported return type %s.", returnType),
+                        getter);
+        }
+    }
+
+    /**
+     * Determine if a getter is annotated with color annotation matching its return type.
+     *
+     * Note that an {@code int} return value annotated with {@link android.annotation.ColorLong} is
+     * not considered to be annotated, nor is a {@code long} annotated with
+     * {@link android.annotation.ColorInt}.
+     *
+     * @param getter The getter to query
+     * @return True if the getter has a color annotation, false otherwise
+     *
+     */
+    private boolean hasColorAnnotation(ExecutableElement getter) {
+        switch (unboxType(getter.getReturnType())) {
+            case INT:
+                for (String name : COLOR_INT_ANNOTATION_NAMES) {
+                    if (mAnnotationUtils.hasAnnotation(getter, name)) {
+                        return true;
+                    }
+                }
+                return false;
+            case LONG:
+                for (String name : COLOR_LONG_ANNOTATION_NAMES) {
+                    if (mAnnotationUtils.hasAnnotation(getter, name)) {
+                        return true;
+                    }
+                }
+                return false;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Infer a property name from a getter method.
+     *
+     * If the method is prefixed with {@code get}, the prefix will be stripped, and the
+     * capitalization fixed. E.g.: {@code getSomeProperty} to {@code someProperty}.
+     *
+     * Additionally, if the method's return type is a boolean, an {@code is} prefix will also be
+     * stripped. E.g.: {@code isPropertyEnabled} to {@code propertyEnabled}.
+     *
+     * Failing that, this method will just return the full name of the getter.
+     *
+     * @param getter An element representing a getter
+     * @return A string property name
+     */
+    private String inferPropertyNameFromGetter(ExecutableElement getter) {
+        final String name = getter.getSimpleName().toString();
+
+        if (GETTER_GET_PREFIX.matcher(name).find()) {
+            return name.substring(3, 4).toLowerCase() + name.substring(4);
+        } else if (isBoolean(getter.getReturnType()) && GETTER_IS_PREFIX.matcher(name).find()) {
+            return name.substring(2, 3).toLowerCase() + name.substring(3);
+        } else {
+            return name;
+        }
+    }
+
+    /**
+     * Determine if a {@link TypeMirror} is a boxed or unboxed boolean.
+     *
+     * @param type The type mirror to check
+     * @return True if the type is a boolean
+     */
+    private boolean isBoolean(TypeMirror type) {
+        if (type.getKind() == TypeKind.DECLARED) {
+            return mProcessingEnv.getTypeUtils().unboxedType(type).getKind() == TypeKind.BOOLEAN;
+        } else {
+            return type.getKind() == TypeKind.BOOLEAN;
+        }
+    }
+
+    /**
+     * Unbox a type mirror if it represents a boxed type, otherwise pass it through.
+     *
+     * @param typeMirror The type mirror to unbox
+     * @return The same type mirror, or an unboxed primitive version
+     */
+    private TypeKind unboxType(TypeMirror typeMirror) {
+        final TypeKind typeKind = typeMirror.getKind();
+
+        if (typeKind.isPrimitive()) {
+            return typeKind;
+        } else if (typeKind == TypeKind.DECLARED) {
+            try {
+                return mProcessingEnv.getTypeUtils().unboxedType(typeMirror).getKind();
+            } catch (IllegalArgumentException e) {
+                return typeKind;
+            }
+        } else {
+            return typeKind;
+        }
+    }
+
+    /**
+     * Determine if a type mirror represents a subtype of {@link android.graphics.Color}.
+     *
+     * @param typeMirror The type mirror to test
+     * @return True if it represents a subclass of color, false otherwise
+     */
+    private boolean isColorType(TypeMirror typeMirror) {
+        final TypeElement colorType = mProcessingEnv
+                .getElementUtils()
+                .getTypeElement("android.graphics.Color");
+
+        if (colorType == null) {
+            return false;
+        } else {
+            return mProcessingEnv.getTypeUtils().isSubtype(typeMirror, colorType.asType());
+        }
+    }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
index 3b85dbb..dd4d8f5 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -51,12 +51,6 @@
     private static final ClassName R_CLASS_NAME = ClassName.get("android", "R");
 
     /**
-     * The class name of {@link android.content.res.ResourceId}.
-     */
-    private static final ClassName RESOURCE_ID_CLASS_NAME = ClassName.get(
-            "android.content.res", "ResourceId");
-
-    /**
      * The class name of {@link android.view.inspector.InspectionCompanion}.
      */
     private static final ClassName INSPECTION_COMPANION = ClassName.get(
@@ -91,11 +85,11 @@
     private static final String GENERATED_CLASS_SUFFIX = "$$InspectionCompanion";
 
     /**
-     * The null resource ID.
+     * The null resource ID, copied to avoid a host dependency on platform code.
      *
-     * @see android.content.res.ResourceId#ID_NULL
+     * @see android.content.res.Resources#ID_NULL
      */
-    private static final int NO_ID = 0;
+    private static final int ID_NULL = 0;
 
     /**
      * @param filer A filer to write the generated source to
@@ -289,8 +283,8 @@
         if (property.isAttributeIdInferrableFromR()) {
             builder.add("$T.attr.$L", R_CLASS_NAME, property.getName());
         } else {
-            if (property.getAttributeId() == NO_ID) {
-                builder.add("$T.ID_NULL", RESOURCE_ID_CLASS_NAME);
+            if (property.getAttributeId() == ID_NULL) {
+                builder.add("$L", ID_NULL);
             } else {
                 builder.add("$L", String.format("0x%08x", property.getAttributeId()));
             }
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
index 3ffcff8..6f52260 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
index e531b67..455f5b0 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -46,11 +46,14 @@
  * @see android.view.inspector.InspectableProperty
  */
 @SupportedAnnotationTypes({
-        PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME
+        PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME,
+        PlatformInspectableProcessor.PROPERTY_QUALIFIED_NAME
 })
 public final class PlatformInspectableProcessor extends AbstractProcessor {
     static final String NODE_NAME_QUALIFIED_NAME =
             "android.view.inspector.InspectableNodeName";
+    static final String PROPERTY_QUALIFIED_NAME =
+            "android.view.inspector.InspectableProperty";
 
     @Override
     public SourceVersion getSupportedSourceVersion() {
@@ -68,6 +71,11 @@
                         new InspectableNodeNameProcessor(NODE_NAME_QUALIFIED_NAME, processingEnv),
                         modelMap);
 
+            } else if (annotation.getQualifiedName().contentEquals(PROPERTY_QUALIFIED_NAME)) {
+                runModelProcessor(
+                        roundEnv.getElementsAnnotatedWith(annotation),
+                        new InspectablePropertyProcessor(PROPERTY_QUALIFIED_NAME, processingEnv),
+                        modelMap);
 
             } else {
                 fail("Unexpected annotation type", annotation);
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
index 6360e0a..b4c6466 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
diff --git a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
index fa4f71f..79185cc 100644
--- a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1 +1 @@
-android.processor.inspector.view.PlatformInspectableProcessor
+android.processor.view.inspector.PlatformInspectableProcessor
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
index f639719..b0775dc 100644
--- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -20,7 +20,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
-import static junit.framework.TestCase.fail;
+import static junit.framework.Assert.fail;
 
 import com.google.common.base.Charsets;
 import com.google.common.io.Resources;
@@ -65,27 +65,28 @@
 
     @Test
     public void testSimpleProperties() {
-        addProperty("boolean", Property.Type.BOOLEAN, "getBoolean");
-        addProperty("byte", Property.Type.BYTE, "getByte");
-        addProperty("char", Property.Type.CHAR, "getChar");
-        addProperty("double", Property.Type.DOUBLE, "getDouble");
-        addProperty("float", Property.Type.FLOAT, "getFloat");
-        addProperty("int", Property.Type.INT, "getInt");
-        addProperty("long", Property.Type.LONG, "getLong");
-        addProperty("short", Property.Type.SHORT, "getShort");
+        addProperty("boolean", "getBoolean", Property.Type.BOOLEAN);
+        addProperty("byte", "getByte", Property.Type.BYTE);
+        addProperty("char", "getChar", Property.Type.CHAR);
+        addProperty("double", "getDouble", Property.Type.DOUBLE);
+        addProperty("float", "getFloat", Property.Type.FLOAT);
+        addProperty("int", "getInt", Property.Type.INT);
+        addProperty("long", "getLong", Property.Type.LONG);
+        addProperty("short", "getShort", Property.Type.SHORT);
 
-        addProperty("object", Property.Type.OBJECT, "getObject");
-        addProperty("color", Property.Type.COLOR, "getColor");
-        addProperty("gravity", Property.Type.GRAVITY, "getGravity");
+        addProperty("object", "getObject", Property.Type.OBJECT);
+        addProperty("color", "getColor", Property.Type.COLOR);
+        addProperty("gravity", "getGravity", Property.Type.GRAVITY);
 
         assertGeneratedFileEquals("SimpleProperties");
     }
 
     @Test
     public void testNoAttributeId() {
-        final Property property = new Property("noAttributeProperty");
-        property.setType(Property.Type.INT);
-        property.setGetter("getNoAttributeProperty");
+        final Property property = new Property(
+                "noAttributeProperty",
+                "getNoAttributeProperty",
+                Property.Type.INT);
         property.setAttributeIdInferrableFromR(false);
         mModel.putProperty(property);
 
@@ -94,19 +95,18 @@
 
     @Test
     public void testSuppliedAttributeId() {
-        final Property property = new Property("suppliedAttributeProperty");
-        property.setType(Property.Type.INT);
-        property.setGetter("getSuppliedAttributeProperty");
+        final Property property = new Property(
+                "suppliedAttributeProperty",
+                "getSuppliedAttributeProperty",
+                Property.Type.INT);
         property.setAttributeId(0xdecafbad);
         mModel.putProperty(property);
 
         assertGeneratedFileEquals("SuppliedAttributeId");
     }
 
-    private Property addProperty(String name, Property.Type type, String getter) {
-        final Property property = new Property(name);
-        property.setType(type);
-        property.setGetter(getter);
+    private Property addProperty(String name, String getter, Property.Type type) {
+        final Property property = new Property(name, getter, type);
         mModel.putProperty(property);
         return property;
     }
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
index 277e840..23d0f78 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
@@ -1,6 +1,5 @@
 package com.android.inspectable;
 
-import android.content.res.ResourceId;
 import android.view.inspector.InspectionCompanion;
 import android.view.inspector.PropertyMapper;
 import android.view.inspector.PropertyReader;
@@ -25,7 +24,7 @@
 
     @Override
     public void mapProperties(PropertyMapper propertyMapper) {
-        mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", ResourceId.ID_NULL);
+        mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", 0);
         mPropertiesMapped = true;
     }
 
diff --git a/tools/signedconfig/prod_public.pem b/tools/signedconfig/prod_public.pem
new file mode 100644
index 0000000..8c10215
--- /dev/null
+++ b/tools/signedconfig/prod_public.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc
++tzC8MvnrzVxodvTpVY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g==
+-----END PUBLIC KEY-----
+
diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh
index 8e1f58c..a4ac6a8 100755
--- a/tools/signedconfig/verify_b64.sh
+++ b/tools/signedconfig/verify_b64.sh
@@ -7,4 +7,30 @@
 # The arg values can be taken from the debug log for SignedConfigService when verbose logging is
 # enabled.
 
-openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d)
+function verify() {
+  D=${1}
+  S=${2}
+  K=${3}
+  echo Trying ${K}
+  openssl dgst -sha256 -verify $(dirname $0)/${K} -signature <(echo ${S} | base64 -d) <(echo ${D} | base64 -d)
+}
+
+
+PROD_KEY_NAME=prod_public.pem
+DEBUG_KEY_NAME=debug_public.pem
+SIGNATURE="$2"
+DATA="$1"
+
+echo DATA: ${DATA}
+echo SIGNATURE: ${SIGNATURE}
+
+if verify "${DATA}" "${SIGNATURE}" "${PROD_KEY_NAME}"; then
+  echo Verified with ${PROD_KEY_NAME}
+  exit 0
+fi
+
+if verify "${DATA}" "${SIGNATURE}" "${DEBUG_KEY_NAME}"; then
+  echo Verified with ${DEBUG_KEY_NAME}
+  exit 0
+fi
+exit 1
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 11b408f..4491a85 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -67,7 +67,7 @@
         case JAVA_TYPE_STRING:
             return "char const*";
         case JAVA_TYPE_BYTE_ARRAY:
-            return "char const*";
+            return "const BytesField&";
         default:
             return "UNKNOWN";
     }
@@ -270,10 +270,6 @@
                                  chainField.name.c_str(), chainField.name.c_str());
                     }
                 }
-            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
-                fprintf(out, ", %s arg%d, size_t arg%d_length",
-                        cpp_type_name(*arg), argIndex, argIndex);
-
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
                              "const std::map<int, int64_t>& arg%d_2, "
@@ -355,7 +351,8 @@
                     fprintf(out, "    event.end();\n\n");
             } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
                 fprintf(out,
-                        "    event.AppendCharArray(arg%d, arg%d_length);\n",
+                        "    event.AppendCharArray(arg%d.arg, "
+                        "arg%d.arg_length);\n",
                         argIndex, argIndex);
             } else {
                 if (*arg == JAVA_TYPE_STRING) {
@@ -397,10 +394,6 @@
                                 chainField.name.c_str(), chainField.name.c_str());
                    }
                }
-           } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
-               fprintf(out, ", %s arg%d, size_t arg%d_length",
-                       cpp_type_name(*arg), argIndex, argIndex);
-
            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                fprintf(out,
                        ", const std::map<int, int32_t>& arg%d_1, "
@@ -434,8 +427,6 @@
                                 chainField.name.c_str(), chainField.name.c_str());
                    }
                }
-           } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
-               fprintf(out, ", arg%d, arg%d_length", argIndex, argIndex);
            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
                        argIndex, argIndex, argIndex);
@@ -494,7 +485,14 @@
                 fprintf(out, "        arg%d = \"\";\n", argIndex);
                 fprintf(out, "    }\n");
             }
-            fprintf(out, "    event << arg%d;\n", argIndex);
+            if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out,
+                        "    event.AppendCharArray(arg%d.arg, "
+                        "arg%d.arg_length);",
+                        argIndex, argIndex);
+            } else {
+                fprintf(out, "    event << arg%d;\n", argIndex);
+            }
             if (argIndex == 2) {
                 fprintf(out, "    event.end();\n\n");
                 fprintf(out, "    event.end();\n\n");
@@ -577,7 +575,9 @@
 static void write_cpp_usage(
     FILE* out, const string& method_name, const string& atom_code_name,
     const AtomDecl& atom, const AtomDecl &attributionDecl) {
-    fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
+    fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(),
+            atom_code_name.c_str());
+
     for (vector<AtomField>::const_iterator field = atom.fields.begin();
             field != atom.fields.end(); field++) {
         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -601,11 +601,6 @@
                          field->name.c_str(),
                          field->name.c_str(),
                          field->name.c_str());
-        } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
-            fprintf(out, ", %s %s, size_t %s_length",
-                    cpp_type_name(field->javaType), field->name.c_str(),
-                    field->name.c_str());
-
         } else {
             fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
         }
@@ -639,9 +634,6 @@
                              "const std::map<int, char const*>& arg%d_3, "
                              "const std::map<int, float>& arg%d_4",
                              argIndex, argIndex, argIndex, argIndex);
-            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
-                fprintf(out, ", %s arg%d, size_t arg%d_length",
-                        cpp_type_name(*arg), argIndex, argIndex);
             } else {
                 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
             }
@@ -708,6 +700,15 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
+    fprintf(out, "struct BytesField {\n");
+    fprintf(out,
+            "  BytesField(char const* array, size_t len) : arg(array), "
+            "arg_length(len) {}\n");
+    fprintf(out, "  char const* arg;\n");
+    fprintf(out, "  size_t arg_length;\n");
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
     fprintf(out, "struct StateAtomFieldOptions {\n");
     fprintf(out, "  std::vector<int> primaryFields;\n");
     fprintf(out, "  int exclusiveField;\n");
@@ -775,6 +776,7 @@
     const AtomDecl &attributionDecl) {
     for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
         signature != signatures.end(); signature++) {
+        fprintf(out, "    /** @hide */\n");
         fprintf(out, "    public static native int %s(int code", method_name.c_str());
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -820,6 +822,7 @@
         }
 
         // Method header (signature)
+        fprintf(out, "    /** @hide */\n");
         fprintf(out, "    public static void write(int code");
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -900,6 +903,7 @@
         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
             write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
         }
+        fprintf(out, "     * @hide\n");
         fprintf(out, "     */\n");
         fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), atom->code);
     }
@@ -916,6 +920,7 @@
                     field->name.c_str());
                 for (map<int, string>::const_iterator value = field->enumValues.begin();
                     value != field->enumValues.end(); value++) {
+                    fprintf(out, "    /** @hide */\n");
                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
                         make_constant_name(atom->message).c_str(),
                         make_constant_name(field->name).c_str(),
@@ -1179,6 +1184,11 @@
                 fprintf(out, "        str%d = NULL;\n", argIndex);
                 fprintf(out, "    }\n");
 
+                fprintf(out,
+                        "    android::util::BytesField bytesField%d(str%d, "
+                        "str%d_length);",
+                        argIndex, argIndex, argIndex);
+
             } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 hadStringOrChain = true;
                 for (auto chainField : attributionDecl.fields) {
@@ -1236,7 +1246,8 @@
 
         // stats_write call
         argIndex = 1;
-        fprintf(out, "\n    int ret =  android::util::%s(code", cpp_method_name.c_str());
+        fprintf(out, "\n    int ret =  android::util::%s(code",
+                cpp_method_name.c_str());
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -1251,16 +1262,12 @@
                 }
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", int32_t_map, int64_t_map, string_map, float_map");
+            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out, ", bytesField%d", argIndex);
             } else {
-                const char* argName = (*arg == JAVA_TYPE_STRING ||
-                                       *arg == JAVA_TYPE_BYTE_ARRAY)
-                                              ? "str"
-                                              : "arg";
+                const char* argName =
+                        (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
                 fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
-
-                if (*arg == JAVA_TYPE_BYTE_ARRAY) {
-                    fprintf(out, ", %s%d_length", argName, argIndex);
-                }
             }
             argIndex++;
         }
diff --git a/tools/streaming_proto/Errors.h b/tools/streaming_proto/Errors.h
index f14bbfd..bddd981 100644
--- a/tools/streaming_proto/Errors.h
+++ b/tools/streaming_proto/Errors.h
@@ -11,7 +11,7 @@
 struct Error
 {
     Error();
-    explicit Error(const Error& that);
+    Error(const Error& that);
     Error(const string& filename, int lineno, const char* message);
 
     string filename;
diff --git a/wifi/java/android/net/wifi/DppStatusCallback.java b/wifi/java/android/net/wifi/DppStatusCallback.java
deleted file mode 100644
index fa2ab30..0000000
--- a/wifi/java/android/net/wifi/DppStatusCallback.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.net.wifi;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.os.Handler;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * DPP Status Callback. Use this callback to get status updates (success, failure, progress)
- * from the DPP operation started with {@link WifiManager#startDppAsConfiguratorInitiator(String,
- * int, int, Handler, DppStatusCallback)} or {@link WifiManager#startDppAsEnrolleeInitiator(String,
- * Handler, DppStatusCallback)}
- * @hide
- */
-@SystemApi
-public abstract class DppStatusCallback {
-    /**
-     * DPP Success event: Configuration sent (Configurator mode).
-     */
-    public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0;
-
-    /** @hide */
-    @IntDef(prefix = { "DPP_EVENT_SUCCESS_" }, value = {
-            DPP_EVENT_SUCCESS_CONFIGURATION_SENT,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DppSuccessStatusCode {}
-
-    /**
-     * DPP Progress event: Initial authentication with peer succeeded.
-     */
-    public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0;
-
-    /**
-     * DPP Progress event: Peer requires more time to process bootstrapping.
-     */
-    public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1;
-
-    /** @hide */
-    @IntDef(prefix = { "DPP_EVENT_PROGRESS_" }, value = {
-            DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS,
-            DPP_EVENT_PROGRESS_RESPONSE_PENDING,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DppProgressStatusCode {}
-
-    /**
-     * DPP Failure event: Scanned QR code is either not a DPP URI, or the DPP URI has errors.
-     */
-    public static final int DPP_EVENT_FAILURE_INVALID_URI = -1;
-
-    /**
-     * DPP Failure event: Bootstrapping/Authentication initialization process failure.
-     */
-    public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2;
-
-    /**
-     * DPP Failure event: Both devices are implementing the same role and are incompatible.
-     */
-    public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3;
-
-    /**
-     * DPP Failure event: Configuration process has failed due to malformed message.
-     */
-    public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4;
-
-    /**
-     * DPP Failure event: DPP request while in another DPP exchange.
-     */
-    public static final int DPP_EVENT_FAILURE_BUSY = -5;
-
-    /**
-     * DPP Failure event: No response from the peer.
-     */
-    public static final int DPP_EVENT_FAILURE_TIMEOUT = -6;
-
-    /**
-     * DPP Failure event: General protocol failure.
-     */
-    public static final int DPP_EVENT_FAILURE = -7;
-
-    /**
-     * DPP Failure event: Feature or option is not supported.
-     */
-    public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8;
-
-    /**
-     * DPP Failure event: Invalid network provided to DPP configurator.
-     * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK).
-     */
-    public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9;
-
-
-    /** @hide */
-    @IntDef(prefix = {"DPP_EVENT_FAILURE_"}, value = {
-            DPP_EVENT_FAILURE_INVALID_URI,
-            DPP_EVENT_FAILURE_AUTHENTICATION,
-            DPP_EVENT_FAILURE_NOT_COMPATIBLE,
-            DPP_EVENT_FAILURE_CONFIGURATION,
-            DPP_EVENT_FAILURE_BUSY,
-            DPP_EVENT_FAILURE_TIMEOUT,
-            DPP_EVENT_FAILURE,
-            DPP_EVENT_FAILURE_NOT_SUPPORTED,
-            DPP_EVENT_FAILURE_INVALID_NETWORK,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DppFailureStatusCode {
-    }
-
-    /**
-     * Called when local DPP Enrollee successfully receives a new Wi-Fi configuration from the
-     * peer DPP configurator. This callback marks the successful end of the DPP current DPP
-     * session, and no further callbacks will be called. This callback is the successful outcome
-     * of a DPP flow starting with {@link WifiManager#startDppAsEnrolleeInitiator(String, Handler,
-     * DppStatusCallback)}.
-     *
-     * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
-     */
-    public abstract void onEnrolleeSuccess(int newNetworkId);
-
-    /**
-     * Called when a DPP success event takes place, except for when configuration is received from
-     * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
-     * This callback marks the successful end of the current DPP session, and no further
-     * callbacks will be called. This callback is the successful outcome of a DPP flow starting with
-     * {@link WifiManager#startDppAsConfiguratorInitiator(String, int, int, Handler,
-     * DppStatusCallback)}.
-     *
-     * @param code DPP success status code.
-     */
-    public abstract void onConfiguratorSuccess(@DppSuccessStatusCode int code);
-
-    /**
-     * Called when a DPP Failure event takes place. This callback marks the unsuccessful end of the
-     * current DPP session, and no further callbacks will be called.
-     *
-     * @param code DPP failure status code.
-     */
-    public abstract void onFailure(@DppFailureStatusCode int code);
-
-    /**
-     * Called when DPP events that indicate progress take place. Can be used by UI elements
-     * to show progress.
-     *
-     * @param code DPP progress status code.
-     */
-    public abstract void onProgress(@DppProgressStatusCode int code);
-}
diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
new file mode 100644
index 0000000..b8c82fd
--- /dev/null
+++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
@@ -0,0 +1,179 @@
+/*
+ * 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.net.wifi;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Handler;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Easy Connect (DPP) Status Callback. Use this callback to get status updates (success, failure,
+ * progress) from the Easy Connect operation started with
+ * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String,
+ * int, int, Handler, EasyConnectStatusCallback)} or
+ * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
+ * Handler, EasyConnectStatusCallback)}
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class EasyConnectStatusCallback {
+    /**
+     * Easy Connect Success event: Configuration sent (Configurator mode).
+     */
+    public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0;
+
+    /** @hide */
+    @IntDef(prefix = {"EASY_CONNECT_EVENT_SUCCESS_"}, value = {
+            EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EasyConnectSuccessStatusCode {
+    }
+
+    /**
+     * Easy Connect Progress event: Initial authentication with peer succeeded.
+     */
+    public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0;
+
+    /**
+     * Easy Connect Progress event: Peer requires more time to process bootstrapping.
+     */
+    public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1;
+
+    /** @hide */
+    @IntDef(prefix = {"EASY_CONNECT_EVENT_PROGRESS_"}, value = {
+            EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS,
+            EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EasyConnectProgressStatusCode {
+    }
+
+    /**
+     * Easy Connect Failure event: Scanned QR code is either not a Easy Connect URI, or the Easy
+     * Connect URI has errors.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1;
+
+    /**
+     * Easy Connect Failure event: Bootstrapping/Authentication initialization process failure.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2;
+
+    /**
+     * Easy Connect Failure event: Both devices are implementing the same role and are incompatible.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3;
+
+    /**
+     * Easy Connect Failure event: Configuration process has failed due to malformed message.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4;
+
+    /**
+     * Easy Connect Failure event: Easy Connect request while in another Easy Connect exchange.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5;
+
+    /**
+     * Easy Connect Failure event: No response from the peer.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6;
+
+    /**
+     * Easy Connect Failure event: General protocol failure.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7;
+
+    /**
+     * Easy Connect Failure event: Feature or option is not supported.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8;
+
+    /**
+     * Easy Connect Failure event: Invalid network provided to Easy Connect configurator.
+     * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK).
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9;
+
+
+    /** @hide */
+    @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = {
+            EASY_CONNECT_EVENT_FAILURE_INVALID_URI,
+            EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION,
+            EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE,
+            EASY_CONNECT_EVENT_FAILURE_CONFIGURATION,
+            EASY_CONNECT_EVENT_FAILURE_BUSY,
+            EASY_CONNECT_EVENT_FAILURE_TIMEOUT,
+            EASY_CONNECT_EVENT_FAILURE_GENERIC,
+            EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED,
+            EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EasyConnectFailureStatusCode {
+    }
+
+    /**
+     * Called when local Easy Connect Enrollee successfully receives a new Wi-Fi configuration from
+     * the
+     * peer Easy Connect configurator. This callback marks the successful end of the Easy Connect
+     * current Easy Connect
+     * session, and no further callbacks will be called. This callback is the successful outcome
+     * of a Easy Connect flow starting with
+     * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
+     * Handler,
+     * EasyConnectStatusCallback)}.
+     *
+     * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
+     */
+    public abstract void onEnrolleeSuccess(int newNetworkId);
+
+    /**
+     * Called when a Easy Connect success event takes place, except for when configuration is
+     * received from
+     * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
+     * This callback marks the successful end of the current Easy Connect session, and no further
+     * callbacks will be called. This callback is the successful outcome of a Easy Connect flow
+     * starting with
+     * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Handler,
+     * EasyConnectStatusCallback)}.
+     *
+     * @param code Easy Connect success status code.
+     */
+    public abstract void onConfiguratorSuccess(@EasyConnectSuccessStatusCode int code);
+
+    /**
+     * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful
+     * end of the
+     * current Easy Connect session, and no further callbacks will be called.
+     *
+     * @param code Easy Connect failure status code.
+     */
+    public abstract void onFailure(@EasyConnectFailureStatusCode int code);
+
+    /**
+     * Called when Easy Connect events that indicate progress take place. Can be used by UI elements
+     * to show progress.
+     *
+     * @param code Easy Connect progress status code.
+     */
+    public abstract void onProgress(@EasyConnectProgressStatusCode int code);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1700006..46c4191 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,7 +16,6 @@
 
 package android.net.wifi;
 
-
 import android.content.pm.ParceledListSlice;
 
 import android.net.wifi.hotspot2.OsuProvider;
@@ -47,7 +46,7 @@
  */
 interface IWifiManager
 {
-    int getSupportedFeatures();
+    long getSupportedFeatures();
 
     WifiActivityEnergyInfo reportActivityInfo();
 
@@ -61,11 +60,11 @@
 
     ParceledListSlice getConfiguredNetworks(String packageName);
 
-    ParceledListSlice getPrivilegedConfiguredNetworks();
+    ParceledListSlice getPrivilegedConfiguredNetworks(String packageName);
 
-    List<WifiConfiguration> getAllMatchingWifiConfigs(in List<ScanResult> scanResult);
+    Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult);
 
-    List<OsuProvider> getMatchingOsuProviders(in List<ScanResult> scanResult);
+    Map getMatchingOsuProviders(in List<ScanResult> scanResult);
 
     Map getMatchingPasspointConfigsForOsuProviders(in List<OsuProvider> osuProviders);
 
@@ -77,6 +76,8 @@
 
     List<PasspointConfiguration> getPasspointConfigurations();
 
+    List<WifiConfiguration> getWifiConfigsForPasspointProfiles(in List<String> fqdnList);
+
     void queryPasspointIcon(long bssid, String fileName);
 
     int matchProviderWithCurrentNetwork(String fqdn);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a809cad..e019f28 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -27,7 +27,6 @@
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
 import android.net.Uri;
-import android.net.wifi.WifiInfo;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -146,11 +145,23 @@
          */
         public static final int SUITE_B_192 = 10;
 
+        /**
+         * WPA pre-shared key with stronger SHA256-based algorithms.
+         * @hide
+         */
+        public static final int WPA_PSK_SHA256 = 11;
+
+        /**
+         * WPA using EAP authentication with stronger SHA256-based algorithms.
+         * @hide
+         */
+        public static final int WPA_EAP_SHA256 = 12;
+
         public static final String varName = "key_mgmt";
 
         public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP",
                 "IEEE8021X", "WPA2_PSK", "OSEN", "FT_PSK", "FT_EAP",
-                "SAE", "OWE", "SUITE_B_192"};
+                "SAE", "OWE", "SUITE_B_192", "WPA_PSK_SHA256", "WPA_EAP_SHA256" };
     }
 
     /**
@@ -491,7 +502,7 @@
      * The set of group management ciphers supported by this configuration.
      * See {@link GroupMgmtCipher} for descriptions of the values.
      */
-    public BitSet allowedGroupMgmtCiphers;
+    public BitSet allowedGroupManagementCiphers;
     /**
      * The set of SuiteB ciphers supported by this configuration.
      * To be used for WPA3-Enterprise mode.
@@ -1642,7 +1653,7 @@
         allowedAuthAlgorithms = new BitSet();
         allowedPairwiseCiphers = new BitSet();
         allowedGroupCiphers = new BitSet();
-        allowedGroupMgmtCiphers = new BitSet();
+        allowedGroupManagementCiphers = new BitSet();
         allowedSuiteBCiphers = new BitSet();
         wepKeys = new String[4];
         for (int i = 0; i < wepKeys.length; i++) {
@@ -1835,8 +1846,8 @@
         }
         sbuf.append('\n');
         sbuf.append(" GroupMgmtCiphers:");
-        for (int gmc = 0; gmc < this.allowedGroupMgmtCiphers.size(); gmc++) {
-            if (this.allowedGroupMgmtCiphers.get(gmc)) {
+        for (int gmc = 0; gmc < this.allowedGroupManagementCiphers.size(); gmc++) {
+            if (this.allowedGroupManagementCiphers.get(gmc)) {
                 sbuf.append(" ");
                 if (gmc < GroupMgmtCipher.strings.length) {
                     sbuf.append(GroupMgmtCipher.strings[gmc]);
@@ -1887,6 +1898,7 @@
         if (creatorName != null) sbuf.append(" cname=" + creatorName);
         if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid);
         if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName);
+        if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier);
         sbuf.append(" lcuid=" + lastConnectUid);
         sbuf.append(" userApproved=" + userApprovedAsString(userApproved));
         sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
@@ -2235,7 +2247,7 @@
             allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
             allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
             allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
-            allowedGroupMgmtCiphers    = (BitSet) source.allowedGroupMgmtCiphers.clone();
+            allowedGroupManagementCiphers = (BitSet) source.allowedGroupManagementCiphers.clone();
             allowedSuiteBCiphers    = (BitSet) source.allowedSuiteBCiphers.clone();
             enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
 
@@ -2282,6 +2294,7 @@
             mRandomizedMacAddress = source.mRandomizedMacAddress;
             macRandomizationSetting = source.macRandomizationSetting;
             requirePMF = source.requirePMF;
+            updateIdentifier = source.updateIdentifier;
         }
     }
 
@@ -2317,7 +2330,7 @@
         writeBitSet(dest, allowedAuthAlgorithms);
         writeBitSet(dest, allowedPairwiseCiphers);
         writeBitSet(dest, allowedGroupCiphers);
-        writeBitSet(dest, allowedGroupMgmtCiphers);
+        writeBitSet(dest, allowedGroupManagementCiphers);
         writeBitSet(dest, allowedSuiteBCiphers);
 
         dest.writeParcelable(enterpriseConfig, flags);
@@ -2389,7 +2402,7 @@
                 config.allowedAuthAlgorithms  = readBitSet(in);
                 config.allowedPairwiseCiphers = readBitSet(in);
                 config.allowedGroupCiphers    = readBitSet(in);
-                config.allowedGroupMgmtCiphers = readBitSet(in);
+                config.allowedGroupManagementCiphers = readBitSet(in);
                 config.allowedSuiteBCiphers   = readBitSet(in);
 
                 config.enterpriseConfig = in.readParcelable(null);
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 7cdd16a..35fba3d 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkUtils;
@@ -93,12 +94,22 @@
     private int mRssi;
 
     /**
-     * Link speed in Mbps
+     * The unit in which links speeds are expressed.
      */
     public static final String LINK_SPEED_UNITS = "Mbps";
     private int mLinkSpeed;
 
     /**
+     * Tx(transmit) Link speed in Mbps
+     */
+    private int mTxLinkSpeed;
+
+    /**
+     * Rx(receive) Link speed in Mbps
+     */
+    private int mRxLinkSpeed;
+
+    /**
      * Frequency in MHz
      */
     public static final String FREQUENCY_UNITS = "MHz";
@@ -114,6 +125,11 @@
     private boolean mTrusted;
 
     /**
+     * OSU (Online Sign Up) AP for Passpoint R2.
+     */
+    private boolean mOsuAp;
+
+    /**
      * Running total count of lost (not ACKed) transmitted unicast data packets.
      * @hide
      */
@@ -187,9 +203,12 @@
         setNetworkId(-1);
         setRssi(INVALID_RSSI);
         setLinkSpeed(-1);
+        setTxLinkSpeedMbps(-1);
+        setRxLinkSpeedMbps(-1);
         setFrequency(-1);
         setMeteredHint(false);
         setEphemeral(false);
+        setOsuAp(false);
         txBad = 0;
         txSuccess = 0;
         rxSuccess = 0;
@@ -213,12 +232,15 @@
             mNetworkId = source.mNetworkId;
             mRssi = source.mRssi;
             mLinkSpeed = source.mLinkSpeed;
+            mTxLinkSpeed = source.mTxLinkSpeed;
+            mRxLinkSpeed = source.mRxLinkSpeed;
             mFrequency = source.mFrequency;
             mIpAddress = source.mIpAddress;
             mMacAddress = source.mMacAddress;
             mMeteredHint = source.mMeteredHint;
             mEphemeral = source.mEphemeral;
             mTrusted = source.mTrusted;
+            mOsuAp = source.mOsuAp;
             txBad = source.txBad;
             txRetries = source.txRetries;
             txSuccess = source.txSuccess;
@@ -306,7 +328,7 @@
 
     /**
      * Returns the current link speed in {@link #LINK_SPEED_UNITS}.
-     * @return the link speed.
+     * @return the link speed or -1 if there is no valid value.
      * @see #LINK_SPEED_UNITS
      */
     public int getLinkSpeed() {
@@ -316,7 +338,39 @@
     /** @hide */
     @UnsupportedAppUsage
     public void setLinkSpeed(int linkSpeed) {
-        this.mLinkSpeed = linkSpeed;
+        mLinkSpeed = linkSpeed;
+    }
+
+    /**
+     * Returns the current transmit link speed in Mbps.
+     * @return the Tx link speed or -1 if there is no valid value.
+     */
+    public int getTxLinkSpeedMbps() {
+        return mTxLinkSpeed;
+    }
+
+    /**
+     * Update the last transmitted packet bit rate in Mbps.
+     * @hide
+     */
+    public void setTxLinkSpeedMbps(int txLinkSpeed) {
+        mTxLinkSpeed = txLinkSpeed;
+    }
+
+    /**
+     * Returns the current receive link speed in Mbps.
+     * @return the Rx link speed or -1 if there is no valid value.
+     */
+    public int getRxLinkSpeedMbps() {
+        return mRxLinkSpeed;
+    }
+
+    /**
+     * Update the last received packet bit rate in Mbps.
+     * @hide
+     */
+    public void setRxLinkSpeedMbps(int rxLinkSpeed) {
+        mRxLinkSpeed = rxLinkSpeed;
     }
 
     /**
@@ -411,6 +465,16 @@
         return mTrusted;
     }
 
+    /** {@hide} */
+    public void setOsuAp(boolean osuAp) {
+        mOsuAp = osuAp;
+    }
+
+    /** {@hide} */
+    @SystemApi
+    public boolean isOsuAp() {
+        return mOsuAp;
+    }
 
     /** @hide */
     @UnsupportedAppUsage
@@ -513,17 +577,19 @@
         StringBuffer sb = new StringBuffer();
         String none = "<none>";
 
-        sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid).
-            append(", BSSID: ").append(mBSSID == null ? none : mBSSID).
-            append(", MAC: ").append(mMacAddress == null ? none : mMacAddress).
-            append(", Supplicant state: ").
-            append(mSupplicantState == null ? none : mSupplicantState).
-            append(", RSSI: ").append(mRssi).
-            append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS).
-            append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS).
-            append(", Net ID: ").append(mNetworkId).
-            append(", Metered hint: ").append(mMeteredHint).
-            append(", score: ").append(Integer.toString(score));
+        sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid)
+                .append(", BSSID: ").append(mBSSID == null ? none : mBSSID)
+                .append(", MAC: ").append(mMacAddress == null ? none : mMacAddress)
+                .append(", Supplicant state: ")
+                .append(mSupplicantState == null ? none : mSupplicantState)
+                .append(", RSSI: ").append(mRssi)
+                .append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS)
+                .append(", Tx Link speed: ").append(mTxLinkSpeed).append(LINK_SPEED_UNITS)
+                .append(", Rx Link speed: ").append(mRxLinkSpeed).append(LINK_SPEED_UNITS)
+                .append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS)
+                .append(", Net ID: ").append(mNetworkId)
+                .append(", Metered hint: ").append(mMeteredHint)
+                .append(", score: ").append(Integer.toString(score));
         return sb.toString();
     }
 
@@ -537,6 +603,8 @@
         dest.writeInt(mNetworkId);
         dest.writeInt(mRssi);
         dest.writeInt(mLinkSpeed);
+        dest.writeInt(mTxLinkSpeed);
+        dest.writeInt(mRxLinkSpeed);
         dest.writeInt(mFrequency);
         if (mIpAddress != null) {
             dest.writeByte((byte)1);
@@ -565,6 +633,7 @@
         dest.writeLong(rxSuccess);
         dest.writeDouble(rxSuccessRate);
         mSupplicantState.writeToParcel(dest, flags);
+        dest.writeInt(mOsuAp ? 1 : 0);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -576,6 +645,8 @@
                 info.setNetworkId(in.readInt());
                 info.setRssi(in.readInt());
                 info.setLinkSpeed(in.readInt());
+                info.setTxLinkSpeedMbps(in.readInt());
+                info.setRxLinkSpeedMbps(in.readInt());
                 info.setFrequency(in.readInt());
                 if (in.readByte() == 1) {
                     try {
@@ -600,6 +671,7 @@
                 info.rxSuccess = in.readLong();
                 info.rxSuccessRate = in.readDouble();
                 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
+                info.mOsuAp = in.readInt() != 0;
                 return info;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e67e8ea..5742bd5 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -16,6 +16,10 @@
 
 package android.net.wifi;
 
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_WIFI_STATE;
+import static android.Manifest.permission.READ_WIFI_CREDENTIAL;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,6 +48,7 @@
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -184,6 +189,7 @@
      */
     public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5;
 
+    /** @hide */
     @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
             STATUS_NETWORK_SUGGESTIONS_SUCCESS,
             STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
@@ -229,6 +235,14 @@
     @SystemApi
     public static final int WIFI_CREDENTIAL_FORGOT = 1;
 
+    /** @hide */
+    @SystemApi
+    public static final int PASSPOINT_HOME_NETWORK = 0;
+
+    /** @hide */
+    @SystemApi
+    public static final int PASSPOINT_ROAMING_NETWORK = 1;
+
     /**
      * Broadcast intent action indicating that a Passpoint provider icon has been received.
      *
@@ -1135,6 +1149,10 @@
     /**
      * Return a list of all the networks configured for the current foreground
      * user.
+     *
+     * Requires the same permissions as {@link #getScanResults}.
+     * If such access is not allowed, this API will always return an empty list.
+     *
      * Not all fields of WifiConfiguration are returned. Only the following
      * fields are filled in:
      * <ul>
@@ -1158,9 +1176,12 @@
      * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
      * when auto-connecting to wifi.
      * <b>Compatibility Note:</b> For applications targeting
-     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return an empty list.
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will return an empty list,
+     * except to callers with Carrier privilege which will receive a restricted list only
+     * containing configurations which they created.
      */
     @Deprecated
+    @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE})
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
@@ -1176,11 +1197,11 @@
 
     /** @hide */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_WIFI_CREDENTIAL)
+    @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL})
     public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
-                mService.getPrivilegedConfiguredNetworks();
+                    mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1191,25 +1212,47 @@
     }
 
     /**
-     * Returns all matching WifiConfigurations for a given list of ScanResult.
+     * Returns a list of all matching WifiConfigurations for a given list of ScanResult.
      *
      * An empty list will be returned when no configurations are installed or if no configurations
      * match the ScanResult.
-
+     *
      * @param scanResults a list of scanResult that represents the BSSID
-     * @return A list of {@link WifiConfiguration} that can have duplicate entries.
+     * @return List that consists of {@link WifiConfiguration} and corresponding scanResults per
+     * network type({@link #PASSPOINT_HOME_NETWORK} and {@link #PASSPOINT_ROAMING_NETWORK}).
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
+    public List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> getAllMatchingWifiConfigs(
             @NonNull List<ScanResult> scanResults) {
+        List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>();
         try {
-            return mService.getAllMatchingWifiConfigs(scanResults);
+            Map<String, Map<Integer, List<ScanResult>>> results =
+                    mService.getAllMatchingFqdnsForScanResults(
+                            scanResults);
+            if (results.isEmpty()) {
+                return configs;
+            }
+            List<WifiConfiguration> wifiConfigurations =
+                    mService.getWifiConfigsForPasspointProfiles(
+                            new ArrayList<>(results.keySet()));
+            for (WifiConfiguration configuration : wifiConfigurations) {
+                Map<Integer, List<ScanResult>> scanResultsPerNetworkType = results.get(
+                        configuration.FQDN);
+                if (scanResultsPerNetworkType != null) {
+                    configs.add(Pair.create(configuration, scanResultsPerNetworkType));
+                }
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        return configs;
     }
 
     /**
@@ -1219,12 +1262,17 @@
      * An empty list will be returned if no match is found.
      *
      * @param scanResults a list of ScanResult
-     * @return A list of {@link OsuProvider} that does not contain duplicate entries.
+     * @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult}
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
+    public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+            List<ScanResult> scanResults) {
         try {
             return mService.getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
@@ -1245,7 +1293,11 @@
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
     public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
             @NonNull Set<OsuProvider> osuProviders) {
         try {
@@ -1617,7 +1669,7 @@
      * suggestion back using this API.</li>
      *
      * @param networkSuggestions List of network suggestions provided by the app.
-     * @return Status code corresponding to the values in {@link NetworkSuggestionsStatusCode}.
+     * @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values.
      * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
      * @throws {@link SecurityException} if the caller is missing required permissions.
      */
@@ -1638,8 +1690,7 @@
      *
      * @param networkSuggestions List of network suggestions to be removed. Pass an empty list
      *                           to remove all the previous suggestions provided by the app.
-     * @return Status code corresponding to the values in
-     * {@link NetworkSuggestionsStatusCode}.
+     * @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values.
      * Any matching suggestions are removed from the device and will not be considered for any
      * further connection attempts.
      */
@@ -1692,7 +1743,13 @@
      * @param fqdn The FQDN of the Passpoint configuration to be removed
      * @throws IllegalArgumentException if no configuration is associated with the given FQDN.
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
+     * @deprecated This is no longer supported.
      */
+    @Deprecated
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
     public void removePasspointConfiguration(String fqdn) {
         try {
             if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) {
@@ -1710,7 +1767,13 @@
      *
      * @return A list of {@link PasspointConfiguration}
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
+     * @deprecated This is no longer supported.
      */
+    @Deprecated
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
     public List<PasspointConfiguration> getPasspointConfigurations() {
         try {
             return mService.getPasspointConfigurations();
@@ -2020,7 +2083,7 @@
     /** @hide */
     public static final int WIFI_FEATURE_DPP              = 0x80000000; // DPP (Easy-Connect)
 
-    private int getSupportedFeatures() {
+    private long getSupportedFeatures() {
         try {
             return mService.getSupportedFeatures();
         } catch (RemoteException e) {
@@ -2028,7 +2091,7 @@
         }
     }
 
-    private boolean isFeatureSupported(int feature) {
+    private boolean isFeatureSupported(long feature) {
         return (getSupportedFeatures() & feature) == feature;
     }
     /**
@@ -4288,6 +4351,11 @@
      * @param callback {@link ProvisioningCallback} for updates regarding provisioning flow
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
     public void startSubscriptionProvisioning(OsuProvider provider, ProvisioningCallback callback,
             @Nullable Handler handler) {
         Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
@@ -4551,93 +4619,100 @@
         }
     }
 
-    /* DPP - Device Provisioning Protocol AKA "Easy Connect" */
+    /* Easy Connect - AKA Device Provisioning Protocol (DPP) */
 
     /**
-     * DPP Network role: Station.
+     * Easy Connect Network role: Station.
+     *
      * @hide
      */
     @SystemApi
-    public static final int DPP_NETWORK_ROLE_STA = 0;
+    public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0;
 
     /**
-     * DPP Network role: Access Point.
+     * Easy Connect Network role: Access Point.
+     *
      * @hide
      */
     @SystemApi
-    public static final int DPP_NETWORK_ROLE_AP = 1;
+    public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1;
 
     /** @hide */
-    @IntDef(prefix = {"DPP_NETWORK_ROLE_"}, value = {
-            DPP_NETWORK_ROLE_STA,
-            DPP_NETWORK_ROLE_AP,
+    @IntDef(prefix = {"EASY_CONNECT_NETWORK_ROLE_"}, value = {
+            EASY_CONNECT_NETWORK_ROLE_STA,
+            EASY_CONNECT_NETWORK_ROLE_AP,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface DppNetworkRole {}
+    public @interface EasyConnectNetworkRole {
+    }
 
     /**
-     * Start DPP in Configurator-Initiator role. The current device will initiate DPP bootstrapping
-     * with a peer, and configure the peer with the SSID and password of the specified network using
-     * the DPP protocol on an encrypted link.
+     * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate
+     * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of
+     * the specified network using the Easy Connect protocol on an encrypted link.
      *
-     * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning)
-     * @param selectedNetworkId Selected network ID to be sent to the peer
+     * @param enrolleeUri         URI of the Enrollee obtained separately (e.g. QR code scanning)
+     * @param selectedNetworkId   Selected network ID to be sent to the peer
      * @param enrolleeNetworkRole The network role of the enrollee
-     * @param callback Callback for status updates
-     * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+     * @param callback            Callback for status updates
+     * @param handler             The handler on whose thread to execute the callbacks. Null for
+     *                            main thread.
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_SETTINGS,
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
-    public void startDppAsConfiguratorInitiator(@NonNull String enrolleeUri,
-            int selectedNetworkId, @DppNetworkRole int enrolleeNetworkRole,
-            @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+    public void startEasyConnectAsConfiguratorInitiator(@NonNull String enrolleeUri,
+            int selectedNetworkId, @EasyConnectNetworkRole int enrolleeNetworkRole,
+            @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) {
         Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
             mService.startDppAsConfiguratorInitiator(binder, enrolleeUri, selectedNetworkId,
-                    enrolleeNetworkRole, new DppCallbackProxy(looper, callback));
+                    enrolleeNetworkRole, new EasyConnectCallbackProxy(looper, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Start DPP in Enrollee-Initiator role. The current device will initiate DPP bootstrapping
-     * with a peer, and receive the SSID and password from the peer configurator.
+     * Start Easy Connect (DPP) in Enrollee-Initiator role. The current device will initiate Easy
+     * Connect bootstrapping with a peer, and receive the SSID and password from the peer
+     * configurator.
      *
      * @param configuratorUri URI of the Configurator obtained separately (e.g. QR code scanning)
-     * @param callback Callback for status updates
-     * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+     * @param callback        Callback for status updates
+     * @param handler         The handler on whose thread to execute the callbacks. Null for main
+     *                        thread.
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_SETTINGS,
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
-    public void startDppAsEnrolleeInitiator(@NonNull String configuratorUri,
-            @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+    public void startEasyConnectAsEnrolleeInitiator(@NonNull String configuratorUri,
+            @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) {
         Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
             mService.startDppAsEnrolleeInitiator(binder, configuratorUri,
-                     new DppCallbackProxy(looper, callback));
+                    new EasyConnectCallbackProxy(looper, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Stop or abort a current DPP session.
+     * Stop or abort a current Easy Connect (DPP) session.
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_SETTINGS,
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
-    public void stopDppSession() {
+    public void stopEasyConnectSession() {
         try {
             /* Request lower layers to stop/abort and clear resources */
             mService.stopDppSession();
@@ -4647,48 +4722,50 @@
     }
 
     /**
-     * Helper class to support DPP callbacks
+     * Helper class to support Easy Connect (DPP) callbacks
+     *
      * @hide
      */
     @SystemApi
-    private static class DppCallbackProxy extends IDppCallback.Stub {
+    private static class EasyConnectCallbackProxy extends IDppCallback.Stub {
         private final Handler mHandler;
-        private final DppStatusCallback mDppStatusCallback;
+        private final EasyConnectStatusCallback mEasyConnectStatusCallback;
 
-        DppCallbackProxy(Looper looper, DppStatusCallback dppStatusCallback) {
+        EasyConnectCallbackProxy(Looper looper,
+                EasyConnectStatusCallback easyConnectStatusCallback) {
             mHandler = new Handler(looper);
-            mDppStatusCallback = dppStatusCallback;
+            mEasyConnectStatusCallback = easyConnectStatusCallback;
         }
 
         @Override
         public void onSuccessConfigReceived(int newNetworkId) {
-            Log.d(TAG, "DPP onSuccessConfigReceived callback");
+            Log.d(TAG, "Easy Connect onSuccessConfigReceived callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onEnrolleeSuccess(newNetworkId);
+                mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId);
             });
         }
 
         @Override
         public void onSuccess(int status) {
-            Log.d(TAG, "DPP onSuccess callback");
+            Log.d(TAG, "Easy Connect onSuccess callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onConfiguratorSuccess(status);
+                mEasyConnectStatusCallback.onConfiguratorSuccess(status);
             });
         }
 
         @Override
         public void onFailure(int status) {
-            Log.d(TAG, "DPP onFailure callback");
+            Log.d(TAG, "Easy Connect onFailure callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onFailure(status);
+                mEasyConnectStatusCallback.onFailure(status);
             });
         }
 
         @Override
         public void onProgress(int status) {
-            Log.d(TAG, "DPP onProgress callback");
+            Log.d(TAG, "Easy Connect onProgress callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onProgress(status);
+                mEasyConnectStatusCallback.onProgress(status);
             });
         }
     }
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index 955e040..aa1669e 100644
--- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -168,8 +168,9 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("WifiNetworkAgentSpecifier [");
-        sb.append("WifiConfiguration=").append(
-                mWifiConfiguration == null ? null : mWifiConfiguration.configKey())
+        sb.append("WifiConfiguration=")
+                .append(", SSID=").append(mWifiConfiguration.SSID)
+                .append(", BSSID=").append(mWifiConfiguration.BSSID)
                 .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid)
                 .append("]");
         return sb.toString();
@@ -180,4 +181,9 @@
         throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used "
                 + "for requests.");
     }
+
+    @Override
+    public NetworkSpecifier redact() {
+        return null;
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index f73b9e5..ecee5ff 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -412,7 +412,7 @@
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
             configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
             // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
-            configuration.allowedGroupMgmtCiphers.set(
+            configuration.allowedGroupManagementCiphers.set(
                     WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
             configuration.allowedSuiteBCiphers.set(
                     WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index 4348399..6e4eeef 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -162,11 +162,11 @@
     @Override
     public String toString() {
         return new StringBuilder()
-                .append("WifiNetworkSpecifierWifiNetworkSpecifier [")
+                .append("WifiNetworkSpecifier [")
                 .append(", SSID Match pattern=").append(ssidPatternMatcher)
                 .append(", BSSID Match pattern=").append(bssidPatternMatcher)
-                .append(", WifiConfiguration=").append(
-                wifiConfiguration == null ? null : wifiConfiguration.configKey())
+                .append(", SSID=").append(wifiConfiguration.SSID)
+                .append(", BSSID=").append(wifiConfiguration.BSSID)
                 .append(", requestorUid=").append(requestorUid)
                 .append("]")
                 .toString();
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 760f1e6..3c90eb7 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -130,7 +130,8 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [")
-                .append(", WifiConfiguration=").append(wifiConfiguration)
+                .append(", SSID=").append(wifiConfiguration.SSID)
+                .append(", BSSID=").append(wifiConfiguration.BSSID)
                 .append(", isAppInteractionRequired=").append(isAppInteractionRequired)
                 .append(", isUserInteractionRequired=").append(isUserInteractionRequired)
                 .append(", suggestorUid=").append(suggestorUid)
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 1426383..4bee837 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -149,6 +149,11 @@
                 "WifiAwareAgentNetworkSpecifier should not be used in network requests");
     }
 
+    @Override
+    public NetworkSpecifier redact() {
+        return null;
+    }
+
     private void initialize() {
         try {
             mDigester = MessageDigest.getInstance("SHA-256");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 26a6c08..1fa1fd5 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -35,6 +35,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.Log;
 
 import libcore.util.HexEncoding;
@@ -434,6 +435,8 @@
                 null, // peerMac (not used in this method)
                 pmk,
                 passphrase,
+                0, // no port info for deprecated IB APIs
+                -1, // no transport info for deprecated IB APIs
                 Process.myUid());
     }
 
@@ -473,6 +476,8 @@
                 peer,
                 pmk,
                 passphrase,
+                0, // no port info for OOB APIs
+                -1, // no transport protocol info for OOB APIs
                 Process.myUid());
     }
 
@@ -824,6 +829,8 @@
         private PeerHandle mPeerHandle;
         private String mPskPassphrase;
         private byte[] mPmk;
+        private int mPort = 0; // invalid value
+        private int mTransportProtocol = -1; // invalid value
 
         /**
          * Configure the {@link PublishDiscoverySession} or {@link SubscribeDiscoverySession}
@@ -902,6 +909,55 @@
         }
 
         /**
+         * Configure the port number which will be used to create a connection over this link. This
+         * configuration should only be done on the server device, e.g. the device creating the
+         * {@link java.net.ServerSocket}.
+         * <p>Notes:
+         * <ul>
+         *     <li>The server device must be the Publisher device!
+         *     <li>The port information can only be specified on secure links, specified using
+         *     {@link #setPskPassphrase(String)}.
+         * </ul>
+         *
+         * @param port A positive integer indicating the port to be used for communication.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         */
+        public @NonNull NetworkSpecifierBuilder setPort(int port) {
+            if (port <= 0 || port > 65535) {
+                throw new IllegalArgumentException("The port must be a positive value (0, 65535]");
+            }
+            mPort = port;
+            return this;
+        }
+
+        /**
+         * Configure the transport protocol which will be used to create a connection over this
+         * link. This configuration should only be done on the server device, e.g. the device
+         * creating the {@link java.net.ServerSocket} for TCP.
+         * <p>Notes:
+         * <ul>
+         *     <li>The server device must be the Publisher device!
+         *     <li>The transport protocol information can only be specified on secure links,
+         *     specified using {@link #setPskPassphrase(String)}.
+         * </ul>
+         * The transport protocol number is assigned by the Internet Assigned Numbers Authority
+         * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml.
+         *
+         * @param transportProtocol The transport protocol to be used for communication.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         */
+        public @NonNull NetworkSpecifierBuilder setTransportProtocol(int transportProtocol) {
+            if (transportProtocol < 0 || transportProtocol > 255) {
+                throw new IllegalArgumentException(
+                        "The transport protocol must be in range [0, 255]");
+            }
+            mTransportProtocol = transportProtocol;
+            return this;
+        }
+
+        /**
          * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)}
          * for a WiFi Aware connection (link) to the specified peer. The
          * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
@@ -929,6 +985,18 @@
                     ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                     : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
 
+            if (mPort != 0 || mTransportProtocol != -1) {
+                if (role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
+                    throw new IllegalStateException(
+                            "Port and transport protocol information can only "
+                                    + "be specified on the Publisher device (which is the server");
+                }
+                if (TextUtils.isEmpty(mPskPassphrase) && mPmk == null) {
+                    throw new IllegalStateException("Port and transport protocol information can "
+                            + "only be specified on a secure link");
+                }
+            }
+
             if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && mPeerHandle == null) {
                 throw new IllegalStateException("Null peerHandle!?");
             }
@@ -936,7 +1004,7 @@
             return new WifiAwareNetworkSpecifier(
                     WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role,
                     mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId,
-                    null, mPmk, mPskPassphrase, Process.myUid());
+                    null, mPmk, mPskPassphrase, mPort, mTransportProtocol, Process.myUid());
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
index 0f29e08..b258906 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
@@ -38,17 +38,30 @@
  * android.net.NetworkCapabilities)} callback.
  * <p>
  * The Wi-Fi Aware-specific network information include the peer's scoped link-local IPv6 address
- * for the Wi-Fi Aware link. The scoped link-local IPv6 can then be used to create a
+ * for the Wi-Fi Aware link, as well as (optionally) the port and transport protocol specified by
+ * the peer.
+ * The scoped link-local IPv6, port, and transport protocol can then be used to create a
  * {@link java.net.Socket} connection to the peer.
+ * <p>
+ * Note: these are the peer's IPv6 and port information - not the local device's!
  */
 public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable {
     private Inet6Address mIpv6Addr;
+    private int mPort = 0; // a value of 0 is considered invalid
+    private int mTransportProtocol = -1; // a value of -1 is considered invalid
 
     /** @hide */
     public WifiAwareNetworkInfo(Inet6Address ipv6Addr) {
         mIpv6Addr = ipv6Addr;
     }
 
+    /** @hide */
+    public WifiAwareNetworkInfo(Inet6Address ipv6Addr, int port, int transportProtocol) {
+        mIpv6Addr = ipv6Addr;
+        mPort = port;
+        mTransportProtocol = transportProtocol;
+    }
+
     /**
      * Get the scoped link-local IPv6 address of the Wi-Fi Aware peer (not of the local device!).
      *
@@ -59,6 +72,34 @@
         return mIpv6Addr;
     }
 
+    /**
+     * Get the port number to be used to create a network connection to the Wi-Fi Aware peer.
+     * The port information is provided by the app running on the peer which requested the
+     * connection, using the {@link WifiAwareManager.NetworkSpecifierBuilder#setPort(int)}.
+     *
+     * @return A port number on the peer. A value of 0 indicates that no port was specified by the
+     *         peer.
+     */
+    public int getPort() {
+        return mPort;
+    }
+
+    /**
+     * Get the transport protocol to be used to communicate over a network connection to the Wi-Fi
+     * Aware peer. The transport protocol is provided by the app running on the peer which requested
+     * the connection, using the
+     * {@link WifiAwareManager.NetworkSpecifierBuilder#setTransportProtocol(int)}.
+     * <p>
+     * The transport protocol number is assigned by the Internet Assigned Numbers Authority
+     * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml.
+     *
+     * @return A transport protocol id. A value of -1 indicates that no transport protocol was
+     *         specified by the peer.
+     */
+    public int getTransportProtocol() {
+        return mTransportProtocol;
+    }
+
     // parcelable methods
 
     @Override
@@ -71,6 +112,8 @@
         dest.writeByteArray(mIpv6Addr.getAddress());
         NetworkInterface ni = mIpv6Addr.getScopedInterface();
         dest.writeString(ni == null ? null : ni.getName());
+        dest.writeInt(mPort);
+        dest.writeInt(mTransportProtocol);
     }
 
     public static final Creator<WifiAwareNetworkInfo> CREATOR =
@@ -94,8 +137,10 @@
                         e.printStackTrace();
                         return null;
                     }
+                    int port = in.readInt();
+                    int transportProtocol = in.readInt();
 
-                    return new WifiAwareNetworkInfo(ipv6Addr);
+                    return new WifiAwareNetworkInfo(ipv6Addr, port, transportProtocol);
                 }
 
                 @Override
@@ -109,7 +154,9 @@
 
     @Override
     public String toString() {
-        return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).toString();
+        return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).append(
+                ", port=").append(mPort).append(", transportProtocol=").append(
+                mTransportProtocol).toString();
     }
 
     /** @hide */
@@ -124,12 +171,13 @@
         }
 
         WifiAwareNetworkInfo lhs = (WifiAwareNetworkInfo) obj;
-        return Objects.equals(mIpv6Addr, lhs.mIpv6Addr);
+        return Objects.equals(mIpv6Addr, lhs.mIpv6Addr) && mPort == lhs.mPort
+                && mTransportProtocol == lhs.mTransportProtocol;
     }
 
     /** @hide */
     @Override
     public int hashCode() {
-        return Objects.hash(mIpv6Addr);
+        return Objects.hash(mIpv6Addr, mPort, mTransportProtocol);
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index 6e37fcf..a93a6d5 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -19,7 +19,6 @@
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 import java.util.Arrays;
 import java.util.Objects;
@@ -117,6 +116,32 @@
     public final String passphrase;
 
     /**
+     * The port information to be used for this link. This information will be communicated to the
+     * peer as part of the layer 2 link setup.
+     *
+     * Information only allowed on secure links since a single layer-2 link is set up for all
+     * requestors. Therefore if multiple apps on a single device request links to the same peer
+     * device they all get the same link. However, the link is only set up on the first request -
+     * hence only the first can transmit the port information. But we don't want to expose that
+     * information to other apps. Limiting to secure links would (usually) imply single app usage.
+     *
+     * @hide
+     */
+    public final int port;
+
+    /**
+     * The transport protocol information to be used for this link. This information will be
+     * communicated to the peer as part of the layer 2 link setup.
+     *
+     * Information only allowed on secure links since a single layer-2 link is set up for all
+     * requestors. Therefore if multiple apps on a single device request links to the same peer
+     * device they all get the same link. However, the link is only set up on the first request -
+     * hence only the first can transmit the port information. But we don't want to expose that
+     * information to other apps. Limiting to secure links would (usually) imply single app usage.
+     */
+    public final int transportProtocol;
+
+    /**
      * The UID of the process initializing this network specifier. Validated by receiver using
      * checkUidIfNecessary() and is used by satisfiedBy() to determine whether matches the
      * offered network.
@@ -127,7 +152,8 @@
 
     /** @hide */
     public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId,
-            byte[] peerMac, byte[] pmk, String passphrase, int requestorUid) {
+            byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol,
+            int requestorUid) {
         this.type = type;
         this.role = role;
         this.clientId = clientId;
@@ -136,6 +162,8 @@
         this.peerMac = peerMac;
         this.pmk = pmk;
         this.passphrase = passphrase;
+        this.port = port;
+        this.transportProtocol = transportProtocol;
         this.requestorUid = requestorUid;
     }
 
@@ -152,6 +180,8 @@
                         in.createByteArray(), // peerMac
                         in.createByteArray(), // pmk
                         in.readString(), // passphrase
+                        in.readInt(), // port
+                        in.readInt(), // transportProtocol
                         in.readInt()); // requestorUid
                 }
 
@@ -186,6 +216,8 @@
         dest.writeByteArray(peerMac);
         dest.writeByteArray(pmk);
         dest.writeString(passphrase);
+        dest.writeInt(port);
+        dest.writeInt(transportProtocol);
         dest.writeInt(requestorUid);
     }
 
@@ -202,19 +234,8 @@
     /** @hide */
     @Override
     public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + type;
-        result = 31 * result + role;
-        result = 31 * result + clientId;
-        result = 31 * result + sessionId;
-        result = 31 * result + peerId;
-        result = 31 * result + Arrays.hashCode(peerMac);
-        result = 31 * result + Arrays.hashCode(pmk);
-        result = 31 * result + Objects.hashCode(passphrase);
-        result = 31 * result + requestorUid;
-
-        return result;
+        return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac),
+                Arrays.hashCode(pmk), passphrase, port, transportProtocol, requestorUid);
     }
 
     /** @hide */
@@ -238,6 +259,8 @@
                 && Arrays.equals(peerMac, lhs.peerMac)
                 && Arrays.equals(pmk, lhs.pmk)
                 && Objects.equals(passphrase, lhs.passphrase)
+                && port == lhs.port
+                && transportProtocol == lhs.transportProtocol
                 && requestorUid == lhs.requestorUid;
     }
 
@@ -256,7 +279,8 @@
                 .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>")
                 // masking PII
                 .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>")
-                .append(", requestorUid=").append(requestorUid)
+                .append(", port=").append(port).append(", transportProtocol=")
+                .append(transportProtocol).append(", requestorUid=").append(requestorUid)
                 .append("]");
         return sb.toString();
     }
diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 6d82ca1..f91790f 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2;
 
+import android.annotation.SystemApi;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiSsid;
@@ -36,16 +37,19 @@
  *
  * @hide
  */
+@SystemApi
 public final class OsuProvider implements Parcelable {
     /**
      * OSU (Online Sign-Up) method: OMA DM (Open Mobile Alliance Device Management).
      * For more info, refer to Section 8.3 of the Hotspot 2.0 Release 2 Technical Specification.
+     * @hide
      */
     public static final int METHOD_OMA_DM = 0;
 
     /**
      * OSU (Online Sign-Up) method: SOAP XML SPP (Subscription Provisioning Protocol).
      * For more info, refer to Section 8.4 of the Hotspot 2.0 Release 2 Technical Specification.
+     * @hide
      */
     public static final int METHOD_SOAP_XML_SPP = 1;
 
@@ -84,6 +88,7 @@
      */
     private final Icon mIcon;
 
+    /** @hide */
     public OsuProvider(WifiSsid osuSsid, Map<String, String> friendlyNames,
             String serviceDescription, Uri serverUri, String nai, List<Integer> methodList,
             Icon icon) {
@@ -104,6 +109,7 @@
      * Copy constructor.
      *
      * @param source The source to copy from
+     * @hide
      */
     public OsuProvider(OsuProvider source) {
         if (source == null) {
@@ -130,10 +136,12 @@
         mIcon = source.mIcon;
     }
 
+    /** @hide */
     public WifiSsid getOsuSsid() {
         return mOsuSsid;
     }
 
+    /** @hide */
     public void setOsuSsid(WifiSsid osuSsid) {
         mOsuSsid = osuSsid;
     }
@@ -162,10 +170,12 @@
         return mFriendlyNames.get(mFriendlyNames.keySet().stream().findFirst().get());
     }
 
+    /** @hide */
     public Map<String, String> getFriendlyNameList() {
         return mFriendlyNames;
     }
 
+    /** @hide */
     public String getServiceDescription() {
         return mServiceDescription;
     }
@@ -174,14 +184,17 @@
         return mServerUri;
     }
 
+    /** @hide */
     public String getNetworkAccessIdentifier() {
         return mNetworkAccessIdentifier;
     }
 
+    /** @hide */
     public List<Integer> getMethodList() {
         return mMethodList;
     }
 
+    /** @hide */
     public Icon getIcon() {
         return mIcon;
     }
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index a62d63c..1ee874a 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2;
 
+import android.annotation.SystemApi;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
 
@@ -25,6 +26,7 @@
  *
  * @hide
  */
+@SystemApi
 public abstract class ProvisioningCallback {
 
     /**
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 7689fc3..59a7290 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -572,7 +572,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mCertType, mCertSha256Fingerprint);
+            return Objects.hash(mCertType, Arrays.hashCode(mCertSha256Fingerprint));
         }
 
         @Override
@@ -842,24 +842,50 @@
     }
 
     /**
-     * CA (Certificate Authority) X509 certificate.
+     * CA (Certificate Authority) X509 certificates.
      */
-    private X509Certificate mCaCertificate = null;
+    private X509Certificate[] mCaCertificates = null;
+
     /**
      * Set the CA (Certification Authority) certificate associated with this credential.
      *
      * @param caCertificate The CA certificate to set to
      */
     public void setCaCertificate(X509Certificate caCertificate) {
-        mCaCertificate = caCertificate;
+        mCaCertificates = null;
+        if (caCertificate != null) {
+            mCaCertificates = new X509Certificate[] {caCertificate};
+        }
     }
+
+    /**
+     * Set the CA (Certification Authority) certificates associated with this credential.
+     *
+     * @param caCertificates The list of CA certificates to set to
+     * @hide
+     */
+    public void setCaCertificates(X509Certificate[] caCertificates) {
+        mCaCertificates = caCertificates;
+    }
+
     /**
      * Get the CA (Certification Authority) certificate associated with this credential.
      *
-     * @return CA certificate associated with this credential
+     * @return CA certificate associated with this credential, {@code null} if certificate is not
+     * set or certificate is more than one.
      */
     public X509Certificate getCaCertificate() {
-        return mCaCertificate;
+        return mCaCertificates == null || mCaCertificates.length > 1 ? null : mCaCertificates[0];
+    }
+
+    /**
+     * Get the CA (Certification Authority) certificates associated with this credential.
+     *
+     * @return The list of CA certificates associated with this credential
+     * @hide
+     */
+    public X509Certificate[] getCaCertificates() {
+        return mCaCertificates;
     }
 
     /**
@@ -933,7 +959,11 @@
                 mClientCertificateChain = Arrays.copyOf(source.mClientCertificateChain,
                                                         source.mClientCertificateChain.length);
             }
-            mCaCertificate = source.mCaCertificate;
+            if (source.mCaCertificates != null) {
+                mCaCertificates = Arrays.copyOf(source.mCaCertificates,
+                        source.mCaCertificates.length);
+            }
+
             mClientPrivateKey = source.mClientPrivateKey;
         }
     }
@@ -952,7 +982,7 @@
         dest.writeParcelable(mUserCredential, flags);
         dest.writeParcelable(mCertCredential, flags);
         dest.writeParcelable(mSimCredential, flags);
-        ParcelUtil.writeCertificate(dest, mCaCertificate);
+        ParcelUtil.writeCertificates(dest, mCaCertificates);
         ParcelUtil.writeCertificates(dest, mClientCertificateChain);
         ParcelUtil.writePrivateKey(dest, mClientPrivateKey);
     }
@@ -977,16 +1007,17 @@
                     : mCertCredential.equals(that.mCertCredential))
                 && (mSimCredential == null ? that.mSimCredential == null
                     : mSimCredential.equals(that.mSimCredential))
-                && isX509CertificateEquals(mCaCertificate, that.mCaCertificate)
+                && isX509CertificatesEquals(mCaCertificates, that.mCaCertificates)
                 && isX509CertificatesEquals(mClientCertificateChain, that.mClientCertificateChain)
                 && isPrivateKeyEquals(mClientPrivateKey, that.mClientPrivateKey);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mRealm, mCreationTimeInMillis, mExpirationTimeInMillis,
+        return Objects.hash(mCreationTimeInMillis, mExpirationTimeInMillis, mRealm,
                 mCheckAaaServerCertStatus, mUserCredential, mCertCredential, mSimCredential,
-                mCaCertificate, mClientCertificateChain, mClientPrivateKey);
+                mClientPrivateKey, Arrays.hashCode(mCaCertificates),
+                Arrays.hashCode(mClientCertificateChain));
     }
 
     @Override
@@ -1067,7 +1098,7 @@
                 credential.setUserCredential(in.readParcelable(null));
                 credential.setCertCredential(in.readParcelable(null));
                 credential.setSimCredential(in.readParcelable(null));
-                credential.setCaCertificate(ParcelUtil.readCertificate(in));
+                credential.setCaCertificates(ParcelUtil.readCertificates(in));
                 credential.setClientCertificateChain(ParcelUtil.readCertificates(in));
                 credential.setClientPrivateKey(ParcelUtil.readPrivateKey(in));
                 return credential;
@@ -1100,7 +1131,7 @@
 
         // CA certificate is required for R1 Passpoint profile.
         // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
-        if (isR1 && mCaCertificate == null) {
+        if (isR1 && mCaCertificates == null) {
             Log.d(TAG, "Missing CA Certificate for user credential");
             return false;
         }
@@ -1131,7 +1162,7 @@
         // Verify required key and certificates for certificate credential.
         // CA certificate is required for R1 Passpoint profile.
         // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
-        if (isR1 && mCaCertificate == null) {
+        if (isR1 && mCaCertificates == null) {
             Log.d(TAG, "Missing CA Certificate for certificate credential");
             return false;
         }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6631fa8..f931ad2 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -223,7 +223,8 @@
         private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
         private String mNetworkName = "";
         private String mPassphrase = "";
-        private int mGroupOwnerBand = GROUP_OWNER_BAND_AUTO;
+        private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO;
+        private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO;
         private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
 
         /**
@@ -285,22 +286,84 @@
         }
 
         /**
-         * Specify the band to use for creating the group. This method only applies when
-         * creating a group as Group Owner using {@link WifiP2pManager#createGroup}.
-         * The band should be {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ},
-         * or allow the system to pick the band by specifying {@link #GROUP_OWNER_BAND_AUTO}.
+         * Specify the band to use for creating the group or joining the group. The band should
+         * be {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ} or
+         * {@link #GROUP_OWNER_BAND_AUTO}.
+         * <p>
+         * When creating a group as Group Owner using {@link
+         * WifiP2pManager#createGroup(WifiP2pManager.Channel,
+         * WifiP2pConfig, WifiP2pManager.ActionListener)},
+         * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to pick the operating
+         * frequency from all supported bands.
+         * Specifying {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ}
+         * only allows the system to pick the operating frequency in the specified band.
          * If the Group Owner cannot create a group in the specified band, the operation will fail.
          * <p>
+         * When joining a group as Group Client using {@link
+         * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
+         * WifiP2pManager.ActionListener)},
+         * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to scan all supported
+         * frequencies to find the desired group. Specifying {@link #GROUP_OWNER_BAND_2GHZ} or
+         * {@link #GROUP_OWNER_BAND_5GHZ} only allows the system to scan the specified band.
+         * <p>
+         *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
+         *     mutually exclusive. Setting operating band and frequency both is invalid.
+         * <p>
          *     Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
          *
-         * @param band the required band of group owner.
+         * @param band the operating band of the group.
          *             This should be one of {@link #GROUP_OWNER_BAND_AUTO},
          *             {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setGroupOwnerBand(int band) {
-            mGroupOwnerBand = band;
+        public Builder setGroupOperatingBand(@GroupOwnerBandType int band) {
+            switch (band) {
+                case GROUP_OWNER_BAND_AUTO:
+                case GROUP_OWNER_BAND_2GHZ:
+                case GROUP_OWNER_BAND_5GHZ:
+                    mGroupOperatingBand = band;
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                        "Invalid constant for the group operating band!");
+            }
+            return this;
+        }
+
+        /**
+         * Specify the frequency to use for creating the group or joining the group.
+         * <p>
+         * When creating a group as Group Owner using {@link WifiP2pManager#createGroup(
+         * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
+         * specifying a frequency only allows the system to pick the specified frequency.
+         * If the Group Owner cannot create a group at the specified frequency,
+         * the operation will fail.
+         * When not specifying a frequency, it allows the system to pick operating frequency
+         * from all supported bands.
+         * <p>
+         * When joining a group as Group Client using {@link WifiP2pManager#connect(
+         * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
+         * specifying a frequency only allows the system to scan the specified frequency.
+         * If the frequency is not supported or invalid, the operation will fail.
+         * When not specifying a frequency, it allows the system to scan all supported
+         * frequencies to find the desired group.
+         * <p>
+         *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
+         *     mutually exclusive. Setting operating band and frequency both is invalid.
+         * <p>
+         *     Optional. 0 by default.
+         *
+         * @param frequency the operating frequency of the group.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setGroupOperatingFrequency(int frequency) {
+            if (frequency < 0) {
+                throw new IllegalArgumentException(
+                    "Invalid group operating frequency!");
+            }
+            mGroupOperatingFrequency = frequency;
             return this;
         }
 
@@ -337,11 +400,21 @@
                         "passphrase must be non-empty.");
             }
 
+            if (mGroupOperatingFrequency > 0 && mGroupOperatingBand > 0) {
+                throw new IllegalStateException(
+                        "Preferred frequency and band are mutually exclusive.");
+            }
+
             WifiP2pConfig config = new WifiP2pConfig();
             config.deviceAddress = mDeviceAddress.toString();
             config.networkName = mNetworkName;
             config.passphrase = mPassphrase;
-            config.groupOwnerBand = mGroupOwnerBand;
+            config.groupOwnerBand = GROUP_OWNER_BAND_AUTO;
+            if (mGroupOperatingFrequency > 0) {
+                config.groupOwnerBand = mGroupOperatingFrequency;
+            } else if (mGroupOperatingBand > 0) {
+                config.groupOwnerBand = mGroupOperatingBand;
+            }
             config.netId = mNetId;
             return config;
         }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 01feb1e..72e57a1 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -17,15 +17,15 @@
 package android.net.wifi.p2p;
 
 import android.annotation.UnsupportedAppUsage;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.regex.Pattern;
+import java.util.List;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * A class representing a Wi-Fi P2p group. A p2p group consists of a single group
@@ -67,6 +67,9 @@
     /** The network id in the wpa_supplicant */
     private int mNetId;
 
+    /** The frequency used by this group */
+    private int mFrequency;
+
     /** P2P group started string pattern */
     private static final Pattern groupStartedPattern = Pattern.compile(
         "ssid=\"(.+)\" " +
@@ -116,8 +119,9 @@
             }
 
             mNetworkName = match.group(1);
-            //freq and psk are unused right now
-            //int freq = Integer.parseInt(match.group(2));
+            // It throws NumberFormatException if the string cannot be parsed as an integer.
+            mFrequency = Integer.parseInt(match.group(2));
+            // psk is unused right now
             //String psk = match.group(3);
             mPassphrase = match.group(4);
             mOwner = new WifiP2pDevice(match.group(5));
@@ -269,6 +273,16 @@
         this.mNetId = netId;
     }
 
+    /** Get the operating frequency of the p2p group */
+    public int getFrequency() {
+        return mFrequency;
+    }
+
+    /** @hide */
+    public void setFrequency(int freq) {
+        this.mFrequency = freq;
+    }
+
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
         sbuf.append("network: ").append(mNetworkName);
@@ -279,6 +293,7 @@
         }
         sbuf.append("\n interface: ").append(mInterface);
         sbuf.append("\n networkId: ").append(mNetId);
+        sbuf.append("\n frequency: ").append(mFrequency);
         return sbuf.toString();
     }
 
@@ -297,6 +312,7 @@
             mPassphrase = source.getPassphrase();
             mInterface = source.getInterface();
             mNetId = source.getNetworkId();
+            mFrequency = source.getFrequency();
         }
     }
 
@@ -312,6 +328,7 @@
         dest.writeString(mPassphrase);
         dest.writeString(mInterface);
         dest.writeInt(mNetId);
+        dest.writeInt(mFrequency);
     }
 
     /** Implement the Parcelable interface */
@@ -329,6 +346,7 @@
                 group.setPassphrase(in.readString());
                 group.setInterface(in.readString());
                 group.setNetworkId(in.readInt());
+                group.setFrequency(in.readInt());
                 return group;
             }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 068b959..1bed914 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1281,7 +1281,16 @@
         c.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, c.putListener(listener));
     }
 
-    /** @hide */
+    /**
+     * Force p2p to enter or exit listen state
+     *
+     * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
+     * @param enable enables or disables listening
+     * @param listener for callbacks on success or failure. Can be null.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void listen(Channel c, boolean enable, ActionListener listener) {
         checkChannel(c);
         c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN,
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
similarity index 89%
rename from wifi/java/com/android/server/wifi/AbstractWifiService.java
rename to wifi/java/com/android/server/wifi/BaseWifiService.java
index e94b9e6..b3ac9f1 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -21,6 +21,7 @@
 import android.content.pm.ParceledListSlice;
 import android.net.DhcpInfo;
 import android.net.Network;
+import android.net.wifi.IDppCallback;
 import android.net.wifi.INetworkRequestMatchCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
@@ -35,6 +36,7 @@
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.IBinder;
 import android.os.Messenger;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
 
@@ -42,7 +44,7 @@
 import java.util.Map;
 
 /**
- * Abstract class implementing IWifiManager with stub methods throwing runtime exceptions.
+ * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions.
  *
  * This class is meant to be extended by real implementations of IWifiManager in order to facilitate
  * cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of
@@ -53,13 +55,16 @@
  * then given a short grace period to update themselves before the @Deprecated stub is removed for
  * good. If the API scheduled for removal has a replacement or an overload (signature change),
  * these should be introduced before the stub is removed to allow children to migrate.
+ *
+ * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as
+ * well otherwise compilation will fail.
  */
-public abstract class AbstractWifiService extends IWifiManager.Stub {
+public class BaseWifiService extends IWifiManager.Stub {
 
-    private static final String TAG = AbstractWifiService.class.getSimpleName();
+    private static final String TAG = BaseWifiService.class.getSimpleName();
 
     @Override
-    public int getSupportedFeatures() {
+    public long getSupportedFeatures() {
         throw new UnsupportedOperationException();
     }
 
@@ -79,51 +84,19 @@
     }
 
     @Override
-    public ParceledListSlice getPrivilegedConfiguredNetworks() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Returns a WifiConfiguration matching this ScanResult
-     * @param scanResult a single ScanResult Object
-     * @return
-     * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
-     */
-    @Deprecated
-    public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Returns all matching WifiConfigurations for this ScanResult.
-     * @param scanResult a single ScanResult Object
-     * @return
-     * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
-     */
-    @Deprecated
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
+    public ParceledListSlice getPrivilegedConfiguredNetworks(String packageName) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(List<ScanResult> scanResults) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Returns a list of Hotspot 2.0 OSU (Online Sign-Up) providers associated with the given AP.
-     *
-     * @param scanResult a single ScanResult Object
-     * @return
-     * @deprecated use {@link #getMatchingOsuProviders(List)} instead.
-     */
-    @Deprecated
-    public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
+    public Map<String, Map<Integer, List<ScanResult>>> getAllMatchingFqdnsForScanResults(
+            List<ScanResult> scanResults) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+    public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+            List<ScanResult> scanResults) {
         throw new UnsupportedOperationException();
     }
 
@@ -155,6 +128,11 @@
     }
 
     @Override
+    public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void queryPasspointIcon(long bssid, String fileName) {
         throw new UnsupportedOperationException();
     }
@@ -464,4 +442,26 @@
     public String[] getFactoryMacAddresses() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void setDeviceMobilityState(int state) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri,
+            int selectedNetworkId, int netRole, IDppCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri,
+            IDppCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void stopDppSession() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index c744f18..7bff68a 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -59,6 +59,7 @@
         WifiConfiguration config = new WifiConfiguration();
         config.setPasspointManagementObjectTree(cookie);
         config.trusted = false;
+        config.updateIdentifier = "1234";
         MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
         Parcel parcelW = Parcel.obtain();
         config.writeToParcel(parcelW, 0);
@@ -73,6 +74,7 @@
         // lacking a useful config.equals, check two fields near the end.
         assertEquals(cookie, reconfig.getMoTree());
         assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
+        assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
         assertFalse(reconfig.trusted);
 
         Parcel parcelWW = Parcel.obtain();
@@ -251,6 +253,18 @@
     }
 
     /**
+     * Verifies that updateIdentifier should be copied for copy constructor.
+     */
+    @Test
+    public void testUpdateIdentifierForCopyConstructor() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.updateIdentifier = "1234";
+        WifiConfiguration copyConfig = new WifiConfiguration(config);
+
+        assertEquals(config.updateIdentifier, copyConfig.updateIdentifier);
+    }
+
+    /**
      * Verifies that the serialization/de-serialization for softap config works.
      */
     @Test
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index fb0af5f..677bf37 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -47,6 +47,7 @@
         writeWifiInfo.txBad = TEST_TX_BAD;
         writeWifiInfo.rxSuccess = TEST_RX_SUCCESS;
         writeWifiInfo.setTrusted(true);
+        writeWifiInfo.setOsuAp(true);
 
         Parcel parcel = Parcel.obtain();
         writeWifiInfo.writeToParcel(parcel, 0);
@@ -60,5 +61,6 @@
         assertEquals(TEST_TX_BAD, readWifiInfo.txBad);
         assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess);
         assertTrue(readWifiInfo.isTrusted());
+        assertTrue(readWifiInfo.isOsuAp());
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index c43948b..4fbef5a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -77,7 +77,9 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Unit tests for {@link android.net.wifi.WifiManager}.
@@ -1274,14 +1276,20 @@
     }
 
     /**
-     * Check the call to getAllMatchingWifiConfigs calls getAllMatchingWifiConfigs of WifiService
-     * with the provided a list of ScanResult.
+     * Check the call to getAllMatchingWifiConfigs calls getAllMatchingFqdnsForScanResults and
+     * getWifiConfigsForPasspointProfiles of WifiService in order.
      */
     @Test
     public void testGetAllMatchingWifiConfigs() throws Exception {
+        Map<String, List<ScanResult>> fqdns = new HashMap<>();
+        fqdns.put("www.test.com", new ArrayList<>());
+        when(mWifiService.getAllMatchingFqdnsForScanResults(any(List.class))).thenReturn(fqdns);
+        InOrder inOrder = inOrder(mWifiService);
+
         mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>());
 
-        verify(mWifiService).getAllMatchingWifiConfigs(any(List.class));
+        inOrder.verify(mWifiService).getAllMatchingFqdnsForScanResults(any(List.class));
+        inOrder.verify(mWifiService).getWifiConfigsForPasspointProfiles(any(List.class));
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 83627ad..dd6e2c9 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -518,7 +518,7 @@
                 .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
         assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
                 .get(WifiConfiguration.GroupCipher.GCMP_256));
-        assertTrue(suggestion.wifiConfiguration.allowedGroupMgmtCiphers
+        assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers
                 .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
         assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
                 .get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA));
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 4189e40..c3b6285 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -62,6 +62,7 @@
                 WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR);
 
         assertEquals(dut, rereadDut);
+        assertEquals(dut.hashCode(), rereadDut.hashCode());
 
         // Ensure that individual network specifiers are satisfied by both the original & marshaled
         // |WifiAwareNetworkAgentSpecifier instances.
@@ -181,6 +182,6 @@
     WifiAwareNetworkSpecifier getDummyNetworkSpecifier(int clientId) {
         return new WifiAwareNetworkSpecifier(WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
                 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, clientId, 0, 0, new byte[6],
-                null, null, 0);
+                null, null, 10, 5, 0);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index ed38c76..6da6d4a 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -16,8 +16,11 @@
 
 package android.net.wifi.aware;
 
+import static android.net.wifi.aware.WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB;
+
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -913,9 +916,10 @@
         final int clientId = 4565;
         final int sessionId = 123;
         final PeerHandle peerHandle = new PeerHandle(123412);
-        final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
         final byte[] pmk = PMK_VALID;
         final String passphrase = PASSPHRASE_VALID;
+        final int port = 5;
+        final int transportProtocol = 10;
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final PublishConfig publishConfig = new PublishConfig.Builder().build();
 
@@ -959,56 +963,70 @@
                 .setPeerHandle(peerHandle).build();
 
         // validate format
-        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(ns.role));
         collector.checkThat("client_id", clientId, equalTo(ns.clientId));
         collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
 
-        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(nsb.role));
         collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
         collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
+        collector.checkThat("port", 0, equalTo(nsb.port));
+        collector.checkThat("transportProtocol", -1, equalTo(nsb.transportProtocol));
 
         // (4) request an encrypted (PMK) network specifier from the session
         ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk(
                 peerHandle, pmk);
-        nsb =
-                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
-                        .setDiscoverySession(
-                        publishSession.getValue()).setPeerHandle(peerHandle).setPmk(pmk).build();
+        nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                .setDiscoverySession(publishSession.getValue()).setPeerHandle(peerHandle)
+                .setPmk(pmk).setPort(port).setTransportProtocol(transportProtocol).build();
 
         // validate format
-        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(ns.role));
         collector.checkThat("client_id", clientId, equalTo(ns.clientId));
         collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
         collector.checkThat("pmk", pmk , equalTo(ns.pmk));
 
-        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(nsb.role));
         collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
         collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
         collector.checkThat("pmk", pmk , equalTo(nsb.pmk));
+        collector.checkThat("port", port, equalTo(nsb.port));
+        collector.checkThat("transportProtocol", transportProtocol, equalTo(nsb.transportProtocol));
 
         // (5) request an encrypted (Passphrase) network specifier from the session
-        ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase(
-                peerHandle, passphrase);
+        ns =
+                (WifiAwareNetworkSpecifier) publishSession.getValue()
+                        .createNetworkSpecifierPassphrase(
+                        peerHandle, passphrase);
         nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
                 .setDiscoverySession(publishSession.getValue()).setPeerHandle(peerHandle)
-                .setPskPassphrase(passphrase).build();
+                .setPskPassphrase(passphrase).setPort(port).setTransportProtocol(transportProtocol)
+                .build();
 
         // validate format
-        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(ns.role));
         collector.checkThat("client_id", clientId, equalTo(ns.clientId));
         collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
         collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase));
 
-        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(nsb.role));
         collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
         collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
         collector.checkThat("passphrase", passphrase, equalTo(nsb.passphrase));
+        collector.checkThat("port", port, equalTo(nsb.port));
+        collector.checkThat("transportProtocol", transportProtocol, equalTo(nsb.transportProtocol));
 
         verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);
@@ -1325,6 +1343,140 @@
         executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, false);
     }
 
+    /**
+     * Validate that get an exception when creating a network specifier with an invalid port number
+     * (<=0).
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierBuilderInvalidPortNumber() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final byte[] pmk = PMK_VALID;
+        final int port = 0;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setPmk(pmk).setPort(port).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with port information
+     * without also requesting a secure link.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidPortOnInsecure() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int port = 5;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setPort(port).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with port information on
+     * a responder.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidPortOnResponder() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int port = 5;
+
+        DiscoverySession subscribeSession = executeSessionStartup(false);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(subscribeSession).setPeerHandle(peerHandle)
+                        .setPort(port).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with an invalid transport
+     * protocol number (not in [0, 255]).
+     */
+    @Test
+    public void testNetworkSpecifierBuilderInvalidTransportProtocolNumber() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final byte[] pmk = PMK_VALID;
+        final int tpNegative = -1;
+        final int tpTooLarge = 256;
+        final int tpSmallest = 0;
+        final int tpLargest = 255;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        try {
+            WifiAwareNetworkSpecifier nsb =
+                    (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                            .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                            .setPmk(pmk).setTransportProtocol(tpNegative).build();
+            assertTrue("No exception on negative transport protocol!", false);
+        } catch (IllegalArgumentException e) {
+            // nop - exception is correct!
+        }
+        try {
+            WifiAwareNetworkSpecifier nsb =
+                    (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                            .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                            .setPmk(pmk).setTransportProtocol(tpTooLarge).build();
+            assertTrue("No exception on >255 transport protocol!", false);
+        } catch (IllegalArgumentException e) {
+            // nop - exception is correct!
+        }
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setPmk(pmk).setTransportProtocol(tpSmallest).build();
+        nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(
+                                publishSession).setPeerHandle(peerHandle).setPmk(
+                        pmk).setTransportProtocol(tpLargest).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with transport protocol
+     * information without also requesting a secure link.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidTransportProtocolOnInsecure() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int transportProtocol = 5;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setTransportProtocol(transportProtocol).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with transport protocol
+     * information on a responder.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidTransportProtocolOnResponder() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int transportProtocol = 5;
+
+        DiscoverySession subscribeSession = executeSessionStartup(false);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(subscribeSession).setPeerHandle(peerHandle)
+                        .setTransportProtocol(transportProtocol).build();
+    }
+
+    /*
+     * Utilities
+     */
+
     private void executeNetworkSpecifierDirect(byte[] someMac, boolean doPmk, byte[] pmk,
             String passphrase, boolean doInitiator) throws Exception {
         final int clientId = 134;
@@ -1356,7 +1508,83 @@
         }
     }
 
-    // WifiAwareNetworkInfo tests
+    private DiscoverySession executeSessionStartup(boolean isPublish) throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int port = 5;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(PublishDiscoverySession.class);
+        ArgumentCaptor<SubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(SubscribeDiscoverySession.class);
+
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+                eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        if (isPublish) {
+            // (2) publish successfully
+            session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+            inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig),
+                    sessionProxyCallback.capture());
+            sessionProxyCallback.getValue().onSessionStarted(sessionId);
+            mMockLooper.dispatchAll();
+            inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+            return publishSession.getValue();
+        } else {
+            // (2) subscribe successfully
+            session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+            inOrder.verify(mockAwareService).subscribe(any(), eq(clientId), eq(subscribeConfig),
+                    sessionProxyCallback.capture());
+            sessionProxyCallback.getValue().onSessionStarted(sessionId);
+            mMockLooper.dispatchAll();
+            inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+            return subscribeSession.getValue();
+        }
+    }
+
+    // WifiAwareNetworkSpecifier && WifiAwareNetworkInfo tests
+
+    @Test
+    public void testWifiAwareNetworkSpecifierParcel() {
+        WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(NETWORK_SPECIFIER_TYPE_IB,
+                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, 5, 568, 334,
+                HexEncoding.decode("000102030405".toCharArray(), false),
+                "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4, 10001);
+
+        Parcel parcelW = Parcel.obtain();
+        ns.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiAwareNetworkSpecifier rereadNs =
+                WifiAwareNetworkSpecifier.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(ns, rereadNs);
+        assertEquals(ns.hashCode(), rereadNs.hashCode());
+    }
 
     @Test
     public void testWifiAwareNetworkCapabilitiesParcel() throws UnknownHostException {
@@ -1364,9 +1592,11 @@
                 "11:22:33:44:55:66").getLinkLocalIpv6FromEui48Mac();
         // note: dummy scope = 5
         final Inet6Address inet6Scoped = Inet6Address.getByAddress(null, inet6.getAddress(), 5);
+        final int port = 5;
+        final int transportProtocol = 6;
 
         assertEquals(inet6Scoped.toString(), "/fe80::1322:33ff:fe44:5566%5");
-        WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped);
+        WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped, port, transportProtocol);
 
         Parcel parcelW = Parcel.obtain();
         cap.writeToParcel(parcelW, 0);
@@ -1380,6 +1610,7 @@
                 WifiAwareNetworkInfo.CREATOR.createFromParcel(parcelR);
 
         assertEquals(cap.getPeerIpv6Addr().toString(), "/fe80::1322:33ff:fe44:5566%5");
+        assertEquals(cap, rereadCap);
         assertEquals(cap.hashCode(), rereadCap.hashCode());
     }
 
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index a9d4b8f..1ecc3fe 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2.pps;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -44,17 +45,16 @@
      * @param userCred Instance of UserCredential
      * @param certCred Instance of CertificateCredential
      * @param simCred Instance of SimCredential
-     * @param caCert CA certificate
      * @param clientCertificateChain Chain of client certificates
      * @param clientPrivateKey Client private key
+     * @param caCerts CA certificates
      * @return {@link Credential}
      */
     private static Credential createCredential(Credential.UserCredential userCred,
-                                               Credential.CertificateCredential certCred,
-                                               Credential.SimCredential simCred,
-                                               X509Certificate caCert,
-                                               X509Certificate[] clientCertificateChain,
-                                               PrivateKey clientPrivateKey) {
+            Credential.CertificateCredential certCred,
+            Credential.SimCredential simCred,
+            X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey,
+            X509Certificate... caCerts) {
         Credential cred = new Credential();
         cred.setCreationTimeInMillis(123455L);
         cred.setExpirationTimeInMillis(2310093L);
@@ -63,7 +63,11 @@
         cred.setUserCredential(userCred);
         cred.setCertCredential(certCred);
         cred.setSimCredential(simCred);
-        cred.setCaCertificate(caCert);
+        if (caCerts != null && caCerts.length == 1) {
+            cred.setCaCertificate(caCerts[0]);
+        } else {
+            cred.setCaCertificates(caCerts);
+        }
         cred.setClientCertificateChain(clientCertificateChain);
         cred.setClientPrivateKey(clientPrivateKey);
         return cred;
@@ -80,8 +84,8 @@
         certCred.setCertType("x509v3");
         certCred.setCertSha256Fingerprint(
                 MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
-        return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
-                new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+        return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT},
+                FakeKeys.RSA_KEY1, FakeKeys.CA_CERT0, FakeKeys.CA_CERT1);
     }
 
     /**
@@ -93,7 +97,7 @@
         Credential.SimCredential simCred = new Credential.SimCredential();
         simCred.setImsi("1234*");
         simCred.setEapType(EAPConstants.EAP_SIM);
-        return createCredential(null, null, simCred, null, null, null);
+        return createCredential(null, null, simCred, null, null, (X509Certificate[]) null);
     }
 
     /**
@@ -110,7 +114,7 @@
         userCred.setSoftTokenApp("TestApp");
         userCred.setEapType(EAPConstants.EAP_TTLS);
         userCred.setNonEapInnerMethod("MS-CHAP");
-        return createCredential(userCred, null, null, FakeKeys.CA_CERT0, null, null);
+        return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0);
     }
 
     private static void verifyParcel(Credential writeCred) {
@@ -120,6 +124,7 @@
         parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
         Credential readCred = Credential.CREATOR.createFromParcel(parcel);
         assertTrue(readCred.equals(writeCred));
+        assertEquals(writeCred.hashCode(), readCred.hashCode());
     }
 
     /**